kdl/
lib.rs

1//! `kdl` is a "document-oriented" parser and API for the [KDL Document
2//! Language](https://kdl.dev), a node-based, human-friendly configuration and
3//! serialization format.
4//!
5//! Unlike serde-based implementations, this crate preserves formatting when
6//! editing, as well as when inserting or changing values with custom
7//! formatting. This is most useful when working with human-maintained KDL
8//! files.
9//!
10//! You can think of this crate as
11//! [`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
12//!
13//! This crate supports both KDL v2.0.0 and v1.0.0 (when using the non-default
14//! `v1` feature). It also supports converting documents between either format.
15//!
16//! There is also a `v1-fallback` feature that may be enabled in order to have
17//! the various `Kdl*::parse` methods try to parse their input as v2, and, if
18//! that fails, try again as v1. In either case, a dedicated `Kdl*::parse_v1`
19//! method is available for v1-exclusive parsing, as long as either `v1` or
20//! `v1-fallback` are enabled.
21//!
22//! ## Example
23//!
24//! ```rust
25//! use kdl::{KdlDocument, KdlValue};
26//!
27//! let doc_str = r#"
28//! hello 1 2 3
29//!
30//! // Comment
31//! world prop=string-value {
32//!     child 1
33//!     child 2
34//!     child #inf
35//! }
36//! "#;
37//!
38//! let doc: KdlDocument = doc_str.parse().expect("failed to parse KDL");
39//!
40//! assert_eq!(
41//!     doc.iter_args("hello").collect::<Vec<&KdlValue>>(),
42//!     vec![&1.into(), &2.into(), &3.into()]
43//! );
44//!
45//! assert_eq!(
46//!     doc.get("world").map(|node| &node["prop"]),
47//!     Some(&"string-value".into())
48//! );
49//!
50//! // Documents fully roundtrip:
51//! assert_eq!(doc.to_string(), doc_str);
52//! ```
53//!
54//! ## Controlling Formatting
55//!
56//! By default, everything is created with default formatting. You can parse
57//! items manually to provide custom representations, comments, etc:
58//!
59//! ```rust
60//! let node_str = r#"
61//!   // indented comment
62//!   "formatted" 1 /* comment */ \
63//!     2;
64//! "#;
65//!
66//! let mut doc = kdl::KdlDocument::new();
67//! doc.nodes_mut().push(node_str.parse().unwrap());
68//!
69//! assert_eq!(&doc.to_string(), node_str);
70//! ```
71//!
72//! [`KdlDocument`], [`KdlNode`], [`KdlEntry`], and [`KdlIdentifier`] can all
73//! be parsed and managed this way.
74//!
75//! ## Error Reporting
76//!
77//! [`KdlError`] implements [`miette::Diagnostic`] and can be used to display
78//! detailed, pretty-printed diagnostic messages when using [`miette::Result`]
79//! and the `"fancy"` feature flag for `miette`:
80//!
81//! ```toml
82//! # Cargo.toml
83//! [dependencies]
84//! miette = { version = "x.y.z", features = ["fancy"] }
85//! ```
86//!
87//! ```no_run
88//! fn main() -> miette::Result<()> {
89//!     "foo 1.".parse::<kdl::KdlDocument>()?;
90//!     Ok(())
91//! }
92//! ```
93//!
94//! This will display a message like:
95//! ```text
96//! Error:
97//!   × Expected valid value.
98//!    ╭────
99//!  1 │ foo 1.
100//!    ·     ─┬
101//!    ·      ╰── invalid float
102//!    ╰────
103//!   help: Floating point numbers must be base 10, and have numbers after the decimal point.
104//! ```
105//!
106//! ## Features
107//!
108//! * `span` (default) - Includes spans in the various document-related structs.
109//! * `v1` - Adds support for v1 parsing. This will pull in the entire previous
110//!   version of `kdl-rs`, and so may be fairly heavy.
111//! * `v1-fallback` - Implies `v1`. Makes it so the various `*::parse()` and
112//!   `FromStr` implementations try to parse their inputs as `v2`, and, if that
113//!   fails, try again with `v1`. For `KdlDocument`, a heuristic will be applied
114//!   if both `v1` and `v2` parsers fail, to pick which error(s) to return. For
115//!   other types, only the `v2` parser's errors will be returned.
116//!
117//! ## Quirks
118//!
119//! ### Properties
120//!
121//! Multiple properties with the same name are allowed, and all duplicated
122//! **will be preserved**, meaning those documents will correctly round-trip.
123//! When using `node.get()`/`node["key"]` & company, the _last_ property with
124//! that name's value will be returned (as per spec).
125//!
126//! ### Numbers
127//!
128//! KDL itself does not specify a particular representation for numbers and
129//! accepts just about anything valid, no matter how large and how small. This
130//! means a few things:
131//!
132//! * Numbers without a decimal point are interpreted as [`i128`].
133//! * Numbers with a decimal point are interpreted as [`f64`].
134//! * The keywords `#inf`, `#-inf`, and `#nan` evaluate to [`f64::INFINITY`],
135//!   [`f64::NEG_INFINITY`], and [`f64::NAN`].
136//! * The original _representation/text_ of these numbers will be preserved,
137//!   unless you [`KdlDocument::autoformat`] in which case the original
138//!   representation will be thrown away and the actual value will be used when
139//!   serializing.
140//!
141//! ## Minimum Supported Rust Version (MSRV)
142//!
143//! You must be at least `1.82` tall to get on this ride.
144//!
145//! ## License
146//!
147//! The code in this repository is covered by [the Apache-2.0
148//! License](./LICENSE).
149
150// TODO(@zkat): bring this back later.
151// ### Query Engine
152
153// `kdl` includes a query engine for
154// [KQL](https://github.com/kdl-org/kdl/blob/main/QUERY-SPEC.md), which lets you
155// pick out nodes from a document using a CSS Selectors-style syntax.
156
157// Queries can be done from either a [`KdlDocument`] or a [`KdlNode`], with
158// mostly the same semantics.
159
160// ```rust
161// use kdl::KdlDocument;
162
163// let doc = r#"
164// a {
165//     b 1
166//     c 2
167//     d 3 {
168//         e prop="hello"
169//     }
170// }
171// "#.parse::<KdlDocument>().expect("failed to parse KDL");
172
173// let results = doc.query("a > b").expect("failed to parse query");
174// assert_eq!(results, Some(&doc.nodes()[0].children().unwrap().nodes()[0]));
175
176// let results = doc.query_get("e", "prop").expect("failed to parse query");
177// assert_eq!(results, Some(&"hello".into()));
178
179// let results = doc.query_get_all("a > []", 0).expect("failed to parse query").collect::<Vec<_>>();
180// assert_eq!(results, vec![&1.into(), &2.into(), &3.into()]);
181// ```
182
183#![deny(missing_debug_implementations, nonstandard_style)]
184#![warn(missing_docs, rust_2018_idioms, unreachable_pub)]
185#![cfg_attr(test, deny(warnings))]
186#![cfg_attr(docsrs, feature(doc_auto_cfg))]
187#![doc(html_favicon_url = "https://kdl.dev/favicon.ico")]
188#![doc(html_logo_url = "https://kdl.dev/logo.svg")]
189
190pub use document::*;
191pub use entry::*;
192pub use error::*;
193pub use fmt::*;
194pub use identifier::*;
195pub use node::*;
196// pub use query::*;
197pub use value::*;
198
199mod document;
200mod entry;
201mod error;
202mod fmt;
203mod identifier;
204mod node;
205// mod nom_compat;
206// mod query;
207// mod query_parser;
208// mod v1_parser;
209mod value;
210
211mod v2_parser;