Expand description
Small streaming KDL v2.0.1 parser.
Designed for reasonable performance and memory efficiency, at the expense (or benefit, depending on use) of not storing formatting information.
§Examples
use just_kdl::dom::{Document, Event, Entry, Node, Value};
use just_kdl::reader::Reader;
use just_kdl::writer::Writer;
let text = "an example; kdl {document}";To read a structured Document you can use the
FromIterator implementation:
let document = Reader::new(text.as_bytes())
.collect::<Result<Document, _>>()
.expect("syntax error");
assert_eq!(document, Document::from(vec![
Node {
name: "an".into(),
entries: vec![Entry::new_value(Value::String("example".into()))],
..Default::default()
},
Node {
name: "kdl".into(),
children: Some(vec![Node::new("document")].into()),
..Default::default()
},
]));Or just use the Reader directly to get a stream of
Events:
let events = Reader::new(text.as_bytes())
.collect::<Result<Vec<_>, _>>()
.expect("syntax error");
assert_eq!(events, &[
(Event::Node { r#type: None, name: "an".into() }, 0..2),
(Event::Entry(Entry::new_value(Value::String("example".into()))), 3..10),
(Event::End, 10..11),
(Event::Node { r#type: None, name: "kdl".into() }, 12..15),
(Event::Children, 16..17),
(Event::Node { r#type: None, name: "document".into() }, 17..25),
(Event::End, 25..25),
(Event::End, 25..26),
]);Because formatting information is lost, writing applies a default format style.
For document oriented uses, Document, Node, Entry, and Value
all implement Display:
assert_eq!(document.to_string(), "
an example
kdl {
document
}
".trim());And for stream-oriented uses, use the Writer:
let mut output = String::new();
let mut writer = Writer::new(&mut output);
for (event, _) in events {
writer.push(event);
}
assert_eq!(output, "
an example
kdl {
document
}
".trim());§Why?
The official Rust implementation is designed to support editing of KDL files. While this is normally useful, if you just need to parse the file into an internal data structure (configuration, document trees, etc.), the formatting information is entirely redundant, wasting parse time and memory.
Additionally, this implementation has a few other benefits:
- Full compliance with the v2.0.1 specification
- Significantly fewer dependencies!
alloc-only (no_std) support
§Benchmarks
On my personal laptop, (i5-1240P, power-saver profile):
| Opt. | Parser | Benchmark | Time | Alloc | Resize | Free | Net |
|---|---|---|---|---|---|---|---|
| Release | kdl-org/kdl | html-standard.kdl | 14.074s | 7.2GiB | 205.0MiB | 5.9GiB | 1.5GiB |
| Debug | kdl-org/kdl | html-standard.kdl | 141.845s | 7.2GiB | 205.0MiB | 5.9GiB | 1.5GiB |
| Release | just-kdl | html-standard.kdl | 0.930s | 290.2MiB | 40.6MiB | 6.0MiB | 324.8MiB |
| Debug | just-kdl | html-standard.kdl | 4.788s | 290.2MiB | 40.6MiB | 6.0MiB | 324.8MiB |
| Release | just-kdl (Read) | html-standard.kdl | 1.053s | 290.2MiB | 40.6MiB | 6.0MiB | 324.8MiB |
| Debug | just-kdl (Read) | html-standard.kdl | 5.939s | 290.2MiB | 40.6MiB | 6.0MiB | 324.8MiB |
| Release | kdl-org/kdl | html-standard-compact.kdl | 9.321s | 4.5GiB | 163.1MiB | 3.9GiB | 871.4MiB |
| Debug | kdl-org/kdl | html-standard-compact.kdl | 101.818s | 4.5GiB | 163.1MiB | 3.9GiB | 871.4MiB |
| Release | just-kdl | html-standard-compact.kdl | 0.730s | 165.0MiB | 35.1MiB | 5.9MiB | 194.2MiB |
| Debug | just-kdl | html-standard-compact.kdl | 3.977s | 165.0MiB | 35.1MiB | 5.9MiB | 194.2MiB |
| Release | just-kdl (Read) | html-standard-compact.kdl | 0.759s | 165.0MiB | 35.1MiB | 5.9MiB | 194.2MiB |
| Debug | just-kdl (Read) | html-standard-compact.kdl | 4.704s | 165.0MiB | 35.1MiB | 5.9MiB | 194.2MiB |
(Read) = with std::io::Read overhead.
Benchmark source
In summary:
- 10-15× faster in Release, 25-30× faster in Debug
- Significantly fewer temporary allocations
- Fewer final output allocations