# `kdl` `kdl` is a "document-oriented" parser and API for the [KDL Document Language](https://kdl.dev), a node-based, human-friendly configuration and serialization format. Unlike serde-based implementations, this crate preserves formatting when editing, as well as when inserting or changing values with custom formatting. This is most 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. This crate supports both KDL v2.0.0 and v1.0.0 (when using the non-default `v1` feature). It also supports converting documents between either format. There is also a `v1-fallback` feature that may be enabled in order to have the various `Kdl*::parse` methods try to parse their input as v2, and, if that fails, try again as v1. In either case, a dedicated `Kdl*::parse_v1` method is available for v1-exclusive parsing, as long as either `v1` or `v1-fallback` are enabled. ### Example ```rust use kdl::{KdlDocument, KdlValue}; let doc_str = r#" hello 1 2 3 // Comment world prop=string-value { child 1 child 2 child #inf } "#; let doc: KdlDocument = doc_str.parse().expect("failed to parse KDL"); assert_eq!( doc.iter_args("hello").collect::>(), vec![&1.into(), &2.into(), &3.into()] ); assert_eq!( doc.get("world").map(|node| &node["prop"]), Some(&"string-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. ### Error Reporting [`KdlError`] implements [`miette::Diagnostic`] and can be used to display detailed, pretty-printed diagnostic messages when using [`miette::Result`] and the `"fancy"` feature flag for `miette`: ```toml # Cargo.toml [dependencies] miette = { version = "x.y.z", features = ["fancy"] } ``` ```rust fn main() -> miette::Result<()> { "foo 1.".parse::()?; Ok(()) } ``` This will display a message like: ``` Error: × Expected valid value. ╭──── 1 │ foo 1. · ─┬ · ╰── invalid float ╰──── help: Floating point numbers must be base 10, and have numbers after the decimal point. ``` ### Features * `span` (default) - Includes spans in the various document-related structs. * `v1` - Adds support for v1 parsing. This will pull in the entire previous version of `kdl-rs`, and so may be fairly heavy. * `v1-fallback` - Implies `v1`. Makes it so the various `*::parse()` and `FromStr` implementations try to parse their inputs as `v2`, and, if that fails, try again with `v1`. For `KdlDocument`, a heuristic will be applied if both `v1` and `v2` parsers fail, to pick which error(s) to return. For other types, only the `v2` parser's errors will be returned. ### Quirks #### Properties Multiple properties with the same name are allowed, and all duplicated **will be preserved**, meaning those documents will correctly round-trip. When using `node.get()`/`node["key"]` & company, the _last_ property with that name's value will be returned (as per spec). #### Numbers KDL itself does not specify a particular representation for numbers and accepts just about anything valid, no matter how large and how small. This means a few things: * Numbers without a decimal point are interpreted as [`i128`]. * Numbers with a decimal point are interpreted as [`f64`]. * The keywords `#inf`, `#-inf`, and `#nan` evaluate to [`f64::INFINITY`], [`f64::NEG_INFINITY`], and [`f64::NAN`]. * The original _representation/text_ of these numbers will be preserved, unless you [`KdlDocument::autoformat`] in which case the original representation will be thrown away and the actual value will be used when serializing. ### Minimum Supported Rust Version (MSRV) You must be at least `1.95` tall to get on this ride. ### License The code in this repository is covered by [the Apache-2.0 License](./LICENSE). [`KdlDocument`]: https://docs.rs/kdl/latest/kdl/struct.KdlDocument.html [`KdlNode`]: https://docs.rs/kdl/latest/kdl/struct.KdlNode.html [`KdlEntry`]: https://docs.rs/kdl/latest/kdl/struct.KdlEntry.html [`KdlIdentifier`]: https://docs.rs/kdl/latest/kdl/struct.KdlIdentifier.html [`KdlError`]: https://docs.rs/kdl/latest/kdl/struct.KdlError.html [`miette::Diagnostic`]: https://docs.rs/miette/latest/miette/trait.Diagnostic.html [`miette::Result`]: https://docs.rs/miette/latest/miette/type.Result.html