yami
A lightweight, zero-copy, minimal-allocation YAML parser in Rust.
Overview
yami is an ultra-fast, zero-copy YAML parser designed for minimal heap allocations, deterministic behavior, and strict parsing semantics.
Unlike traditional YAML libraries that allocate owned Strings for every scalar, key, and value in a document, yami returns an Abstract Syntax Tree (AST) where all scalars borrow directly from the input text slice (&'a str). This eliminates heap churn and enables zero-allocation deserialization into domain structs.
Background & Motivation
In the Rust ecosystem, standard YAML parsers like serde_yaml cannot deserialize into structs containing borrowed string slices (&'a str) due to internal stream buffering and DeserializeOwned trait bounds:
// Attempting zero-copy deserialization with serde_yaml:
let app: App = from_str.unwrap;
// ^^^ ERROR: the trait `for<'de> Deserialize<'de>` is not implemented for `App<'_>`
// note: required by `serde_yaml::from_str` due to `DeserializeOwned` requirements
This limitation is documented in dtolnay/serde-yaml#94 ("Can't deserialize borrowed str with from_str") and highlighted in dtolnay/request-for-implementation#9 ("Minimal YAML parser").
yami was built specifically to solve this problem:
- Zero-Copy AST: Directly parses
&'a strintoYaml<'a>with scalars borrowing from the source buffer without heap allocations. - Strict Semantics: Inspired by StrictYAML,
yamieliminates the complexity and ambiguities of full YAML 1.2 (such as arbitrary object tags and silent type coercions), focusing on fast, clean, and deterministic configuration parsing.
Why yami?
- ⚡ Zero-Copy Scalars: All plain scalars, quoted strings (
'...'/"..."), booleans, and numbers borrow directly as&'a strfrom the input buffer. - 🚀 Ultra-Fast Throughput: Parses real-world configuration payloads at ~440,000 parses/sec (~2.26 µs per document, ~260 MB/sec throughput).
- 🛡️ Lifetime Safe: Verified with
compile_faildoctests to ensure borrowed AST nodes cannot outlive their source buffer. - 🎯 Precise Diagnostics: Reports exact 1-indexed
(line, column)coordinates and structured error variants viathiserror. - 📐 Strict & Unambiguous: Rejects ambiguous constructs such as tabs in indentation positions (
ErrorKind::TabInIndentation) and detects duplicate keys (ErrorKind::DuplicateKey). - 🧩 Block & Flow Hybrid: Seamlessly parses block mappings, block sequences, compact mappings (
- key: val), and JSON-style inline flow collections ([...]and{...}).
Installation
Add yami to your Cargo.toml:
Or manually specify it in Cargo.toml:
[]
= "0.1.0"
Quick Start
use ;
Zero-Copy Struct Extraction
You can extract configuration data directly into Rust domain models without allocating any String heap buffers:
use ;
Core Data Model
Navigation & Helper Methods
| Method | Return Type | Description |
|---|---|---|
doc["key"] |
&Yaml<'a> |
Index mapping by key (returns &Yaml::Scalar("") if missing) |
doc[index] |
&Yaml<'a> |
Index sequence by position (returns &Yaml::Scalar("") if out-of-bounds) |
doc.get("key") |
Option<&Yaml<'a>> |
Lookup mapping value by key |
doc.as_scalar() |
Option<&'a str> |
Borrows scalar slice |
doc.as_sequence() |
Option<&[Yaml<'a>]> |
Borrows sequence slice |
doc.as_mapping() |
Option<&[Entry<'a>]> |
Borrows mapping entries slice |
doc.to_bool() |
Result<bool, YamlError> |
Parses true/false, yes/no, on/off, 1/0 |
doc.to_i64() |
Result<i64, YamlError> |
Parses integer scalar |
doc.to_f64() |
Result<f64, YamlError> |
Parses floating-point scalar |
Error Handling & Diagnostics
Errors provide structured variants via thiserror and 1-indexed (line, column) source locations:
use ;
let malformed = "
server:
\thost: localhost # Tabs in indentation are strictly forbidden
";
match parse
Benchmarks & Performance
Run the release benchmark on your machine:
Benchmark Results (Apple Silicon M-Series)
============================================================
yami Zero-Copy YAML Parser Benchmark
============================================================
Payload size: 623 bytes
Iterations: 100,000
Total time: 226.41 ms
Time per parse: 2.26 µs (2264 ns)
Throughput: 441,683 parses/sec
Bandwidth: 262.42 MB/sec
============================================================
Development & Testing
# Run unit and integration tests
# Run snapshot regression tests with insta
# Run property-based fuzz tests with quickcheck
# Run doctests (including compile_fail lifetime tests)
# Run linter checks
# Check code formatting
Requirements
- Minimum Supported Rust Version (MSRV): Rust
1.80.0or later.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.