serde_yml 0.0.13

DEPRECATED — `serde_yml` is unmaintained. This release is a thin compatibility shim that forwards every call to `noyalib` (a pure-Rust, `#![forbid(unsafe_code)]` YAML library). Please migrate to `noyalib`.
Documentation

Contents

Getting started

Choosing a replacement

Deprecation reference

Operational


Install

serde_yml = "0.0.13" is a stop-gap so an in-flight migration doesn't block your release. Existing call sites compile unchanged; the compiler emits a deprecation warning at each use serde_yml::* import pointing at the migration guide.

[dependencies]
serde_yml = "0.0.13"

The shim itself depends on noyalib's compat-serde-yaml feature for its implementation. The previous C-FFI parser (libyml) and serde_yaml 0.9 are no longer in the dependency graph. Whether your eventual destination is noyalib or one of the other maintained alternatives is your call.


Security: RUSTSEC-2025-0068 fixed in 0.0.13

RUSTSEC-2025-0068 (also GHSA-hhw4-xg65-fp2x) flagged all serde_yml versions ≤ 0.0.12 as unsound — the serde_yml::ser::Serializer.emitter field could cause a segmentation fault. The advisory is informational severity but real: it stems from the C-FFI libyaml parser the original crate linked against.

Upgrading to serde_yml = "0.0.13" removes the vulnerable surface entirely:

  • The C-FFI libyml dependency is gone from the graph.
  • serde_yml::ser::Serializer is now a re-export of a pure-Rust unit struct (pub struct Serializer;) with no emitter field. Any code that referenced .emitter won't compile — which is exactly the desired outcome.
  • The pure-Rust backend (noyalib) enforces #![forbid(unsafe_code)] across the whole workspace, so the underlying unsoundness class cannot recur through the shim.

Verification:

$ cargo update -p serde_yml --precise 0.0.13
$ cargo audit
# RUSTSEC-2025-0068 no longer flagged for serde_yml — the
# vulnerable surface is not present in 0.0.13.
$ cargo tree -p serde_yml | grep libyml
# (no output — libyml is not in the dependency graph)

The same fix flows through to any maintained alternative you eventually migrate to. Pinning serde_yml = "=0.0.12" keeps the advisory in your audit feed; pinning serde_yml = "0.0.13" (or migrating directly) clears it.


Quick Start

#![allow(deprecated)]
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
    name: String,
    port: u16,
}

fn main() -> serde_yml::Result<()> {
    let yaml = "name: myapp\nport: 8080\n";
    let cfg: Config = serde_yml::from_str(yaml)?;
    let back = serde_yml::to_string(&cfg)?;
    let round: Config = serde_yml::from_str(&back)?;
    assert_eq!(cfg, round);
    Ok(())
}

Run the bundled examples with cargo run --example example (the aggregator) or cargo run --example migration (a single-file demo).


Maintained alternatives

Three crates are realistic destinations for a serde_yml user. None is being prescribed — pick the one that fits the codebase.

Crate Latest Migration shape Best fit
noyalib 0.0 Drop-in via features = ["compat-serde-yaml"] — zero call-site changes for typical users Codebases that want a serde_yml-shaped API on a modern, safe, pure-Rust backend
serde-saphyr 0.0 Path rename for typed code; no Value DOM Typed-deserialise workloads (from_str::<MyStruct>) — the 95 % case. Doesn't fit codebases that hold dynamic Value trees in flight
yaml-rust2 0.9 Not serde-integrated — lower-level parser API Users who were on the low-level serde_yml::libyml / loader surface (removed in this shim) and want to keep working at that level

Decision guide

  • You used serde_yml::from_str / to_string / Value / Mapping / with::singleton_map* — pick noyalib. The compat-serde-yaml feature was built for this exact migration: one-line Cargo.toml change, one search-replace on imports, done.
  • You only ever called from_str::<MyStruct> and never touched the Value type — pick serde-saphyr. Smaller surface than noyalib, modern parser.
  • You were using serde_yml::libyml::* or serde_yml::loader::Loader — pick yaml-rust2. Those surfaces are gone from this shim regardless; yaml-rust2 is the natural Rust-native equivalent for low-level YAML processing.

One-minute migration paths

Side-by-side diff snippets for each destination. The full function-mapping tables are in MIGRATION.md.

noyalib

-[dependencies]
-serde_yml = "0.0"
+[dependencies]
+noyalib = { version = "0.0.5", features = ["compat-serde-yaml"] }
-use serde_yml::{from_str, to_string, Value};
+use noyalib::compat::serde_yaml::{from_str, to_string, Value};

serde-saphyr (typed-only)

