A [KDL](https://kdl.dev) file format parser with great error reporting and convenient derive macros. # About KDL To give you some background on the KDL format. Here is a small example: ```kdl foo 1 key="val" "three" { bar (role)baz 1 2 } ``` Here is what are annotations for all the datum as described by the [specification] and this guide: ```text foo 1 "three" key="val" { ╮ ─┬─ ┬ ───┬─── ────┬──── │ │ │ │ ╰───── property (can be multiple) │ │ │ │ │ │ ╰────┴────────────── arguments │ │ │ └── node name ├─ node "foo", with │ "bar" and "baz" bar │ being children (role)baz 1 2 │ ──┬─ │ └────── type name for node named "baz" │ } ╯ ``` (note, the order of properties doesn't matter as well as the order of properties with respect to arguments, so I've moved arguments to have less intersections for the arrows) # Usage Most common usage of this library is using `derive` and [parse] function: ```rust #[derive(knuffel::Decode)] enum TopLevelNode { Route(Route), Plugin(Plugin), } #[derive(knuffel::Decode)] struct Route { #[knuffel(argument)] path: String, #[knuffel(children(name="route"))] subroutes: Vec, } #[derive(knuffel::Decode)] struct Plugin { #[knuffel(argument)] name: String, #[knuffel(property)] url: String, } # fn main() -> miette::Result<()> { let config = knuffel::parse::>("example.kdl", r#" route "/api" { route "/api/v1" } plugin "http" url="https://example.org/http" "#)?; # Ok(()) # } ``` This parses into a vector of nodes as enums `TopLevelNode`, but you also use some node as a root of the document if there is no properties and arguments declared: ```rust,ignore #[derive(knuffel::Decode)] struct Document { #[knuffel(child, unwrap(argument))] version: Option, #[knuffel(children(name="route"))] routes: Vec, #[knuffel(children(name="plugin"))] plugins: Vec, } let config = parse::("example.kdl", r#" version "2.0" route "/api" { route "/api/v1" } plugin "http" url="https://example.org/http" "#)?; ``` See description of [Decode](derive@Decode) and [DecodeScalar](derive@DecodeScalar) for the full reference on allowed attributes and parse modes. # Errors This crate publishes nice errors, like this: 
Screenshot of error. Here is how narratable printer would print the error:
Error: single char expected after `Alt+`
    Diagnostic severity: error
\
Begin snippet for test.kdl starting at line 17, column 1
\
snippet line 17:     }
snippet line 18:     key "Alt+" mode="normal" {
    label starting at line 18, column 10: invalid value
snippet line 19:         move-focus "left"
To make them working, [miette]'s "fancy" feature must be enabled in the final application's `Cargo.toml`: ```toml [dependencies] miette = { version="4.3.0", features=["fancy"] } ``` And the error returned from parser should be converted to [miette::Report] and printed with debugging handler. The most manual way to do that is: ```rust # #[derive(knuffel::Decode, Debug)] # struct Config {} # let file_name = "1.kdl"; # let text = ""; let config = match knuffel::parse::(file_name, text) { Ok(config) => config, Err(e) => { println!("{:?}", miette::Report::new(e)); std::process::exit(1); } }; ``` But usually function that returns `miette::Result` is good enough: ```rust,no_run # use std::fs; # #[derive(knuffel::Decode)] # struct Config {} use miette::{IntoDiagnostic, Context}; fn parse_config(path: &str) -> miette::Result { let text = fs::read_to_string(path).into_diagnostic() .wrap_err_with(|| format!("cannot read {:?}", path))?; Ok(knuffel::parse(path, &text)?) } fn main() -> miette::Result<()> { let config = parse_config("my.kdl")?; # Ok(()) } ``` See [miette guide] for other ways of configuring error output. # The Name KDL is pronounced as cuddle. "Knuffel" means the same as cuddle in Dutch. License ======= Licensed under either of * Apache License, Version 2.0, (./LICENSE-APACHE or ) * MIT license (./LICENSE-MIT or ) at your option. Contribution ------------ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [specification]: https://github.com/kdl-org/kdl/blob/main/SPEC.md [miette]: https://docs.rs/miette/ [miette guide]: https://docs.rs/miette/latest/miette/#-handler-options