Skip to main content

Crate just_kdl

Crate just_kdl 

Source
Expand description

Repository Crates.io docs.rs MIT OR Apache-2.0

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.ParserBenchmarkTimeAllocResizeFreeNet
Releasekdl-org/kdlhtml-standard.kdl14.074s7.2GiB205.0MiB5.9GiB1.5GiB
Debugkdl-org/kdlhtml-standard.kdl141.845s7.2GiB205.0MiB5.9GiB1.5GiB
Releasejust-kdlhtml-standard.kdl0.930s290.2MiB40.6MiB6.0MiB324.8MiB
Debugjust-kdlhtml-standard.kdl4.788s290.2MiB40.6MiB6.0MiB324.8MiB
Releasejust-kdl (Read)html-standard.kdl1.053s290.2MiB40.6MiB6.0MiB324.8MiB
Debugjust-kdl (Read)html-standard.kdl5.939s290.2MiB40.6MiB6.0MiB324.8MiB
Releasekdl-org/kdlhtml-standard-compact.kdl9.321s4.5GiB163.1MiB3.9GiB871.4MiB
Debugkdl-org/kdlhtml-standard-compact.kdl101.818s4.5GiB163.1MiB3.9GiB871.4MiB
Releasejust-kdlhtml-standard-compact.kdl0.730s165.0MiB35.1MiB5.9MiB194.2MiB
Debugjust-kdlhtml-standard-compact.kdl3.977s165.0MiB35.1MiB5.9MiB194.2MiB
Releasejust-kdl (Read)html-standard-compact.kdl0.759s165.0MiB35.1MiB5.9MiB194.2MiB
Debugjust-kdl (Read)html-standard-compact.kdl4.704s165.0MiB35.1MiB5.9MiB194.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

Modules§

dom
Document tree structures.
lexer
Read raw tokens out of a file.
reader
Read document events out of a file.
validator
Verification of event streams.
writer
Write an event stream to an output.