yaml-rust

A pure Rust YAML 1.2 parser and emitter with a dynamic Yaml enum and panic-free indexed access.

Library
Cargo
v0.4.5
648stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity0
Maintenance20
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality72
Innovation68
Learning Curve55

yaml-rust is a pure Rust implementation of the YAML 1.2 specification, built with no unsafe code and no C bindings. It parses YAML text into a dynamically-typed Yaml enum (Array, Hash, String, Integer, Real, Boolean, Null) that supports Ruby-like index access via doc["key"][0], and it can emit that same structure back to a YAML string.

The crate exposes both a high-level YamlLoader/YamlEmitter API for quick document parsing and dumping, and a lower-level, libyaml-influenced event stream (Parser, Scanner, Event) for consumers who need to react to tokens directly rather than build a full in-memory tree. It has shipped as the de facto pure-Rust YAML crate for years and remains widely depended upon even though active development has slowed.

What You Get

  • A YamlLoader::load_from_str entry point that parses a YAML string into a Vec<Yaml> covering multi-document streams
  • A Yaml enum with Ruby-like Index<&str> and Index<usize> implementations, returning Yaml::BadValue instead of panicking on invalid access
  • A YamlEmitter that serializes a Yaml tree back into a YAML string for round-tripping documents
  • A lower-level Parser/Scanner/Event API (via the EventReceiver and MarkedEventReceiver traits) for streaming, low-allocation consumption of YAML tokens
  • Typed accessor helpers (as_str, as_i64, as_f64, as_bool, as_vec, as_hash) for pulling native Rust values out of a parsed node

Common Use Cases

  • Reading application or CLI configuration files without defining a full serde-derived struct up front
  • Building tools that need to inspect or transform arbitrary/unknown-shape YAML documents dynamically
  • Round-tripping YAML through a parse-modify-emit cycle while preserving document structure
  • Implementing YAML-based DSLs or manifest formats where the schema is not known statically

Under The Hood

Architecture The crate is a straightforward four-module pipeline: scanner.rs tokenizes raw text into a VecDeque of typed tokens, parser.rs drives an explicit State enum (mirroring libyaml/yaml-cpp’s design) that turns those tokens into a stream of Events (DocumentStart, SequenceStart, Scalar, MappingEnd, and so on), yaml.rs’s YamlLoader implements the MarkedEventReceiver trait to fold that event stream into the public Yaml tree using a doc_stack/key_stack, and emitter.rs walks a Yaml tree back out to a string. Each stage only depends on the one before it, so a consumer can tap in at the event level (implementing EventReceiver directly) without ever materializing the full Yaml tree. The main fragility is that Yaml is a public, non-exhaustive-unmarked enum — anything matching on it directly breaks if a variant is ever added.

Tech Stack A minimal Rust 2018-edition crate with a single runtime dependency, linked-hash-map (for order-preserving Yaml::Hash maps), and quickcheck as a dev-dependency for property-based round-trip testing. There is no unsafe code, no async runtime, no build script, and no FFI — the whole crate is safe, synchronous, in-memory string processing. CI runs on Travis (stable/beta/nightly plus a pinned 1.33.0 MSRV job and an optional clippy lint job) and AppVeyor for Windows coverage, though both predate the project’s move to GitHub Actions-era tooling and the crate itself has been effectively feature-frozen since its 0.4.5 release.

Code Quality Testing is spread across three layers: inline #[cfg(test)] unit tests in lib.rs, a tests/spec_test.rs suite that replays YAML-test-suite-style events against the low-level parser, and tests/test_round_trip.rs, which asserts that parsing and re-emitting a document is lossless for tricky cases like escape characters, colons inside strings, and number-like quoted strings (several of which are direct regressions from filed GitHub issues). Errors are typed and returned as Result (ScanError, EmitError) rather than panicking in library code, and #[cfg_attr(feature = "cargo-clippy", ...)] attributes show clippy was run against the codebase, though there’s no rustfmt.toml or CI-enforced formatting check.

API Design The API optimizes for zero-ceremony reads: YamlLoader::load_from_str(s) plus chained doc["key"][0] indexing gets you into a document with no struct definitions, and invalid paths degrade to Yaml::BadValue instead of panicking, which keeps exploratory or partially-known-schema code simple. The tradeoff is that consumers wanting compile-time-checked structures need to hand-write the as_* conversions themselves, since this crate predates (and is now largely superseded by) serde-integrated alternatives like serde_yaml and its own community fork yaml-rust2.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search