mirror of https://github.com/kdl-org/kdl-rs.git
chore: clean up and docs
This commit is contained in:
parent
a1555949ed
commit
fc1d0fd24b
|
|
@ -9,3 +9,9 @@ workspace=false
|
||||||
install_crate="cargo-release"
|
install_crate="cargo-release"
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["release", "--workspace", "${@}"]
|
args = ["release", "--workspace", "${@}"]
|
||||||
|
|
||||||
|
[tasks.readme]
|
||||||
|
workspace=false
|
||||||
|
install_crate="cargo-readme"
|
||||||
|
command = "cargo"
|
||||||
|
args = ["readme", "-o", "README.md"]
|
||||||
|
|
|
||||||
60
README.md
60
README.md
|
|
@ -1,7 +1,9 @@
|
||||||
|
# `kdl`
|
||||||
|
|
||||||
`kdl` is a "document-oriented" parser and API. That means that, unlike
|
`kdl` is a "document-oriented" parser and API. That means that, unlike
|
||||||
serde-based implementations, it's meant to preserve formatting when editing,
|
serde-based implementations, it's meant to preserve formatting when
|
||||||
as well as inserting values with custom formatting. This is useful when
|
editing, as well as inserting values with custom formatting. This is
|
||||||
working with human-maintained KDL files.
|
useful when working with human-maintained KDL files.
|
||||||
|
|
||||||
You can think of this crate as
|
You can think of this crate as
|
||||||
[`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
|
[`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
|
||||||
|
|
@ -11,18 +13,58 @@ You can think of this crate as
|
||||||
```rust
|
```rust
|
||||||
use kdl::KdlDocument;
|
use kdl::KdlDocument;
|
||||||
|
|
||||||
let doc: KdlDocument = r#"
|
let doc_str = r#"
|
||||||
hello 1 2 3
|
hello 1 2 3
|
||||||
|
|
||||||
world prop="value" {
|
world prop="value" {
|
||||||
child 1
|
child 1
|
||||||
child 2
|
child 2
|
||||||
}
|
}
|
||||||
"#.parse().expect("failed to parse KDL");
|
"#;
|
||||||
|
|
||||||
assert_eq!(doc.get_args("hello"), vec![&1.into(), &2.into(), &3.into()]);
|
let doc: KdlDocument = doc_str.parse().expect("failed to parse KDL");
|
||||||
assert_eq!(doc.get("world").map(|node| &node["prop"]), Some(&"value".into()));
|
|
||||||
|
assert_eq!(
|
||||||
|
doc.get_args("hello"),
|
||||||
|
vec![&1.into(), &2.into(), &3.into()]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
doc.get("world").map(|node| &node["prop"]),
|
||||||
|
Some(&"value".into())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Documents fully roundtrip:
|
||||||
|
assert_eq!(doc.to_string(), doc_str);
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
### Controlling Formatting
|
||||||
|
|
||||||
The code in this repository is covered by [the Apache-2.0 License](LICENSE.md).
|
By default, everything is created with default formatting. You can parse
|
||||||
|
items manually to provide custom representations, comments, etc:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let node_str = r#"
|
||||||
|
// indented comment
|
||||||
|
"formatted" 1 /* comment */ \
|
||||||
|
2;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut doc = kdl::KdlDocument::new();
|
||||||
|
doc.nodes_mut().push(node_str.parse().unwrap());
|
||||||
|
|
||||||
|
assert_eq!(&doc.to_string(), node_str);
|
||||||
|
```
|
||||||
|
|
||||||
|
[`KdlDocument`], [`KdlNode`], [`KdlEntry`], and [`KdlIdentifier`] can all
|
||||||
|
be parsed and managed this way.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
The code in this repository is covered by [the Apache-2.0
|
||||||
|
License](LICENSE.md).
|
||||||
|
|
||||||
|
[`KdlDocument`]: https://docs.rs/kdl/3.0.1-alpha.0/kdl/struct.KdlDocument.html
|
||||||
|
[`KdlNode`]: https://docs.rs/kdl/3.0.1-alpha.0/kdl/struct.KdlNode.html
|
||||||
|
[`KdlEntry`]: https://docs.rs/kdl/3.0.1-alpha.0/kdl/struct.KdlEntry.html
|
||||||
|
[`KdlIdentifier`]: https://docs.rs/kdl/3.0.1-alpha.0/kdl/struct.KdlIdentifier.html
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# `{{crate}}`
|
||||||
|
|
||||||
|
{{readme}}
|
||||||
|
|
||||||
|
[`KdlDocument`]: https://docs.rs/kdl/{{version}}/kdl/struct.KdlDocument.html
|
||||||
|
[`KdlNode`]: https://docs.rs/kdl/{{version}}/kdl/struct.KdlNode.html
|
||||||
|
[`KdlEntry`]: https://docs.rs/kdl/{{version}}/kdl/struct.KdlEntry.html
|
||||||
|
[`KdlIdentifier`]: https://docs.rs/kdl/{{version}}/kdl/struct.KdlIdentifier.html
|
||||||
|
|
@ -8,6 +8,14 @@ use crate::{parser, KdlError, KdlNode, KdlValue};
|
||||||
/// This type is also used to manage a [`KdlNode`]'s [`Children
|
/// This type is also used to manage a [`KdlNode`]'s [`Children
|
||||||
/// Block`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#children-block),
|
/// Block`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#children-block),
|
||||||
/// when present.
|
/// when present.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The easiest way to create a `KdlDocument` is to parse it:
|
||||||
|
/// ```rust
|
||||||
|
/// # use kdl::KdlDocument;
|
||||||
|
/// let kdl: KdlDocument = "foo 1 2 3\nbar 4 5 6".parse().expect("parse failed");
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
pub struct KdlDocument {
|
pub struct KdlDocument {
|
||||||
pub(crate) leading: Option<String>,
|
pub(crate) leading: Option<String>,
|
||||||
|
|
@ -88,6 +96,9 @@ impl KdlDocument {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable reference to the first argument (value) of the first
|
||||||
|
/// child node with a matching name. This is a shorthand utility for cases
|
||||||
|
/// where a document is being used as a key/value store.
|
||||||
pub fn get_arg_mut(&mut self, name: &str) -> Option<&mut KdlValue> {
|
pub fn get_arg_mut(&mut self, name: &str) -> Option<&mut KdlValue> {
|
||||||
self.get_mut(name)
|
self.get_mut(name)
|
||||||
.and_then(|node| node.get_mut(0))
|
.and_then(|node| node.get_mut(0))
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ impl KdlEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to this entry's name, if it's a property entry.
|
||||||
pub fn name(&self) -> Option<&KdlIdentifier> {
|
pub fn name(&self) -> Option<&KdlIdentifier> {
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,14 @@ use {
|
||||||
#[error("{kind}")]
|
#[error("{kind}")]
|
||||||
pub struct KdlError {
|
pub struct KdlError {
|
||||||
#[source_code]
|
#[source_code]
|
||||||
|
/// Source string for the KDL document that failed to parse.
|
||||||
pub input: String,
|
pub input: String,
|
||||||
|
|
||||||
/// Offset in chars of the error.
|
/// Offset in chars of the error.
|
||||||
#[label = "here"]
|
#[label = "here"]
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
|
|
||||||
|
/// Specific error kind for this parser error.
|
||||||
pub kind: KdlErrorKind,
|
pub kind: KdlErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,18 +31,24 @@ pub struct KdlError {
|
||||||
pub enum KdlErrorKind {
|
pub enum KdlErrorKind {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
#[diagnostic(code(kdl::parse_int))]
|
#[diagnostic(code(kdl::parse_int))]
|
||||||
|
/// An error occurred while parsing an integer.
|
||||||
ParseIntError(ParseIntError),
|
ParseIntError(ParseIntError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
#[diagnostic(code(kdl::parse_float))]
|
#[diagnostic(code(kdl::parse_float))]
|
||||||
|
/// An error occurred while parsing a floating point number.
|
||||||
ParseFloatError(ParseFloatError),
|
ParseFloatError(ParseFloatError),
|
||||||
|
|
||||||
#[error("Expected {0}.")]
|
#[error("Expected {0}.")]
|
||||||
#[diagnostic(code(kdl::parse_component))]
|
#[diagnostic(code(kdl::parse_component))]
|
||||||
|
/// Generic parsing error. The given context string denotes the component
|
||||||
|
/// that failed to parse.
|
||||||
Context(&'static str),
|
Context(&'static str),
|
||||||
|
|
||||||
#[error("An unspecified error occurred.")]
|
#[error("An unspecified error occurred.")]
|
||||||
#[diagnostic(code(kdl::other))]
|
#[diagnostic(code(kdl::other))]
|
||||||
|
/// Generic unspecified error. If this is returned, the call site should
|
||||||
|
/// be annotated with context, if possible.
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
96
src/lib.rs
96
src/lib.rs
|
|
@ -1,32 +1,72 @@
|
||||||
|
//! `kdl` is a "document-oriented" parser and API. That means that, unlike
|
||||||
|
//! serde-based implementations, it's meant to preserve formatting when
|
||||||
|
//! editing, as well as inserting values with custom formatting. This is
|
||||||
|
//! useful when working with human-maintained KDL files.
|
||||||
|
//!
|
||||||
|
//! You can think of this crate as
|
||||||
|
//! [`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
|
||||||
|
//!
|
||||||
|
//! ## Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use kdl::KdlDocument;
|
||||||
|
//!
|
||||||
|
//! let doc_str = r#"
|
||||||
|
//! hello 1 2 3
|
||||||
|
//!
|
||||||
|
//! world prop="value" {
|
||||||
|
//! child 1
|
||||||
|
//! child 2
|
||||||
|
//! }
|
||||||
|
//! "#;
|
||||||
|
//!
|
||||||
|
//! let doc: KdlDocument = doc_str.parse().expect("failed to parse KDL");
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! doc.get_args("hello"),
|
||||||
|
//! vec![&1.into(), &2.into(), &3.into()]
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! doc.get("world").map(|node| &node["prop"]),
|
||||||
|
//! Some(&"value".into())
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! // Documents fully roundtrip:
|
||||||
|
//! assert_eq!(doc.to_string(), doc_str);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Controlling Formatting
|
||||||
|
//!
|
||||||
|
//! By default, everything is created with default formatting. You can parse
|
||||||
|
//! items manually to provide custom representations, comments, etc:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! let node_str = r#"
|
||||||
|
//! // indented comment
|
||||||
|
//! "formatted" 1 /* comment */ \
|
||||||
|
//! 2;
|
||||||
|
//! "#;
|
||||||
|
//!
|
||||||
|
//! let mut doc = kdl::KdlDocument::new();
|
||||||
|
//! doc.nodes_mut().push(node_str.parse().unwrap());
|
||||||
|
//!
|
||||||
|
//! assert_eq!(&doc.to_string(), node_str);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`KdlDocument`], [`KdlNode`], [`KdlEntry`], and [`KdlIdentifier`] can all
|
||||||
|
//! be parsed and managed this way.
|
||||||
|
//!
|
||||||
|
//! ## License
|
||||||
|
//!
|
||||||
|
//! The code in this repository is covered by [the Apache-2.0
|
||||||
|
//! License](LICENSE.md).
|
||||||
|
|
||||||
|
#![deny(missing_debug_implementations, nonstandard_style)]
|
||||||
|
#![warn(missing_docs, unreachable_pub, rust_2018_idioms, unreachable_pub)]
|
||||||
|
#![cfg_attr(test, deny(warnings))]
|
||||||
#![doc(html_logo_url = "https://kdl.dev/logo.svg")]
|
#![doc(html_logo_url = "https://kdl.dev/logo.svg")]
|
||||||
/// `kdl` is a "document-oriented" parser and API. That means that, unlike
|
|
||||||
/// serde-based implementations, it's meant to preserve formatting when editing,
|
|
||||||
/// as well as inserting values with custom formatting. This is useful when
|
|
||||||
/// working with human-maintained KDL files.
|
|
||||||
///
|
|
||||||
/// You can think of this crate as
|
|
||||||
/// [`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use kdl::KdlDocument;
|
|
||||||
///
|
|
||||||
/// let doc: KdlDocument = r#"
|
|
||||||
/// hello 1 2 3
|
|
||||||
/// world prop="value" {
|
|
||||||
/// child 1
|
|
||||||
/// child 2
|
|
||||||
/// }
|
|
||||||
/// "#.parse().expect("failed to parse KDL");
|
|
||||||
///
|
|
||||||
/// assert_eq!(doc.get_args("hello"), vec![&1.into(), &2.into(), &3.into()]);
|
|
||||||
/// assert_eq!(doc.get("world").map(|node| &node["prop"]), Some(&"value".into()));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## License
|
|
||||||
///
|
|
||||||
/// The code in this repository is covered by [the Apache-2.0 License](LICENSE.md).
|
|
||||||
pub use document::*;
|
pub use document::*;
|
||||||
pub use entry::*;
|
pub use entry::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,17 @@ impl KdlNode {
|
||||||
self.name = name.into();
|
self.name = name.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the node's type, if any.
|
/// Gets the node's type identifier, if any.
|
||||||
pub fn ty(&self) -> Option<&KdlIdentifier> {
|
pub fn ty(&self) -> Option<&KdlIdentifier> {
|
||||||
self.ty.as_ref()
|
self.ty.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the node's type.
|
/// Gets a mutable reference to the node's type identifier.
|
||||||
pub fn ty_mut(&mut self) -> &mut Option<KdlIdentifier> {
|
pub fn ty_mut(&mut self) -> &mut Option<KdlIdentifier> {
|
||||||
&mut self.ty
|
&mut self.ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the node's type identifier.
|
||||||
pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
|
pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
|
||||||
self.ty = Some(ty.into());
|
self.ty = Some(ty.into());
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +335,9 @@ impl KdlNode {
|
||||||
/// Represents a [`KdlNode`]'s entry key.
|
/// Represents a [`KdlNode`]'s entry key.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum NodeKey {
|
pub enum NodeKey {
|
||||||
|
/// Key for a node property entry.
|
||||||
Key(KdlIdentifier),
|
Key(KdlIdentifier),
|
||||||
|
/// Index for a node argument entry (positional value).
|
||||||
Index(usize),
|
Index(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use nom::error::{ErrorKind, ParseError};
|
use nom::error::{ErrorKind, ParseError};
|
||||||
use nom::{Err, IResult, Parser};
|
use nom::{Err, IResult, Parser};
|
||||||
|
|
||||||
pub fn many0<I, O, E, F>(mut f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
pub(crate) fn many0<I, O, E, F>(mut f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
||||||
where
|
where
|
||||||
I: Clone + PartialEq,
|
I: Clone + PartialEq,
|
||||||
F: Parser<I, O, E>,
|
F: Parser<I, O, E>,
|
||||||
|
|
@ -26,7 +26,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn many1<I, O, E, F>(mut f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
pub(crate) fn many1<I, O, E, F>(mut f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
||||||
where
|
where
|
||||||
I: Clone + PartialEq,
|
I: Clone + PartialEq,
|
||||||
F: Parser<I, O, E>,
|
F: Parser<I, O, E>,
|
||||||
|
|
@ -58,7 +58,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn many_till<I, O, P, E, F, G>(
|
pub(crate) fn many_till<I, O, P, E, F, G>(
|
||||||
mut f: F,
|
mut f: F,
|
||||||
mut g: G,
|
mut g: G,
|
||||||
) -> impl FnMut(I) -> IResult<I, (Vec<O>, P), E>
|
) -> impl FnMut(I) -> IResult<I, (Vec<O>, P), E>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue