Expand description
§noyalib
A YAML 1.2 library for Rust. Pure safe code. Full serde integration.
§Two APIs, one parser
noyalib exposes two complementary surfaces over the same scanner and strictness rules. Pick the one that matches your job:
-
Data binding —
from_str,to_string,Value,StreamingDeserializer,borrowed::BorrowedValue. Read YAML into typed Rust data, write Rust data back to YAML. The round-trip travels through aValue/struct, so comments, blank lines, and the original whitespace are not preserved. Use this for config loaders, RPC payloads, and the 95% of YAML workloads that just want data. -
Tooling / automation —
cst::parse_document,cst::parse_stream,cst::Document. Read YAML into a side-table CST that reproduces the source byte-for-byte, targeted edits viadoc.set("path", "fragment")rewrite only the touched span — comments, formatting, and sibling entries are left untouched. Use this when what the user wrote matters (Renovate-style version bumps, Kubernetes manifest patchers, formatters, schema-driven linters). Seeexamples/lossless_edit.rs.
§Quick Start
use noyalib::{from_str, to_string};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
port: u16,
features: Vec<String>,
}
let yaml = "name: myapp\nport: 8080\nfeatures:\n - auth\n - api\n";
let config: Config = from_str(yaml).unwrap();
assert_eq!(config.name, "myapp");
assert_eq!(config.port, 8080);
let output = to_string(&config).unwrap();
let roundtrip: Config = from_str(&output).unwrap();
assert_eq!(config, roundtrip);§Deserialization
// From string, byte slice, reader, or Value
let v: Value = noyalib::from_str(yaml).unwrap();
let v: Value = noyalib::from_slice(bytes).unwrap();
let v: Value = noyalib::from_reader(file).unwrap();
let v: Value = noyalib::from_value(&value).unwrap();
// With security limits
let config = noyalib::ParserConfig::strict();
let v: Value = noyalib::from_str_with_config(yaml, &config).unwrap();§Serialization
// To string, writer, or fmt::Write
let yaml: String = noyalib::to_string(&value).unwrap();
let mut buf = Vec::new();
noyalib::to_writer(&mut buf, &value).unwrap();
let mut s = String::new();
noyalib::to_fmt_writer(&mut s, &value).unwrap();
// With custom config
let config = noyalib::SerializerConfig::new()
.indent(4)
.quote_all(true);
let yaml = noyalib::to_string_with_config(&value, &config).unwrap();§Highlights
- Pure Rust — native YAML 1.2 scanner and parser. No C bindings. No FFI.
- Zero
unsafe—#![forbid(unsafe_code)]enforced at compile time. - Fast — 75% faster serialization, 50% faster deserialization than serde_yaml_ng. Streaming deserializer bypasses the Value AST.
- Serde-native — serialize and deserialize any
Serialize/Deserializetype. - Ordered mappings —
IndexMap-backed. Insertion order preserved. - Source spans —
Spanned<T>tracks exact line, column, and byte offset. - Hardened — configurable depth, size, and alias limits. Billion-laughs safe.
- 100% YAML Test Suite — 406/406 official test cases pass.
- Zero-copy —
borrowed::BorrowedValueborrows strings from input. - Path queries —
value.query("items[*].name")with wildcards. no_std— works withalloconly (default-features = false).miette— optional rich terminal diagnostics (--features miette).
§API stability and SemVer policy
noyalib follows Semantic Versioning 2.0.0. Pre-1.0, the
version axis used for breaking changes is the patch number
during the 0.0.x series and the minor number during the
0.x.y series — patch bumps within a stable line are
source-compatible.
- Public surface = items reachable from the crate root by an
in-scope
pub use(this file). Items reachable only via apubmodule (e.g. helpers inborrowed,cst,policy) are also public; everything in apub(crate)/ private module is internal. #[non_exhaustive]is applied to every public configuration struct (ParserConfig,SerializerConfig,Error,MergeKeyPolicy,DuplicateKeyPolicy,FlowStyle,ScalarStyle,YamlVersion) so adding a field or variant in a future release is not a breaking change. Construct configuration via the documentednew/default/strictconstructors plus the builder setters; do not use exhaustive struct-literal syntax outside this crate.- What we will not break in patch releases:
- public function signatures (parameter names, types, return types);
- the
Valueenum’s variant set; - re-exported macro names (none today);
- the YAML 1.2 default-strictness contract.
- The deserialise-target bound is
T: for<'de> Deserialize<'de> + 'static. The'statichalf is the contract every real-worldDeserializeOwnedtype already satisfies (the HRTB itself disallows borrowed lifetimes); it is documented explicitly because a small number of externally-defined trait signatures (e.g.figment’sFormat::from_str) drop the'staticfrom their own bound — for those, noyalib provides feature-gated internal entry points that bypass theValue-tag-preserving fast path. - What may change without a major bump: non-default
ParserConfigsemantics under explicit opt-in (e.g. a futurelegacy_*flag), error message wording (variant names are stable), benchmark numbers, internal module layout. - Deprecations ship with
#[deprecated(since = "x.y.z", note = "...")]for at least one minor release before removal. CHANGELOG carries the migration recipe. - API drift checks:
cargo semver-checksruns in CI on every PR.
§MSRV policy
- Core library (
noyalib) — Rust 1.75.0 stable. CI’smsrv-1-75-corejob builds thedefault-features = falseand the standarddefaultset onrustc 1.75.0for every PR. The MSRV is treated as part of the public contract: a bump within0.0.xis a breaking change and ships a major version. - Optional features that pull a dep with a higher floor
(
miette,garde,validate-schema,figment,parallel,validator) inherit that dep’s MSRV — currently1.80–1.86depending on the feature. The CI matrix runs each one against the dep’s declaredrust-version. - Companion crates (
noya-cli,noyalib-lsp) carry their own higher MSRVs because their dep tree includes edition-2024 transitives —1.85.0for both at time of writing. nightly-simdis the only feature that requires nightly rustc (#![feature(portable_simd)]); abuild.rscfg-detect probe means stable builds with--all-featuresstill compile by treatingnightly-simdas a no-op.
§Feature flag matrix
All optional integrations are off by default — enable only
what your application needs. Default-on flags can be opted out
via default-features = false.
| Feature | Default | Pulls in | Adds | Implies |
|---|---|---|---|---|
std | ✅ | — | I/O, Spanned<T> deserialise, cst | — |
fast-int | ✅ | itoa | branchless integer formatting | std recommended |
fast-float | ✅ | ryu | branchless float formatting | std recommended |
strict-deserialise | ✅ | serde_ignored | from_*_strict family | std |
minimal | ⛔ | — | meta-alias for std only (drops the three above) | std |
miette | ⛔ | miette 7 | rich terminal diagnostics | — |
schema | ⛔ | schemars, serde_json | schema_for / schema_for_yaml + consumer must also depend on schemars = "1.2" to derive JsonSchema | — |
validate-schema | ⛔ | schema + jsonschema | validate_against_schema, coerce_to_schema | schema |
figment | ⛔ | figment 0.10 | figment::Yaml Provider | std |
garde | ⛔ | garde 0.22 | Validated<T> | — |
validator | ⛔ | validator 0.19 | ValidatedValidator<T> | — |
robotics | ⛔ | — | Degrees / Radians / StrictFloat newtypes | — |
parallel | ⛔ | rayon 1.10 | parallel::parse, parallel::values | std |
simd | ⛔ | — | noyalib::simd::* primitives | — |
nightly-simd | ⛔ | nightly rustc | 32-byte StructuralIter | simd |
compat-serde-yaml | ⛔ | — | noyalib::compat::serde_yaml shim | — |
compare-saphyr | ⛔ | serde-saphyr | comparison-bench arms (dev-only — do not ship in release builds) | — |
noyavalidate | ⛔ | — | meta-feature: validate-schema + miette | validate-schema, miette |
wasm-opt | ⛔ | — | marker consumed by noyalib-wasm’s build.rs to opt into a Binaryen post-build pass | — |
docs.rs builds with --all-features; every gated item is
tagged with the feature it requires via the doc(cfg(...))
badge.
§Concurrency guarantees
- All public top-level functions (
from_str,from_slice,from_reader,to_string,to_writer, …) are pure over their inputs and may be called concurrently from any number of threads. Value,Mapping,Number,Spanned<T>,ErrorareSend + Sync. Cloning aValueisO(n)in the value graph; share ownership viaArc<Value>when that cost matters.policy::PolicyrequiresSend + Syncso policies can be shared by reference across threads. Stateful policies should hold their state behind interior mutability (std::sync::Mutexor equivalent).Spanned<T>deserialisation uses a thread-local span context (stdfeature). The TLS guard installs on entry tofrom_str_with_configand clears on return — no leakage across calls or across threads.- Anchor and alias state lives in the parser stack frame (one per call); concurrent calls share no mutable state.
- The Rayon-backed
parallelmodule pre-scans document boundaries on the calling thread, then dispatches each document to the global Rayon pool —T: Sendis required. anchors::ArcAnchorRegistry/anchors::ArcAnchoruseArc+Weakand are explicitly multi-thread-safe; theRc-backed siblings are single-thread.
§Security posture
- No
unsafe—#![forbid(unsafe_code)]enforced at compile time on every workspace crate. - No FFI — pure Rust scanner / parser / serialiser /
CST. Closes the historical
libyamlC-FFI CVE class. - No arbitrary object instantiation from tags — custom
tags surface as
Value::Taggeddata; opt-in dispatch viaTagRegistry. There is no path from a parsed YAML document to running attacker-chosen code. - Resource budgets — seven configurable limits in
ParserConfigcap depth, document size, alias expansions, mapping keys, sequence length, duplicate-key policy, and boolean strictness.ParserConfig::stricttightens every budget for untrusted input. Alias-byte accumulation usessaturating_addso a crafted overflow still trips the cap. - Pluggable policies —
policy::DenyAnchors,policy::DenyTags,policy::MaxScalarLengthfor organisational “Safe YAML” enforcement. Custom policies implementpolicy::Policy. - Supply chain —
cargo audit,cargo deny,cargo vetgate every PR. Releases ship SLSA L3 provenance and sigstore signatures (verification cookbook inpkg/VERIFY.md). No archived or unmaintained crate appears in the dependency graph.
Disclosure policy: see
SECURITY.md.
§Performance and complexity
- Parser — single-pass,
O(n)in input bytes for the scanner; loader isO(n)events.IndexMapinsert is amortisedO(1);FxHasherkeeps key hashing cheap on short keys. - Streaming deserialise — bypasses the dynamic
ValueAST when the caller asks for a typedT, eliminating intermediate allocations. ~30% faster than the AST-via-Valuepath on real workloads. - Zero-copy scanner — string scalars come out as
Cow::Borrowedwhen no escape sequence forces an allocation.borrowed::BorrowedValuesurfaces this all the way to the caller. - SIMD primitives —
simd::find_any_ofdispatches tomemchrSSE2/NEON for arity 1/2/3 and SWAR for arity 4+. Withnightly-simd, the structural-bitmask scanner widens to 32-byte lanes — ~9× speedup vs the memchr loop on 1 MiB inputs. - SWAR decimal parser — folds 8 ASCII digits per
u64cycle. ~2× faster than<i64 as FromStr>::from_stron big numbers. - Serialiser — branchless integer (
itoa) and float (ryu) formatting in the hot path; falls back tocore::fmtunder--no-default-features. - Parallel multi-document —
parallel::parsescales near-linearly with cores on----separated streams; the pre-scan isO(input.len())on the calling thread. Value::cloneisO(n)over the value graph; share viaArc<Value>when that matters.
§Platform support
- Tier 1:
x86_64-unknown-linux-gnu,x86_64-apple-darwin,aarch64-apple-darwin,x86_64-pc-windows-msvc,aarch64-unknown-linux-gnu. CI runs on each of these on every PR. - Tier 2: musl Linux (
*-musl),i686-pc-windows-msvc,aarch64-pc-windows-msvc. Built in release CI; not gated on every PR. - Embedded /
no_std: any target supported byalloc. Thestd-only items (from_reader,to_writer,Spanned<T>deserialisation via TLS, thecstmodule) are gone; the rest of the surface compiles. CI enforcescargo check --no-default-featureson every PR. - WASM:
wasm32-unknown-unknownvia thenoyalib-wasmcompanion crate. 338 KB release binary (LTO). Browser demo incrates/noyalib/examples/wasm/. - Big-endian: validated under Miri’s
mips64-unknown-linux-gnuabi64simulation in the weeklymiri-bigendianjob.
§Error model
Every fallible function returns Result<T>
aliasing core::result::Result<T, Error>. Error is
#[non_exhaustive], implements core::fmt::Display,
core::error::Error (via std::error::Error under the
std feature), and — with --features miette —
miette::Diagnostic for rich terminal reports.
Each entry-point’s # Errors section enumerates the variant
set callers must handle; cross-reference the Error
variants for descriptions.
Re-exports§
pub use document::DocumentReadIterator;stdpub use document::read;stdpub use document::read_with_config;stdpub use document::load_all;pub use document::load_all_as;pub use document::load_all_with_config;pub use document::try_load_all;pub use fmt::Commented;pub use fmt::FlowMap;pub use fmt::FlowSeq;pub use fmt::FoldStr;pub use fmt::FoldString;pub use fmt::LitStr;pub use fmt::LitString;pub use fmt::SpaceAfter;pub use tag_registry::TagRegistry;pub use validated::Validated;gardepub use validated::ValidatedValidator;validator
Modules§
- ariadne_
adapter ariadne - Zero-copy YAML values that borrow from the input.
ariadneadapter — rendercrate::Erroras anariadne::Reportwith the offending byte range labelled. Behind theariadneCargo feature.ariadneadapter forcrate::Error. - borrowed
- Zero-copy YAML values that borrow strings from the input.
- compat
- Drop-in compatibility shims for upstream YAML crates. Each shim
is gated behind its own feature flag so unused migration paths
add zero compile cost. See
compat::serde_yamlfor theserde_yaml0.9 surface. Compatibility shims for downstream crates migrating tonoyalib. - cst
std - Side-table CST for byte-faithful round-tripping with typed path-targeted edits.
- diagnostic
miette - Spanned-to-miette diagnostic bridge (requires
miettefeature). Spanned value tomiette::Reportbridge. - document
- Multi-document loading and iteration. Multi-document YAML loading.
- figment
figment figmentprovider integration. Pulls infigment0.10 when thefigmentCargo feature is enabled.figmentprovider for noyalib YAML.- fmt
- Formatting wrappers for per-value YAML output style control. Formatting wrappers for fine-grained control over YAML output style.
- i18n
- Pluggable error-message formatters:
i18n::MessageFormattertrait plusi18n::DefaultFormatter(developer-facing, verbatim) andi18n::UserFormatter(user-facing, simplified language). Usecrate::Error::render_with_formatterto plug in localisation tables or custom rendering. Pluggable error-message formatters for user-facing rendering. - include
include !includedirective — resolver types (IncludeResolver,IncludeRequest,InputSource,SymlinkPolicy,SafeFileResolver). Wired intoParserConfig::include_resolver.!includedirective support — compose YAML documents from multiple files via the!include path/to/file.yamltag.- interner
- Key interning for memory-efficient repeated-key workloads. Key interning for memory-efficient repeated-key workloads.
- parallel
parallel - Parallel multi-document YAML parsing via Rayon. Gated by the
parallelfeature. Parallel multi-document YAML parsing — the “MapReduce” path. - policy
- Pluggable parser policies for “Safe YAML” enforcement. Pluggable parser policies for “Safe YAML” enforcement.
- robotics
robotics - Robotics and scientific numeric types (requires
roboticsfeature). Robotics and scientific numeric types for precise YAML deserialization. - simd
- SIMD-friendly multi-byte search primitives.
- tag_
registry - Streaming-path registry for custom YAML tag pass-through.
- validated
gardeorvalidator - Declarative post-deserialise validation via
gardeorvalidator(requires the corresponding feature). Declarative validation viagardeorvalidator. - validated_
miette miette Spanned<T>+ garde / validator →miette::Reportbridge. Behind themietteCargo feature; the actual conversion functions are gated onmiette + gardeormiette + validator.Spanned<T>+garde/validator→miette::Reportbridge.- with
- Helper modules for customizing serialization and deserialization.
Macros§
- parser_
config - Construct a
crate::ParserConfigfrom a field-value list. - serializer_
config - Construct a
crate::SerializerConfigfrom a field-value list.
Structs§
- Anchor
Registry - Registry for shared
Rcanchor references during deserialization. - ArcAnchor
- An
Arcwrapper with YAML anchor semantics. - ArcAnchor
Registry - Registry for shared
Arcanchor references during deserialization. - ArcRecursion
std - Thread-safe weak recursive reference — pairs with
ArcRecursive. - ArcRecursive
std - Thread-safe recursive anchor type — the
RcRecursivecounterpart for cross-thread / parallel-parse use cases. - ArcWeak
Anchor - A weak
Arcreference with YAML anchor semantics. - Comment
- A scanned YAML comment with its source span and classification.
- Cropped
Region - A windowed slice of source text around an error location.
- Deserializer
- A YAML deserializer.
- Flattened
- Wrapper that pairs a typed deserialization of
Twith the underlyingValuetree captured from the source. - Location
- A
(line, column, byte index)location in a YAML document. - Mapping
- A YAML mapping (dictionary/object).
- Mapping
Any - A YAML mapping with
Valuekeys. - Parse
Number Error - Error returned when parsing a number from a string fails.
- Parser
Config - Deserialization configuration.
- RcAnchor
- An
Rcwrapper with YAML anchor semantics. - RcRecursion
std - Single-threaded weak recursive reference — pairs with
RcRecursive. - RcRecursive
std - Single-threaded recursive anchor type for cyclic / late-initialised YAML graphs.
- RcWeak
Anchor - A weak
Rcreference with YAML anchor semantics. - Render
Options - Caller-controlled rendering options for
Error::render_with_options. - Serializer
- A YAML serializer.
- Serializer
Config - Configuration options for YAML serialization.
- Spanned
- A value annotated with source span information.
- Streaming
Deserializer - A streaming YAML deserializer operating directly on parser events.
- Tag
- A YAML tag.
- Tagged
Value - A tagged YAML value.
Enums§
- Budget
Breach - Errors that can occur during YAML serialization or deserialization.
- Comment
Kind - Classification of a scanned comment.
- Duplicate
KeyPolicy - Policy for handling duplicate keys in a YAML mapping.
- Error
- Examples
- Flow
Style - Flow style preference for collections.
- Maybe
Tag - Result of checking whether a value looks like a YAML tag.
- Merge
KeyPolicy - Number
- Represents a YAML number.
- Path
- Represents a path to a location within a YAML document structure.
- Require
Indent - Policy for handling the YAML merge key (
<<) during parsing. - Scalar
Style - Scalar style preference for strings.
- Value
- Represents any valid YAML value.
- Yaml
Version - Which version of the YAML specification the resolver follows.
Traits§
- Json
Schema schema - Re-export of the
schemarsderive macro and trait. DerivingJsonSchemaon your Rust type is what makesschema_for/schema_for_yamlproduce a schema for it. - Value
Index - A type that can be used to index into a
Value.
Functions§
- check_
for_ tag - Checks whether a value’s display representation looks like a YAML tag.
- coerce_
to_ schema validate-schema - Apply schema-driven type coercions to
valuein place. Walks the validator output for type-mismatch errors, and for each case where the instance is aValue::Stringbut the schema requires an integer / number / boolean — and the string parses cleanly into that target type — replaces the offending node with the coerced value. - from_
reader std - Deserialize YAML from an
std::io::Readsource. - from_
reader_ strict stdandstrict-deserialise - Strict deserialise from an IO reader — like
from_readerbut errors if the input contains keys the target typeTdoes not declare. - from_
reader_ with_ config std - Deserialize YAML from an
std::io::Readsource with a customParserConfig. - from_
slice - Deserialize YAML from a byte slice.
- from_
slice_ strict stdandstrict-deserialise - Strict deserialise from a byte slice — like
from_slicebut errors if the input contains keys the target typeTdoes not declare. - from_
slice_ with_ config - Deserialize YAML from a byte slice with a custom
ParserConfig. - from_
str - Deserialize YAML from a
&strinto a typedT. - from_
str_ borrowing - Deserialise a YAML document into a target type that may borrow
from the input slice (e.g.
&'a str,Cow<'a, str>, structs containing those). - from_
str_ borrowing_ with_ config from_str_borrowingwith a customParserConfig— typically to tighten security limits for untrusted input.- from_
str_ strict stdandstrict-deserialise - Strict deserialise: like
from_strbut errors ifscontains any keys that the target typeTdoes not declare. - from_
str_ with_ config - Deserialize YAML from a
&strwith a customParserConfig. - from_
value - Deserialize a
Valueinto a typedTvia Serde’s data model. - is_
yaml_ failsafe_ compatible - Check if a value uses only YAML 1.2 Failsafe schema types.
- is_
yaml_ json_ compatible - Check if a value is valid against the YAML 1.2 JSON-compatible schema.
- load_
comments - Scan a YAML document and return the list of comments it contains.
- nobang
- Strips a leading
!from a string, if present. - schema_
for schema - Generate the JSON Schema 2020-12 document for
Tand parse it into acrate::Valuetree, ready for indexing, transcoding, or further programmatic walking. - schema_
for_ yaml schema - Generate the JSON Schema 2020-12 document for
Tand emit it as YAML text — ready to write to disk, share with downstream consumers, or check into version control alongside the type definition. - to_
fmt_ writer - Serialize a Rust value to YAML and write into a
core::fmt::Writesink — the no_std-friendly counterpart toto_writer. - to_
fmt_ writer_ with_ config - Serialize a Rust value to YAML and write into a
core::fmt::Writesink, using a customSerializerConfig. - to_
string - Serialize a Rust value to a YAML
String. - to_
string_ multi - Serialize an iterable of values as a multi-document YAML
string with
---document-start markers between each. - to_
string_ multi_ with_ config - Serialize an iterable of values as a multi-document YAML
string with a custom
SerializerConfig. - to_
string_ tracking_ shared std - Serialize a Rust type to a YAML string with automatic anchor/alias emission
for shared
RcandArcpointers wrapped inRcAnchor/ArcAnchor. - to_
string_ tracking_ shared_ with_ config std - Serialize with automatic anchor/alias emission and a custom configuration.
- to_
string_ value - Serialize a
Valuedirectly to a YAMLString, preservingValue::Taggedshape losslessly. - to_
string_ value_ with_ config - Serialize a
Valueto a YAMLStringwith a customSerializerConfig, preservingValue::Taggedshape losslessly. Seeto_string_valuefor the rationale. - to_
string_ with_ config - Serialize a Rust value to a YAML
Stringwith a customSerializerConfig. - to_
value - Serialize a Rust value into a dynamic
Valuetree via the Serde data model. - to_
writer std - Serialize a Rust value to YAML and write to a
std::io::Writesink. - to_
writer_ multi std - Serialize multiple values as multi-document YAML to a writer.
- to_
writer_ multi_ with_ config std - Serialize multiple values as multi-document YAML to a writer with custom configuration.
- to_
writer_ tracking_ shared std - Write YAML to a writer with automatic anchor/alias emission for shared
Rc/Arcpointers. - to_
writer_ tracking_ shared_ with_ config std - Write YAML to a writer with automatic anchor/alias emission and a custom configuration.
- to_
writer_ value std - Write a
Valueto anstd::io::Writesink, preservingValue::Taggedshape losslessly. Seeto_string_valuefor the rationale. - to_
writer_ value_ with_ config std - Write a
Valueto anstd::io::Writesink with a customSerializerConfig, preservingValue::Taggedshape losslessly. Seeto_string_valuefor the rationale. - to_
writer_ with_ config std - Serialize a Rust value to YAML and write to a
std::io::Writesink, using a customSerializerConfig. - validate_
against_ schema validate-schema - Validate
valueagainst the JSON Schema 2020-12 documentschema. Both inputs areValuetrees — the schema is usually loaded from a YAML / JSON file viacrate::from_str, or built programmatically. - validate_
against_ schema_ str validate-schema - Validate the YAML text in
yamlagainst the JSON Schema document inschema_yaml. Convenience wrapper aroundvalidate_against_schema— parses both inputs and forwards. - validate_
yaml_ core_ schema - Validate a value against the YAML 1.2 Core Schema.
- validate_
yaml_ failsafe_ schema - Validate a value against the YAML 1.2 Failsafe Schema.
- validate_
yaml_ json_ schema - Validate a value against the YAML 1.2 JSON-compatible schema level.
Type Aliases§
Derive Macros§
- Json
Schema schema - Re-export of the
schemarsderive macro and trait. DerivingJsonSchemaon your Rust type is what makesschema_for/schema_for_yamlproduce a schema for it.