Skip to main content

Module serde_yaml

Module serde_yaml 

Source
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_yaml 0.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 via cargo 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 verify serde_yaml’s unsafe blocks 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 original serde_yaml cannot do at all.
  • No dead branch. The compat-serde-yaml shim does not re-introduce the unmaintained crate as a dependency. Every type the shim exposes is a noyalib-native type re-exported under the serde_yaml name; downstream cargo audit / cargo deny never 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_yaml 0.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

  • Mapping is string-keyed. noyalib’s Mapping uses String keys; serde_yaml::Mapping allowed any Value as a key. The 99 % case (configuration files, RPC payloads) is string-keyed and works unchanged. If your code constructs a Mapping with non-string keys, switch to noyalib’s crate::MappingAny directly — the shim does not re-export it under the Mapping name 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::Tagged instead of being silently coerced to the inner string. from_str::<Value> on !Custom 'hello' returns Value::Tagged(Tag("!Custom"), Value::String("hello")), not Value::String("hello"). Migrants who previously exhaustive-matched the six-variant serde_yaml::Value need to either add a Value::Tagged(_) arm or call Value::untag / Value::untag_ref before the match. See docs/MIGRATION-FROM-SERDE-YAML.md §1 for the recipe.
  • YAML 1.2 strict booleans by default. country: NO stays "NO" (the YAML 1.2 fix to the “Norway problem”) instead of becoming false. Opt back into YAML 1.1 resolver semantics via ParserConfig::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::Error rendered the way serde_yaml 0.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.
TaggedValue
A tagged YAML value.

Enums§

Number
Represents a YAML number.
Value
Represents any valid YAML value.

Functions§

from_reader
Deserialize a YAML document from any std::io::Read source.
from_slice
Deserialize a YAML document from a byte slice.
from_str
Deserialize a YAML document into the target type — with serde_yaml 0.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 the Deserializer::from_str chained with into_iter::<T>() — the typical serde_yaml multi-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::Write sink.

Type Aliases§

Result
Shim result type: Result<T, Error> with the shim’s own Error.
Sequence
A YAML sequence (array/list).