-[dependencies]
-serde_yml = "0.0"
+[dependencies]
+serde-saphyr = "0.0"
-use serde_yml::from_str;
+use serde_saphyr::from_str;
 let cfg: MyConfig = from_str(yaml)?;

If your code holds a serde_yml::Value in flight, you cannot move it directly to serde-saphyr — there is no equivalent Value type. The two viable options are: (a) restructure to typed-only deserialisation, or (b) pick noyalib instead.

yaml-rust2 (low-level)

-[dependencies]
-serde_yml = "0.0"
+[dependencies]
+yaml-rust2 = "0.9"
-use serde_yml::{from_str, Value};
-let v: Value = serde_yml::from_str(yaml)?;
+use yaml_rust2::YamlLoader;
+let docs = YamlLoader::load_from_str(yaml)?;
+let v = &docs[0];

yaml-rust2 returns a Yaml enum (its own AST), not a serde::Deserialize value — bring your own typed-conversion code or hand-write the read paths. This is the right choice when you actually want the parser primitives.


What changed in 0.0.13

serde_yml 0.0.13 is a thin compatibility shim. The runtime dependency list dropped from six crates (libyml, indexmap, itoa, ryu, memchr, serde) to two (noyalib, serde) — the C-FFI parser is no longer in the graph. Existing call sites compile unchanged; the compiler emits a #[deprecated] warning at each one so you can budget the migration.

The shim being backed by noyalib internally is an implementation detail, not a recommendation to use noyalib specifically. The Maintained alternatives section above covers the choice.


What still works in 0.0.13

The shim is wire-compatible with typical user code. Verified by cargo test --all-targets + cargo run --example example + cargo run --example migration:

Surface Status
tests/shim.rs — typed round-trips, sub-module path imports, Error::location() 9 / 9 pass
examples/example.rs — aggregator running 17 sub-modules from serializer/, value/, with/ exits 0
examples/migration.rs — standalone shim demo exits 0

The full per-file inventory of retained / patched / removed tests and examples is in MIGRATION.md § "Test and example coverage in 0.0.13".


What was removed in 0.0.13

The deep internal modules that previous versions exposed leaked implementation details of the C-FFI parser. They are removed in this shim. The right replacement depends on which alternative you picked:

Removed from serde_yml What it was Where it goes
serde_yml::libyml::* Raw FFI bindings to C libyaml yaml-rust2 for low-level parsing; otherwise no equivalent (the pure-Rust replacements don't expose FFI)
serde_yml::loader::Loader Low-level YAML event loader yaml-rust2::YamlLoader; noyalib::load_all_as::<T> for typed
serde_yml::de::{Event, Progress} Event enum + input cursor Covered by yaml-rust2's parser API or noyalib's streaming Deserializer
serde_yml::de::DocumentAnchor Anchor-resolution helper noyalib and serde-saphyr resolve anchors transparently
serde_yml::ser::{SerializerConfig, State} C-emitter configuration noyalib::ser::Config
serde_yml::modules::path::Path Error-path builder noyalib::Error::location() / Error::path()
serde_yml::value::Index Sealed trait for Value indexing noyalib::Value implements Index<&str> / Index<usize> natively

The full table is in MIGRATION.md.


Behavioural notes

The shim is backed by noyalib's parser, which is intentionally safer than the original serde_yml defaults. Two behaviours flow through that you may need to handle:

  1. Custom-tag scalars surface as Value::Tagged rather than being silently coerced to the inner string. Exhaustive matches on the previous six-variant Value enum need either a Value::Tagged(_) arm or a call to Value::untag() / Value::untag_ref() before the match.

  2. YAML 1.2 strict booleans by default. country: NO stays "NO" (the YAML 1.2 fix to the "Norway problem") instead of becoming false. The legacy boolean recognition was a YAML 1.1 resolver behaviour.

Migrations to serde-saphyr or yaml-rust2 will encounter the same YAML 1.2 strictness (it is the spec-correct behaviour); only noyalib exposes an explicit opt-back-in via ParserConfig::version(YamlVersion::V1_1).


MSRV

serde_yml 0.0.13 requires Rust 1.85.0 (matching the backend's MSRV). The previous releases required 1.56. Users who cannot move past 1.56 should pin serde_yml = "=0.0.12" and plan a migration window.


Documentation

Document Covers
MIGRATION.md Find/replace tables per destination, full removed-surface mapping, test/example coverage triage, MSRV note
noyalibGitHub Drop-in destination via compat-serde-yaml feature
serde-saphyr Typed-deserialise destination
yaml-rust2 Low-level parser destination
docs.rs/serde_yml API reference for this shim — every item carries the #[deprecated] banner

License

Dual-licensed under Apache 2.0 or MIT, at your option.