Skip to content

hansstimer/nested-text

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nested-text

crates.io docs.rs

A fully spec-compliant NestedText v3.8 parser and serializer for Rust.

Compliance

nested-text passes 100% of the official NestedText test suite (KenKundert/nestedtext_tests):

  • 146/146 load tests — including full validation of error messages, line numbers, column numbers, and source line text for all 68 error cases
  • 80/80 roundtrip dump tests — every successfully loaded document survives a dump/load cycle with identical output
  • 2 tests skipped (non-UTF-8 encoding tests — NestedText is a UTF-8 format)

The implementation mirrors the architecture of the Python reference implementation by Ken Kundert: a line-based lexer feeding a recursive descent parser.

Installation

Add to your Cargo.toml:

[dependencies]
nested-text = "0.1"

serde support is enabled by default. To disable it:

[dependencies]
nested-text = { version = "0.1", default-features = false }

Usage

Direct API

use nested_text::{loads, dumps, Top, Value, DumpOptions};

// Parse a NestedText string
let input = "name: Alice\nage: 30\nitems:\n  - one\n  - two\n";
let value = loads(input, Top::Any).unwrap().unwrap();

// Access values
assert_eq!(value.get("name").unwrap().as_str(), Some("Alice"));
assert_eq!(value.get("age").unwrap().as_str(), Some("30"));

// Serialize back to NestedText
let output = dumps(&value, &DumpOptions::default());
let roundtripped = loads(&output, Top::Any).unwrap().unwrap();
assert_eq!(value, roundtripped);

Top-level type constraint

use nested_text::{loads, Top};

// Require a specific top-level type
let dict = loads("key: value", Top::Dict).unwrap();
let list = loads("- item", Top::List).unwrap();
let string = loads("> hello", Top::String).unwrap();

// Empty documents return a default value for the given type
let empty_dict = loads("", Top::Dict).unwrap();  // Some(Dict([]))
let empty_any = loads("", Top::Any).unwrap();     // None

serde integration

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
struct Config {
    name: String,
    debug: bool,
    port: u16,
    tags: Vec<String>,
}

let input = "\
name: my-app
debug: true
port: 8080
tags:
    - web
    - api
";

// Deserialize — numeric and boolean fields are parsed from strings
let config: Config = nested_text::from_str(input).unwrap();
assert_eq!(config.name, "my-app");
assert_eq!(config.debug, true);
assert_eq!(config.port, 8080);

// Serialize back to NestedText
let output = nested_text::to_string(&config).unwrap();

Reading from files

use nested_text::{load, Top};
use std::fs::File;

let file = File::open("config.nt").unwrap();
let value = load(file, Top::Any).unwrap();

Error handling

Errors include location information matching the reference implementation:

use nested_text::{loads, Top};

match loads("key: a\nkey: b", Top::Any) {
    Err(e) => {
        assert_eq!(e.message, "duplicate key: key.");
        assert_eq!(e.lineno, Some(1));  // 0-based
        assert_eq!(e.colno, Some(0));   // 0-based
        assert_eq!(e.line.as_deref(), Some("key: b"));
    }
    _ => panic!("expected error"),
}

Building values directly

use nested_text::{Value, dumps, DumpOptions};

let value = Value::Dict(vec![
    ("name".into(), Value::String("Alice".into())),
    ("scores".into(), Value::List(vec![
        Value::String("95".into()),
        Value::String("87".into()),
    ])),
]);

let nt = dumps(&value, &DumpOptions::default());
// name: Alice
// scores:
//     - 95
//     - 87

API Reference

Parsing

Function Description
loads(input, top) -> Result<Option<Value>> Parse a NestedText string
load(reader, top) -> Result<Option<Value>> Parse from any Read implementor
from_str<T>(input) -> Result<T> Deserialize via serde (requires serde feature)

Serialization

Function Description
dumps(value, options) -> String Serialize a Value to NestedText
dump(value, options, writer) -> Result<()> Serialize to any Write implementor
to_string(value) -> Result<String> Serialize via serde (requires serde feature)
to_string_with_options(value, options) -> Result<String> Serialize via serde with custom options

Types

  • ValueString(String), List(Vec<Value>), Dict(Vec<(String, Value)>)
    • Dicts preserve insertion order using Vec<(String, Value)>
    • Convenience methods: as_str(), as_list(), as_dict(), get(key), is_string(), is_list(), is_dict()
  • TopDict, List, String, Any
  • DumpOptionsindent: usize (default 4), sort_keys: bool (default false)
  • Errormessage, lineno (0-based), colno (0-based), line, kind

NestedText format

NestedText is a data format similar to YAML but with no type ambiguity — all leaf values are strings. No quoting, no escaping, no type coercion surprises.

# This is a comment
server:
    host: localhost
    port: 8080
    features:
        - logging
        - metrics
    database:
        > postgres://localhost
        > :5432/mydb

For the full specification, see nestedtext.org.

Validating compliance

Clone with the official test suite submodule and run:

git clone --recurse-submodules https://github.com/hansstimer/nested-text.git
cd nested-text
cargo test

To see detailed pass/fail counts:

cargo test --test official -- --nocapture

This runs the full official NestedText test suite automatically:

  • Load tests — decodes each test case from tests.json, parses the input, and compares the result against load_out. For error cases, validates the error message, line number, column number, and source line text against load_err.
  • Roundtrip dump tests — for every successful load test, serializes the parsed value back to NestedText, parses it again, and verifies the result is identical.

If you already cloned without --recurse-submodules:

git submodule update --init

License

Licensed under either of

at your option.

About

A fully spec-compliant NestedText v3.8 parser and serializer for Rust

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages