chore: clean up and docs

This commit is contained in:
Kat Marchán 2022-04-22 22:10:33 -07:00
parent a1555949ed
commit fc1d0fd24b
9 changed files with 161 additions and 42 deletions

View File

@ -9,3 +9,9 @@ workspace=false
install_crate="cargo-release"
command = "cargo"
args = ["release", "--workspace", "${@}"]
[tasks.readme]
workspace=false
install_crate="cargo-readme"
command = "cargo"
args = ["readme", "-o", "README.md"]

View File

@ -1,7 +1,9 @@
# `kdl`
`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.
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.
@ -11,18 +13,58 @@ You can think of this crate as
```rust
use kdl::KdlDocument;
let doc: KdlDocument = r#"
let doc_str = 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()));
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);
```
## 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

8
README.tpl Normal file
View File

@ -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

View File

@ -8,6 +8,14 @@ use crate::{parser, KdlError, KdlNode, KdlValue};
/// This type is also used to manage a [`KdlNode`]'s [`Children
/// Block`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#children-block),
/// 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)]
pub struct KdlDocument {
pub(crate) leading: Option<String>,
@ -88,6 +96,9 @@ impl KdlDocument {
.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> {
self.get_mut(name)
.and_then(|node| node.get_mut(0))

View File

@ -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> {
self.name.as_ref()
}

View File

@ -15,12 +15,14 @@ use {
#[error("{kind}")]
pub struct KdlError {
#[source_code]
/// Source string for the KDL document that failed to parse.
pub input: String,
/// Offset in chars of the error.
#[label = "here"]
pub offset: usize,
/// Specific error kind for this parser error.
pub kind: KdlErrorKind,
}
@ -29,18 +31,24 @@ pub struct KdlError {
pub enum KdlErrorKind {
#[error(transparent)]
#[diagnostic(code(kdl::parse_int))]
/// An error occurred while parsing an integer.
ParseIntError(ParseIntError),
#[error(transparent)]
#[diagnostic(code(kdl::parse_float))]
/// An error occurred while parsing a floating point number.
ParseFloatError(ParseFloatError),
#[error("Expected {0}.")]
#[diagnostic(code(kdl::parse_component))]
/// Generic parsing error. The given context string denotes the component
/// that failed to parse.
Context(&'static str),
#[error("An unspecified error occurred.")]
#[diagnostic(code(kdl::other))]
/// Generic unspecified error. If this is returned, the call site should
/// be annotated with context, if possible.
Other,
}

View File

@ -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")]
/// `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 entry::*;
pub use error::*;

View File

@ -50,16 +50,17 @@ impl KdlNode {
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> {
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> {
&mut self.ty
}
/// Sets the node's type identifier.
pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
self.ty = Some(ty.into());
}
@ -334,7 +335,9 @@ impl KdlNode {
/// Represents a [`KdlNode`]'s entry key.
#[derive(Debug, Clone, PartialEq)]
pub enum NodeKey {
/// Key for a node property entry.
Key(KdlIdentifier),
/// Index for a node argument entry (positional value).
Index(usize),
}

View File

@ -1,7 +1,7 @@
use nom::error::{ErrorKind, ParseError};
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
I: Clone + PartialEq,
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
I: Clone + PartialEq,
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 g: G,
) -> impl FnMut(I) -> IResult<I, (Vec<O>, P), E>