Expand description
Drop-in API surface compatible with serde_yaml 0.9.
The upstream serde_yaml 0.9 crate is unmaintained. This
module exposes a name-for-name surface so existing codebases
can migrate by editing two lines:
§Why migrate from serde_yaml?
- Maintained.
serde_yaml0.9 was archived by its author in 2024; security advisories and YAML-spec corrections do not flow into it. noyalib is actively maintained. - Faster. noyalib’s deserialiser outpaces
serde_yaml_ng(the most active fork) by 39 – 64 % on representative workloads; the streaming path adds another 22 % on top of that for large documents. SIMD-accelerated structural discovery and SWAR decimal parsing pull big-document parses another 4–9× ahead on the bytes / second metric. Numbers are reproducible viacargo bench --bench comparison. - Zero
unsafe. noyalib enforces#![forbid(unsafe_code)]across the entire workspace — every line of parser, scanner, formatter, and CST code is checked at compile time. Audits that would otherwise need to verifyserde_yaml’sunsafeblocks evaporate. - Lossless tooling. noyalib ships a byte-faithful CST
(
crate::cst::Document) so editing tools can patch a single value while preserving every comment, indent, and sibling entry — something the originalserde_yamlcannot do at all. - No dead branch. The
compat-serde-yamlshim does not re-introduce the unmaintained crate as a dependency. Every type the shim exposes is a noyalib-native type re-exported under theserde_yamlname; downstreamcargo audit/cargo denynever picks up the archived advisory chain. - YAML 1.2 spec compliant. noyalib passes 406/406 cases
in the official YAML 1.2 test suite.
serde_yaml0.9 carries known spec deviations that are baked-in for back compat; noyalib has the freedom to fix them.
§Drop-in migration
Zero source changes, via Cargo’s package rename and the
noyalib-serde-yaml companion crate:
# Cargo.toml — the whole migration
serde_yaml = { package = "noyalib-serde-yaml", version = "=0.0.29" }Or two lines, depending on noyalib directly:
# Cargo.toml — before
serde_yaml = "0.9"
# Cargo.toml — after
noyalib = { version = "0.0", features = ["compat-serde-yaml"] }// anywhere in the codebase
- use serde_yaml::{from_str, to_string, Value};
+ use noyalib::compat::serde_yaml::{from_str, to_string, Value};Every function delegates to the underlying noyalib engine — no
double-parsing, no extra allocations, no parser fork. Where
serde_yaml’s signature differs from noyalib’s (the most common
case is taking a Value by value vs. by reference), this shim
provides a thin adapter; everything else is a re-export.
§Zero legacy dependencies
The shim deliberately does not depend on the unmaintained
serde_yaml 0.9 crate. Every type the shim exposes is a
noyalib-native type re-exported under the serde_yaml name —
you migrate off the legacy crate, not into a vendored copy
of it. Downstream cargo audit / cargo deny runs do not
pick up the archived advisory chain.
§Known surface differences
Mappingis string-keyed. noyalib’sMappingusesStringkeys;serde_yaml::Mappingallowed anyValueas a key. The 99 % case (configuration files, RPC payloads) is string-keyed and works unchanged. If your code constructs aMappingwith non-string keys, switch to noyalib’scrate::MappingAnydirectly — the shim does not re-export it under theMappingname to keep type errors localised.
§Migrating an in-flight serde_yaml::Value
Mid-migration codebases sometimes still hold an upstream
::serde_yaml::Value produced by an un-migrated module. The
Serde data model is the universal translator: every
Serde-compatible value can be funnelled through
crate::from_value / crate::to_value without depending
on the upstream library.
// Upstream value in flight (un-migrated module hands you one).
let upstream: ::serde_yaml::Value = legacy_call();
// Lower it into noyalib::Value via the Serde bridge — works
// because both ASTs implement `Serialize` / `Deserialize`.
let lowered: noyalib::Value = noyalib::to_value(&upstream)?;
// Or go straight to a typed struct, skipping the Value AST.
let cfg: MyConfig = noyalib::from_value(&noyalib::to_value(&upstream)?)?;Going the other direction is just as direct:
let lifted: ::serde_yaml::Value =
::serde_yaml::to_value(&my_noyalib_value)?;Both directions cost one Serde round-trip — the same wall-clock
cost as a hand-written From impl on a representative
Value shape. The benefit: zero dependency on the archived
crate.
§Error parity
The Error::location() -> Option<Location> /
Location::line() / Location::column() / Location::index()
chain matches serde_yaml’s shape byte-for-byte (1-indexed
line and column, 0-indexed byte offset), so existing
diagnostic-emitting code that destructures these compiles
unchanged.
§Migration cookbook
use noyalib::compat::serde_yaml as syml;
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
struct Config { name: String, port: u16 }
let yaml = "name: noyalib\nport: 8080\n";
let cfg: Config = syml::from_str(yaml).unwrap();
assert_eq!(cfg, Config { name: "noyalib".into(), port: 8080 });
let back = syml::to_string(&cfg).unwrap();
let round: Config = syml::from_str(&back).unwrap();
assert_eq!(cfg, round);§Behavioural parity with upstream serde_yaml 0.9
Since v0.0.29 the shim is behavioural, not just
name-compatible: its entry points parse under
crate::ParserConfig::serde_yaml_compat and its Error
renders upstream’s wording and locations. The 18-case
serde_yaml contract suite (tests/serde_yaml_contract.rs,
expectations captured live from serde_yaml 0.9.34) pins:
literal << entries with resolved alias values, 0123 as a
string and 0b11 as 3, 1e999 as a string, full u64
precision with one-past-u64::MAX refused as
JSON number out of range, non-scalar keys refused as
invalid type: sequence, expected a string key, upstream’s
repetition limit exceeded alias budget, and libyaml’s
error phrasing and end-of-input location convention. One
documented partial: a custom tag under deserialize_any
refuses with upstream’s message, anchored at the value rather
than the tag.
Callers who want noyalib’s own spec-strict defaults use the
direct API (crate::from_str); the differences below then
apply:
- Custom-tag scalars surface as
Value::Taggedinstead of being silently coerced to the inner string.from_str::<Value>on!Custom 'hello'returnsValue::Tagged(Tag("!Custom"), Value::String("hello")), notValue::String("hello"). Migrants who previously exhaustive-matched the six-variantserde_yaml::Valueneed to either add aValue::Tagged(_)arm or callValue::untag/Value::untag_refbefore the match. Seedocs/MIGRATION-FROM-SERDE-YAML.md§1 for the recipe. - YAML 1.2 strict booleans by default.
country: NOstays"NO"(the YAML 1.2 fix to the “Norway problem”) instead of becomingfalse. Opt back into YAML 1.1 resolver semantics viaParserConfig::version(YamlVersion::V1_1)if your existing pipeline depended on the legacy boolean recognition.
Both of these are documented under “Things noyalib adds”
and “Behavioural differences worth knowing” in the migration
guide. Neither is reachable via the existing serde_yaml
API surface — they are extra information / safer defaults
that flow through unchanged for the typed-deserialise path.
Modules§
- mapping
- Sub-module mirroring
serde_yaml::mapping. - value
- Sub-module mirroring
serde_yaml::value. - with
- Sub-module mirroring
serde_yaml::with.
Structs§
- Deserializer
- A YAML deserializer.
- Error
- The shim’s error type: a
crate::Errorrendered the wayserde_yaml0.9 worded and located it. - Location
- A
(line, column, byte index)location in a YAML document. - Mapping
- A YAML mapping (dictionary/object).
- Serializer
- A YAML serializer.
- Tag
- A YAML tag.
- Tagged
Value - A tagged YAML value.
Enums§
Functions§
- from_
reader - Deserialize a YAML document from any
std::io::Readsource. - from_
slice - Deserialize a YAML document from a byte slice.
- from_
str - Deserialize a YAML document into the target type — with
serde_yaml0.9’s observable behaviour, not noyalib’s defaults. - from_
str_ multi - Iterate every YAML document in a multi-document stream and
deserialize each into
T. Mirrors theDeserializer::from_strchained withinto_iter::<T>()— the typicalserde_yamlmulti-document idiom. - from_
value - Deserialize a typed value from a
Value. - to_
string - Serialize a typed value to a YAML string.
- to_
value - Serialize a typed value to a
Value. - to_
writer - Serialize a typed value to any
std::io::Writesink.
Type Aliases§
- Result
- Shim result type:
Result<T, Error>with the shim’s ownError. - Sequence
- A YAML sequence (array/list).