Skip to main content

Crate tomlini

Crate tomlini 

Source
Expand description

§tomlini — SAX TOML/INI parser and editor

A zero-dependency, three-tier (core/alloc/std) TOML implementation that parses into a flat span index instead of a DOM tree. Edits are byte-range splices on the source string — no decor model, no footguns.

§Quick start

use tomlini::Editor;

let mut doc = tomlini::parse("[server]\nport = 8080\n")?;

// Read values
assert!(doc.has("server.port"));
let raw = doc.get("server.port").unwrap();        // "8080"
let val = doc.get_decoded("server.port").unwrap(); // "8080"

// Edit with the batch editor
doc.edit()
    .set("server.port", "9090")
    .insert("server", "host", "\"0.0.0.0\"")
        .with_above_comment("Bind address")
    .commit()?;

let output = doc.to_string();

§Feature tiers

FeatureWhat you get
(none)parse_into() — classified spans via callback, zero allocations
allocFlatDoc, parse(), full editing API
std(default) std::error::Error impls

§Key types

  • FlatDoc — parsed document: source string + flat span index
  • Editor — batch mutation accumulator, commit applies all ops at once
  • BringAlong — composable flags for what adjacent text to carry when relocating
  • Span, SpanKind — classified byte ranges in the source
  • ParseError — parse error with byte position
  • ValidationMode — lenient/relaxed/strict validation levels
  • EditError — editor error variants (NotFound, InvalidPath, SectionExists, TableMismatch)
  • SpanSink — core-only callback trait for span emission

§Performance

Parse is 18× faster than toml_edit on a 94-line Cargo.toml (3.3 µs vs 59.5 µs). Batch edits are 2–3× faster than equivalent toml_edit operations. See crates/benchmarks/ for details.

§Format preservation

Every edit operation preserves comments, whitespace, and formatting unless explicitly overridden. We test for these invariants across all 22 edit operations:

  • Comments between keys survive insertions and removals
  • Inline comments on modified lines stay in place
  • Key formatting (quoted vs bare, dotted vs flat) is never altered
  • Value formatting (hex integers, multi-line strings, literal vs basic) passes through unchanged
  • Blank-line separators between sections are maintained
  • Indentation of new keys copies the neighbor’s indentation
  • Removing the last key in a section cleans up trailing whitespace
  • Dotted-key headers like [profiles.dev] survive reordering intact
  • Comments above keys move with the key when BringAlong flags are used

These invariants are verified by 11 footgun immunity tests and 8 proptest fuzzers that generate random documents and random edit sequences, asserting that the editor never panics and that successful commits produce re-parseable output.

§Movement & comment control

BringAlong bitflags let you control what adjacent text travels with a key or section when it moves:

use tomlini::editor::BringAlong;

// Move a key, bringing the comment above it
doc.edit().move_key_bring("a.k", "b.k", BringAlong::COMMENTS_ABOVE).commit()?;

// Promote to root, bringing comments on both sides
doc.edit().promote_key_bring("meta.base",
    BringAlong::COMMENTS_ABOVE | BringAlong::COMMENTS_BELOW).commit()?;

// Reorder root entries, keeping section-preceding comments with their section
doc.edit().reorder_root_bring(&["base", "meta"], BringAlong::COMMENTS_ABOVE).commit()?;

§Acknowledgments

Built on the excellent work of the toml-rs project: toml_edit, toml_datetime, and toml-test.

§INI files

tomlini parses INI-style configs out of the box — ; comments, bare values, = separators. No special mode needed: tomlini::parse(ini_str).

§Validation modes

use tomlini::ValidationMode;

doc.validate(ValidationMode::Lenient);   // everything accepted
doc.validate(ValidationMode::Relaxed);   // structural TOML + INI extensions
doc.validate(ValidationMode::Strict);    // full TOML 1.1.0 spec

§Container editing

Arrays, inline tables, and array-of-tables are first-class edit targets:

doc.edit()
    .array_push("hosts", "\"10.0.0.3\"")
    .inline_set("headers", "content-type", "\"text/html\"")
    .aot_push("backend", &[("host", "\"10.0.0.4\""), ("port", "9000")])
    .aot_remove("backend", 0)
    .commit()?;

§Core-only usage

Without the alloc feature, the parser emits spans through a callback:

tomlini::parse_into(input, &mut |kind, start, end| {
    // Called for every classified span
});

Modules§

editor
Batch editor for tomlini.

Structs§

FlatDoc
ParseError
Span
ValidationError
A single validation error with position and description.

Enums§

EditError
SpanKind
ValidationErrorKind
The kind of validation problem found.
ValidationMode
Validation strictness level.

Traits§

SpanSink
Sink trait for receiving classified spans without allocation.

Functions§

parse
Parse a TOML document into a FlatDoc for editing.
parse_into