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" 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"]

View File

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

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 /// 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))

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> { pub fn name(&self) -> Option<&KdlIdentifier> {
self.name.as_ref() self.name.as_ref()
} }

View File

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

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")] #![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::*;

View File

@ -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),
} }

View File

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