gray-matter-rs

A fast Rust crate for extracting YAML, TOML, or JSON front matter out of Markdown and text content, with a pluggable engine trait for custom formats.

Library
Cargo
v0.3.2
57stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
31/100Needs Attention
Development Activity0
Maintenance20
Community32
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture78
Code Quality85
Innovation72
Learning Curve45

gray_matter is a Rust port of the popular JavaScript gray-matter library, built for pulling structured front matter out of Markdown files and other text content. Given a string with a delimited block at the top (--- by default), it splits the input into parsed metadata, an optional excerpt, and the remaining body content, returning all three in a single ParsedEntity struct.

The crate ships with built-in engines for YAML, TOML, and JSON, selected via Cargo feature flags so consumers only compile what they use. An Engine trait makes it straightforward to add support for other formats. Parsed data is represented internally as a Pod enum (a polyglot value type supporting nulls, strings, numbers, booleans, arrays, and hashes) which can be indexed directly or deserialized into a caller-defined struct via serde.

It’s a natural fit anywhere a Rust program needs to read configuration embedded in documents — static site generators, documentation tools, note-taking apps, or any CLI that processes Markdown files with YAML/TOML/JSON headers.

What You Get

  • A Matter<Engine> struct that parses front matter with a configurable open/close delimiter and optional separate excerpt delimiter
  • Built-in YAML, TOML, and JSON engines, each gated behind its own Cargo feature so unused parsers aren’t compiled in
  • An Engine trait for plugging in custom front matter formats beyond the three built-ins
  • A Pod polyglot value type with Index/IndexMut for direct field access plus as_string()/as_i64()/as_bool()-style accessors
  • Direct deserialization of parsed front matter into any serde::Deserialize struct via Matter::parse::<T>()
  • Automatic excerpt extraction — content between the front matter and a second delimiter (or a custom excerpt delimiter) is captured separately from the rest of the document

Common Use Cases

  • Static site generators reading YAML front matter (title, tags, date) out of Markdown posts before rendering
  • Documentation tooling that extracts TOML or JSON metadata headers from content files during a build step
  • Note-taking or knowledge-base tools that store structured metadata alongside free-form Markdown notes
  • CLI utilities that batch-process text files and need to separate a configuration header from the document body

Under The Hood

Architecture The crate centers on a single generic struct, Matter<T: Engine>, parameterized by which front matter format it should parse; PhantomData carries the engine type without adding runtime overhead. Matter::parse walks the input line by line through a small internal state machine (Part::Matter -> Part::MaybeExcerpt -> Part::Content) to isolate the delimited front matter block, an optional excerpt, and the remaining body, then hands the raw matter text to T::parse for engine-specific decoding into a Pod. Pod, defined in value/pod.rs, is the intermediate representation every engine converts into — a recursive enum covering null, string, integer, float, boolean, array, and hash variants with Index/IndexMut implementations for ergonomic field access, plus a serde Deserializer so a Pod can be converted directly into any caller-defined struct. Swapping the underlying YAML/TOML/JSON crate only requires touching that engine’s module in src/engine/; the delimiter-splitting logic in matter.rs and the Pod representation are unaffected.

Tech Stack Written in Rust 2018 edition with serde (derive feature) as the sole required runtime dependency for (de)serialization, plus thiserror for structured, typed error variants in value/error.rs. The three built-in parsing engines are optional: serde_json for the json feature, toml v0.9 for toml, and yaml-rust2 v0.10 for yaml (enabled by default). Development tooling includes criterion for benchmark harnesses (benches/parsing_benchmark.rs), cargo-husky to run tests, clippy, and fmt as pre-commit hooks, and a gray_matter_old (v0.2) dev-dependency used to compare against the crate’s own previous major version in benchmarks. CI runs via GitHub Actions, and the crate publishes to crates.io / docs.rs with all-features documentation.

Code Quality Testing is extensive and inline: matter.rs and value/pod.rs each carry dozens of #[test] functions covering delimiter edge cases (custom delimiters, mismatched open/close, empty matter, content that merely looks like a delimiter), excerpt extraction, and Pod indexing/comparison/deserialization behavior. A #[cfg(doctest)] macro even compiles the README’s own code blocks as doctests, so the published usage examples can’t silently drift from the actual API. Error handling is explicit and typed via thiserror-derived Error variants (e.g. Error::type_error) rather than panics or string errors, and cargo-husky’s pre-commit hook enforces cargo test, cargo clippy, and cargo fmt before code lands, backed by a CI workflow badge in the README.

API Design The public surface is deliberately small: constructing Matter::<YAML>::new() and calling .parse::<T>(input) covers the common case with no builder boilerplate, while delimiter, close_delimiter, and excerpt_delimiter are plain public fields on Matter rather than requiring a separate config struct. The Engine trait is a single associated function (fn parse(content: &str) -> Result<Pod>), keeping the bar for adding a custom format low. Pod’s Index/IndexMut implementations let consumers navigate parsed data with familiar data["key"][0] syntax before opting into strongly-typed deserialization, giving callers a choice between quick, loosely-typed access and a fully typed struct without changing parsing calls.

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