Skip to main content

noyalib/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2026 Noyalib. All rights reserved.
3
4//! # noyalib
5//!
6//! A YAML 1.2 library for Rust. Pure safe code. Full serde integration.
7//!
8//! ## Two APIs, one parser
9//!
10//! noyalib exposes two complementary surfaces over the same scanner
11//! and strictness rules. Pick the one that matches your job:
12//!
13//! - **Data binding** — [`from_str`], [`to_string`], [`Value`],
14//!   [`StreamingDeserializer`], [`borrowed::BorrowedValue`]. Read
15//!   YAML into typed Rust data, write Rust data back to YAML. The
16//!   round-trip travels through a `Value`/struct, so comments,
17//!   blank lines, and the original whitespace are not preserved.
18//!   Use this for config loaders, RPC payloads, and the 95% of YAML
19//!   workloads that just want data.
20//!
21//! - **Tooling / automation** — [`cst::parse_document`],
22//!   [`cst::parse_stream`], [`cst::Document`]. Read YAML into a
23//!   side-table CST that reproduces the source byte-for-byte,
24//!   targeted edits via `doc.set("path", "fragment")` rewrite only
25//!   the touched span — comments, formatting, and sibling entries
26//!   are left untouched. Use this when *what the user wrote* matters
27//!   (Renovate-style version bumps, Kubernetes manifest patchers,
28//!   formatters, schema-driven linters). See `examples/lossless_edit.rs`.
29//!
30//! ## Quick Start
31//!
32//! ```rust
33//! use noyalib::{from_str, to_string};
34//! use serde::{Deserialize, Serialize};
35//!
36//! #[derive(Debug, Serialize, Deserialize, PartialEq)]
37//! struct Config {
38//!     name: String,
39//!     port: u16,
40//!     features: Vec<String>,
41//! }
42//!
43//! let yaml = "name: myapp\nport: 8080\nfeatures:\n  - auth\n  - api\n";
44//! let config: Config = from_str(yaml).unwrap();
45//! assert_eq!(config.name, "myapp");
46//! assert_eq!(config.port, 8080);
47//!
48//! let output = to_string(&config).unwrap();
49//! let roundtrip: Config = from_str(&output).unwrap();
50//! assert_eq!(config, roundtrip);
51//! ```
52//!
53//! ## Deserialization
54//!
55//! ```rust,no_run
56//! # use noyalib::Value;
57//! # let yaml = "key: value";
58//! # let bytes = b"key: value";
59//! # let file = std::io::Cursor::new(yaml);
60//! # let value = Value::Null;
61//! // From string, byte slice, reader, or Value
62//! let v: Value = noyalib::from_str(yaml).unwrap();
63//! let v: Value = noyalib::from_slice(bytes).unwrap();
64//! let v: Value = noyalib::from_reader(file).unwrap();
65//! let v: Value = noyalib::from_value(&value).unwrap();
66//!
67//! // With security limits
68//! let config = noyalib::ParserConfig::strict();
69//! let v: Value = noyalib::from_str_with_config(yaml, &config).unwrap();
70//! ```
71//!
72//! ## Serialization
73//!
74//! ```rust,no_run
75//! # use noyalib::Value;
76//! # let value = Value::Null;
77//! // To string, writer, or fmt::Write
78//! let yaml: String = noyalib::to_string(&value).unwrap();
79//! let mut buf = Vec::new();
80//! noyalib::to_writer(&mut buf, &value).unwrap();
81//! let mut s = String::new();
82//! noyalib::to_fmt_writer(&mut s, &value).unwrap();
83//!
84//! // With custom config
85//! let config = noyalib::SerializerConfig::new()
86//!     .indent(4)
87//!     .quote_all(true);
88//! let yaml = noyalib::to_string_with_config(&value, &config).unwrap();
89//! ```
90//!
91//! ## Highlights
92//!
93//! - **Pure Rust** — native YAML 1.2 scanner and parser. No C bindings. No FFI.
94//! - **Zero `unsafe`** — `#![forbid(unsafe_code)]` enforced at compile time.
95//! - **Fast** — 75% faster serialization, 50% faster deserialization than
96//!   serde\_yaml\_ng. Streaming deserializer bypasses the Value AST.
97//! - **Serde-native** — serialize and deserialize any `Serialize` /
98//!   `Deserialize` type.
99//! - **Ordered mappings** — [`IndexMap`](indexmap::IndexMap)-backed. Insertion
100//!   order preserved.
101//! - **Source spans** — [`Spanned<T>`] tracks exact line, column, and byte
102//!   offset.
103//! - **Hardened** — configurable depth, size, and alias limits. Billion-laughs
104//!   safe.
105//! - **100% YAML Test Suite** — 406/406 official test cases pass.
106//! - **Zero-copy** — [`borrowed::BorrowedValue`] borrows strings from input.
107//! - **Path queries** — `value.query("items[*].name")` with wildcards.
108//! - **`no_std`** — works with `alloc` only (`default-features = false`).
109//! - **`miette`** — optional rich terminal diagnostics (`--features miette`).
110//!
111//! ## API stability and SemVer policy
112//!
113//! noyalib follows [Semantic Versioning 2.0.0]. Pre-`1.0`, the
114//! version axis used for breaking changes is the **patch number**
115//! during the `0.0.x` series and the **minor number** during the
116//! `0.x.y` series — patch bumps within a stable line are
117//! source-compatible.
118//!
119//! - **Public surface** = items reachable from the crate root by an
120//!   in-scope `pub use` (this file). Items reachable only via a
121//!   `pub` module (e.g. helpers in [`borrowed`], [`cst`],
122//!   [`policy`]) are also public; everything in a `pub(crate)` /
123//!   private module is internal.
124//! - **`#[non_exhaustive]`** is applied to every public
125//!   configuration struct ([`ParserConfig`], [`SerializerConfig`],
126//!   [`Error`], [`MergeKeyPolicy`], [`DuplicateKeyPolicy`],
127//!   [`FlowStyle`], [`ScalarStyle`], [`YamlVersion`]) so adding a
128//!   field or variant in a future release is **not** a breaking
129//!   change. Construct configuration via the documented
130//!   `new` / `default` / `strict` constructors plus the builder
131//!   setters; do not use exhaustive struct-literal syntax outside
132//!   this crate.
133//! - **What we will not break in patch releases:**
134//!   - public function signatures (parameter names, types, return
135//!     types);
136//!   - the [`Value`] enum's variant set;
137//!   - re-exported macro names (none today);
138//!   - the YAML 1.2 default-strictness contract.
139//! - **The deserialise-target bound is `T: for<'de>
140//!   Deserialize<'de> + 'static`.** The `'static` half is the
141//!   contract every real-world `DeserializeOwned` type already
142//!   satisfies (the HRTB itself disallows borrowed lifetimes); it
143//!   is documented explicitly because a small number of
144//!   externally-defined trait signatures (e.g. `figment`'s
145//!   `Format::from_str`) drop the `'static` from their own bound
146//!   — for those, noyalib provides feature-gated internal entry
147//!   points that bypass the [`Value`]-tag-preserving fast path.
148//! - **What may change without a major bump:** non-default
149//!   `ParserConfig` semantics under explicit opt-in (e.g. a future
150//!   `legacy_*` flag), error *message* wording (variant *names*
151//!   are stable), benchmark numbers, internal module layout.
152//! - **Deprecations** ship with `#[deprecated(since = "x.y.z",
153//!   note = "...")]` for at least one minor release before
154//!   removal. CHANGELOG carries the migration recipe.
155//! - **API drift checks**: `cargo semver-checks` runs in CI on
156//!   every PR.
157//!
158//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
159//!
160//! ## MSRV policy
161//!
162//! - **Core library (`noyalib`)** — Rust **1.85.0** stable
163//!   (edition 2024, raised in v0.0.5). CI's MSRV gate builds
164//!   the `default-features = false` and the standard `default`
165//!   set on `rustc 1.85.0` for every PR. The MSRV is treated as
166//!   part of the public contract: a bump within `0.0.x` is a
167//!   breaking change and ships a major version.
168//! - **Optional features** that pull a dep with a higher floor
169//!   (`miette`, `garde`, `validate-schema`, `figment`,
170//!   `parallel`, `validator`, `tokio`) inherit that dep's MSRV
171//!   — typically `1.85`–`1.86` depending on the feature. The
172//!   CI matrix runs each one against the dep's declared
173//!   `rust-version`.
174//! - **Companion crates** ([`noya-cli`], [`noyalib-lsp`]) carry
175//!   the same `1.85.0` floor.
176//! - **`nightly-simd`** is the only feature that requires nightly
177//!   rustc (`#![feature(portable_simd)]`); a `build.rs` cfg-detect
178//!   probe means stable builds with `--all-features` still
179//!   compile by treating `nightly-simd` as a no-op.
180//!
181//! [`noya-cli`]: https://crates.io/crates/noya-cli
182//! [`noyalib-lsp`]: https://crates.io/crates/noyalib-lsp
183//!
184//! ## Feature flag matrix
185//!
186//! All optional integrations are off by default — enable only
187//! what your application needs. Default-on flags can be opted out
188//! via `default-features = false`.
189//!
190//! | Feature | Default | Pulls in | Adds | Implies |
191//! | :--- | :---: | :--- | :--- | :--- |
192//! | `std` | ✅ | — | I/O, [`Spanned<T>`] deserialise, [`cst`] | — |
193//! | `fast-int` | ✅ | `itoa` | branchless integer formatting | `std` recommended |
194//! | `fast-float` | ✅ | `ryu` | branchless float formatting | `std` recommended |
195//! | `strict-deserialise` | ✅ | `serde_ignored` | `from_*_strict` family | `std` |
196//! | `minimal` | ⛔ | — | meta-alias for `std` only (drops the three above) | `std` |
197//! | `miette` | ⛔ | `miette 7` | rich terminal diagnostics | — |
198//! | `schema` | ⛔ | `schemars`, `serde_json` | [`schema_for`] / [`schema_for_yaml`] **+** consumer must also depend on `schemars = "1.2"` to derive [`JsonSchema`] | — |
199//! | `validate-schema` | ⛔ | `schema` + `jsonschema` | [`validate_against_schema`], [`coerce_to_schema`] | `schema` |
200//! | `figment` | ⛔ | `figment 0.10` | [`figment::Yaml`](crate::figment) Provider | `std` |
201//! | `garde` | ⛔ | `garde 0.22` | [`Validated<T>`] | — |
202//! | `validator` | ⛔ | `validator 0.19` | [`ValidatedValidator<T>`] | — |
203//! | `robotics` | ⛔ | — | `Degrees` / `Radians` / `StrictFloat` newtypes | — |
204//! | `parallel` | ⛔ | `rayon 1.10` | [`parallel::parse`], [`parallel::values`] | `std` |
205//! | `simd` | ⛔ | — | `noyalib::simd::*` primitives | — |
206//! | `nightly-simd` | ⛔ | nightly rustc | 32-byte `StructuralIter` | `simd` |
207//! | `compat-serde-yaml` | ⛔ | — | `noyalib::compat::serde_yaml` shim | — |
208//! | `compare-saphyr` | ⛔ | `serde-saphyr` | comparison-bench arms (dev-only — do **not** ship in release builds) | — |
209//! | `noyavalidate` | ⛔ | — | meta-feature: `validate-schema` + `miette` | `validate-schema`, `miette` |
210//! | `wasm-opt` | ⛔ | — | marker consumed by `noyalib-wasm`'s build.rs to opt into a Binaryen post-build pass | — |
211//!
212//! `docs.rs` builds with `--all-features`; every gated item is
213//! tagged with the feature it requires via the `doc(cfg(...))`
214//! badge.
215//!
216//! ## Concurrency guarantees
217//!
218//! - All public top-level functions ([`from_str`], [`from_slice`],
219//!   [`from_reader`], [`to_string`], [`to_writer`], …) are pure
220//!   over their inputs and may be called concurrently from any
221//!   number of threads.
222//! - [`Value`], [`Mapping`], [`Number`], [`Spanned<T>`],
223//!   [`Error`] are `Send + Sync`. Cloning a `Value` is `O(n)` in
224//!   the value graph; share ownership via
225//!   [`Arc`](std::sync::Arc)`<Value>` when that cost matters.
226//! - [`policy::Policy`] requires `Send + Sync` so policies can be
227//!   shared by reference across threads. Stateful policies should
228//!   hold their state behind interior mutability
229//!   ([`std::sync::Mutex`] or equivalent).
230//! - [`Spanned<T>`] deserialisation uses a thread-local span
231//!   context (`std` feature). The TLS guard installs on entry to
232//!   [`from_str_with_config`] and clears on return — no leakage
233//!   across calls or across threads.
234//! - Anchor and alias state lives in the parser stack frame (one
235//!   per call); concurrent calls share no mutable state.
236//! - The Rayon-backed [`parallel`] module pre-scans document
237//!   boundaries on the calling thread, then dispatches each
238//!   document to the global Rayon pool — `T: Send` is required.
239//! - [`anchors::ArcAnchorRegistry`] / [`anchors::ArcAnchor`] use
240//!   `Arc` + `Weak` and are explicitly multi-thread-safe; the
241//!   `Rc`-backed siblings are single-thread.
242//!
243//! ## Security posture
244//!
245//! - **No `unsafe`** — `#![forbid(unsafe_code)]` enforced at
246//!   compile time on every workspace crate.
247//! - **No FFI** — pure Rust scanner / parser / serialiser /
248//!   CST. Closes the historical `libyaml` C-FFI CVE class.
249//! - **No arbitrary object instantiation from tags** — custom
250//!   tags surface as [`Value::Tagged`] data; opt-in dispatch via
251//!   [`TagRegistry`]. There is no path from a parsed YAML
252//!   document to running attacker-chosen code.
253//! - **Resource budgets** — seven configurable limits in
254//!   [`ParserConfig`] cap depth, document size, alias
255//!   expansions, mapping keys, sequence length, duplicate-key
256//!   policy, and boolean strictness. [`ParserConfig::strict`]
257//!   tightens every budget for untrusted input. Alias-byte
258//!   accumulation uses `saturating_add` so a crafted overflow
259//!   still trips the cap.
260//! - **Pluggable policies** — [`policy::DenyAnchors`],
261//!   [`policy::DenyTags`], [`policy::MaxScalarLength`] for
262//!   organisational "Safe YAML" enforcement. Custom policies
263//!   implement [`policy::Policy`].
264//! - **Supply chain** — `cargo audit`, `cargo deny`, `cargo vet`
265//!   gate every PR. Releases ship SLSA L3 provenance and
266//!   sigstore signatures (verification cookbook in
267//!   [`pkg/VERIFY.md`](https://github.com/sebastienrousseau/noyalib/blob/main/pkg/VERIFY.md)).
268//!   No archived or unmaintained crate appears in the dependency
269//!   graph.
270//!
271//! Disclosure policy: see
272//! [`SECURITY.md`](https://github.com/sebastienrousseau/noyalib/blob/main/SECURITY.md).
273//!
274//! ## Performance and complexity
275//!
276//! - **Parser** — single-pass, `O(n)` in input bytes for the
277//!   scanner; loader is `O(n)` events. `IndexMap` insert is
278//!   amortised `O(1)`; `FxHasher` keeps key hashing cheap on
279//!   short keys.
280//! - **Streaming deserialise** — bypasses the dynamic `Value`
281//!   AST when the caller asks for a typed `T`, eliminating
282//!   intermediate allocations. ~30% faster than the
283//!   AST-via-`Value` path on real workloads.
284//! - **Zero-copy scanner** — string scalars come out as
285//!   `Cow::Borrowed` when no escape sequence forces an
286//!   allocation. [`borrowed::BorrowedValue`] surfaces this all
287//!   the way to the caller.
288//! - **SIMD primitives** — [`simd::find_any_of`] dispatches to
289//!   `memchr` SSE2/NEON for arity 1/2/3 and SWAR for arity 4+.
290//!   With `nightly-simd`, the structural-bitmask scanner widens
291//!   to 32-byte lanes — ~9× speedup vs the memchr loop on 1 MiB
292//!   inputs.
293//! - **SWAR decimal parser** — folds 8 ASCII digits per `u64`
294//!   cycle. ~2× faster than `<i64 as FromStr>::from_str` on big
295//!   numbers.
296//! - **Serialiser** — branchless integer (`itoa`) and float
297//!   (`ryu`) formatting in the hot path; falls back to
298//!   `core::fmt` under `--no-default-features`.
299//! - **Parallel multi-document** — [`parallel::parse`] scales
300//!   near-linearly with cores on `---`-separated streams; the
301//!   pre-scan is `O(input.len())` on the calling thread.
302//! - **`Value::clone`** is `O(n)` over the value graph; share
303//!   via `Arc<Value>` when that matters.
304//!
305//! ## Platform support
306//!
307//! - **Tier 1**: `x86_64-unknown-linux-gnu`,
308//!   `x86_64-apple-darwin`, `aarch64-apple-darwin`,
309//!   `x86_64-pc-windows-msvc`, `aarch64-unknown-linux-gnu`. CI
310//!   runs on each of these on every PR.
311//! - **Tier 2**: musl Linux (`*-musl`),
312//!   `i686-pc-windows-msvc`, `aarch64-pc-windows-msvc`. Built
313//!   in release CI; not gated on every PR.
314//! - **Embedded / `no_std`**: any target supported by `alloc`.
315//!   The `std`-only items ([`from_reader`], [`to_writer`],
316//!   [`Spanned<T>`] deserialisation via TLS, the [`cst`]
317//!   module) are gone; the rest of the surface compiles. CI
318//!   enforces `cargo check --no-default-features` on every PR.
319//! - **WASM**: `wasm32-unknown-unknown` via the `noyalib-wasm`
320//!   companion crate. 338 KB release binary (LTO). Browser
321//!   demo in `crates/noyalib/examples/wasm/`.
322//! - **Big-endian**: validated under Miri's
323//!   `mips64-unknown-linux-gnuabi64` simulation in the weekly
324//!   `miri-bigendian` job.
325//!
326//! ## Error model
327//!
328//! Every fallible function returns [`Result<T>`](crate::Result)
329//! aliasing `core::result::Result<T, Error>`. [`Error`] is
330//! `#[non_exhaustive]`, implements `core::fmt::Display`,
331//! `core::error::Error` (via `std::error::Error` under the
332//! `std` feature), and — with `--features miette` —
333//! `miette::Diagnostic` for rich terminal reports.
334//!
335//! Each entry-point's `# Errors` section enumerates the variant
336//! set callers must handle; cross-reference the [`Error`]
337//! variants for descriptions.
338
339// SPDX-License-Identifier: MIT OR Apache-2.0
340// Copyright (c) 2026 Noyalib. All rights reserved.
341
342#![forbid(unsafe_code)]
343#![warn(missing_docs)]
344#![cfg_attr(docsrs, feature(doc_cfg))]
345#![cfg_attr(not(feature = "std"), no_std)]
346#![cfg_attr(
347    all(feature = "nightly-simd", noyalib_nightly),
348    allow(unstable_features),
349    feature(portable_simd)
350)]
351// Opt-in coverage annotations. `noyalib_coverage` is set by the
352// build script when `NOYALIB_COVERAGE=1` is exported (typically by
353// the CI coverage job running on nightly). When active, items
354// annotated with `#[cfg_attr(noyalib_coverage, coverage(off))]`
355// are excluded from coverage instrumentation. Stable builds and
356// regular nightly builds never see the `coverage_attribute`
357// feature flag, so the annotations are no-ops there.
358#![cfg_attr(noyalib_coverage, allow(unstable_features))]
359#![cfg_attr(noyalib_coverage, feature(coverage_attribute))]
360
361// README doctest coverage: every ```rust block in
362// crates/noyalib/README.md is exercised by `cargo test --doc`.
363// The hidden module exists only when doctesting so the README
364// content does not leak into the docs.rs page (the lib's own
365// crate-level docs above are the canonical surface there).
366#[cfg(doctest)]
367#[doc = include_str!("../README.md")]
368mod readme_doctests {}
369
370// The workspace-root README (GitHub landing page) is doctested by
371// `scripts/check-readme-examples.sh` — every ```rust block there
372// is extracted and compiled against `noyalib` from a scratch
373// project. Cannot `include_str!("../../../README.md")` here
374// because the workspace-root README lives outside the crate's
375// package layout, so `cargo publish --dry-run` would fail
376// verification. The script-based path preserves the invariant
377// (broken root-README examples fail CI) without breaking publish.
378
379#[cfg(not(feature = "std"))]
380extern crate alloc;
381
382/// Internal prelude for no_std compatibility.
383/// Provides String, Vec, Box, etc. from alloc when std is absent.
384#[cfg(not(feature = "std"))]
385pub(crate) mod prelude {
386    pub(crate) use alloc::borrow::{Cow, ToOwned};
387    pub(crate) use alloc::boxed::Box;
388    pub(crate) use alloc::format;
389    pub(crate) use alloc::string::{String, ToString};
390    pub(crate) use alloc::sync::Arc;
391    pub(crate) use alloc::vec;
392    pub(crate) use alloc::vec::Vec;
393    pub(crate) use core::fmt;
394}
395
396/// Internal prelude for std compatibility.
397#[cfg(feature = "std")]
398pub(crate) mod prelude {
399    pub(crate) use std::borrow::{Cow, ToOwned};
400    pub(crate) use std::boxed::Box;
401    pub(crate) use std::fmt;
402    pub(crate) use std::format;
403    pub(crate) use std::string::{String, ToString};
404    pub(crate) use std::sync::Arc;
405    pub(crate) use std::vec;
406    pub(crate) use std::vec::Vec;
407}
408
409mod anchors;
410/// Zero-copy YAML values that borrow from the input.
411/// [`ariadne`] adapter — render `crate::Error` as an
412/// `ariadne::Report` with the offending byte range labelled.
413/// Behind the `ariadne` Cargo feature.
414#[cfg(feature = "ariadne")]
415#[cfg_attr(docsrs, doc(cfg(feature = "ariadne")))]
416pub mod ariadne_adapter;
417
418/// Internal RFC 4648 base64 codec for `!!binary` scalars.
419mod base64;
420/// `!include` directive — resolver types
421/// (`IncludeResolver`, `IncludeRequest`, `InputSource`,
422/// `SymlinkPolicy`, `SafeFileResolver`). Wired into
423/// `ParserConfig::include_resolver`.
424#[cfg(feature = "include")]
425#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
426pub mod include;
427
428/// Declarative `parser_config!` / `serializer_config!` builder
429/// macros. Pure expansion to the existing chained-setter
430/// builders — zero runtime overhead.
431mod macros;
432
433/// Pluggable error-message formatters: [`i18n::MessageFormatter`]
434/// trait plus [`i18n::DefaultFormatter`] (developer-facing,
435/// verbatim) and [`i18n::UserFormatter`] (user-facing,
436/// simplified language). Use
437/// [`crate::Error::render_with_formatter`] to plug in
438/// localisation tables or custom rendering.
439pub mod i18n;
440
441/// `Spanned<T>` + garde / validator → `miette::Report` bridge.
442/// Behind the `miette` Cargo feature; the actual conversion
443/// functions are gated on `miette + garde` or `miette + validator`.
444#[cfg(feature = "miette")]
445#[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
446pub mod validated_miette;
447
448pub mod borrowed;
449mod comments;
450/// Drop-in compatibility shims for upstream YAML crates. Each shim
451/// is gated behind its own feature flag so unused migration paths
452/// add zero compile cost. See [`compat::serde_yaml`] for the
453/// `serde_yaml` 0.9 surface.
454pub mod compat;
455/// Side-table CST for byte-faithful round-tripping with typed
456/// path-targeted edits.
457///
458/// See `docs/design/green-tree.md` for the architectural plan. The
459/// `Document` API depends on the parser's `SpanTree`, which lives
460/// under the `std` feature.
461#[cfg(feature = "std")]
462pub mod cst;
463mod de;
464/// Spanned-to-miette diagnostic bridge (requires `miette` feature).
465#[cfg(feature = "miette")]
466#[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
467pub mod diagnostic;
468/// Workspace-private `---` document-boundary scanner shared by
469/// `parallel::split`, `recovery::split_documents`, and
470/// `tokio_async::find_doc_boundary` — one CRLF/BOM-aware scanner,
471/// one DoS cap.
472pub(crate) mod doc_boundary;
473/// Multi-document loading and iteration.
474pub mod document;
475mod error;
476/// [`figment`] provider integration. Pulls in `figment` 0.10
477/// when the `figment` Cargo feature is enabled.
478#[cfg(feature = "figment")]
479#[cfg_attr(docsrs, doc(cfg(feature = "figment")))]
480pub mod figment;
481mod flattened;
482/// Formatting wrappers for per-value YAML output style control.
483pub mod fmt;
484/// Key interning for memory-efficient repeated-key workloads.
485pub mod interner;
486/// Parallel multi-document YAML parsing via Rayon. Gated by the
487/// `parallel` feature.
488#[cfg(feature = "parallel")]
489#[cfg_attr(docsrs, doc(cfg(feature = "parallel")))]
490pub mod parallel;
491mod parser;
492mod path;
493/// Pluggable parser policies for "Safe YAML" enforcement.
494pub mod policy;
495/// Error-recovering parser for LSP / IDE partial parsing. Gated
496/// by the `recovery` feature.
497#[cfg(feature = "recovery")]
498#[cfg_attr(docsrs, doc(cfg(feature = "recovery")))]
499pub mod recovery;
500/// Robotics and scientific numeric types (requires `robotics` feature).
501#[cfg(feature = "robotics")]
502#[cfg_attr(docsrs, doc(cfg(feature = "robotics")))]
503pub mod robotics;
504mod schema;
505/// JSON Schema codegen via [`schemars`] — derive
506/// [`schemars::JsonSchema`] for a Rust type and call
507/// [`schema_for`] / [`schema_for_yaml`] to obtain the schema as a
508/// [`crate::Value`] or as YAML text. Requires the `schema` feature.
509#[cfg(feature = "schema")]
510#[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
511mod schema_codegen;
512/// Schema *validation* — enforce a JSON Schema 2020-12 contract
513/// against a parsed [`Value`]. Pairs with [`schema_codegen`].
514/// Requires the `validate-schema` feature (which implies `schema`).
515#[cfg(feature = "validate-schema")]
516#[cfg_attr(docsrs, doc(cfg(feature = "validate-schema")))]
517mod schema_validate;
518mod ser;
519/// `sval` zero-allocation streaming serialization adapter.
520/// Gated by the `sval` feature.
521#[cfg(feature = "sval")]
522#[cfg_attr(docsrs, doc(cfg(feature = "sval")))]
523pub mod sval_adapter;
524/// Native async YAML parsing for tokio runtimes —
525/// `from_async_reader` plus a `tokio_util::codec::Decoder`.
526/// Gated by the `tokio` feature.
527#[cfg(feature = "tokio")]
528#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
529pub mod tokio_async;
530
531/// SIMD-friendly multi-byte search primitives.
532///
533/// Pure-safe Rust (no `unsafe`, no platform intrinsics, no
534/// hardware-specific deps). The vectorisation comes from
535/// `memchr`'s SSE2 / NEON dispatch for arity 1/2/3 and SWAR
536/// (SIMD-Within-A-Register) for arity 4+. The parser hot path
537/// uses these primitives unconditionally; the `simd` Cargo
538/// feature is retained as a no-op for forward compatibility.
539pub mod simd;
540pub(crate) mod span_context;
541pub(crate) mod spanned;
542mod streaming;
543pub mod tag_registry;
544/// Declarative post-deserialise validation via [`garde`] or [`validator`]
545/// (requires the corresponding feature).
546#[cfg(any(feature = "garde", feature = "validator"))]
547#[cfg_attr(docsrs, doc(cfg(any(feature = "garde", feature = "validator"))))]
548pub mod validated;
549mod value;
550pub mod with;
551
552pub use anchors::{
553    AnchorRegistry, ArcAnchor, ArcAnchorRegistry, ArcWeakAnchor, RcAnchor, RcWeakAnchor,
554};
555// Recursive anchor wrappers depend on `Rc<RefCell<…>>` /
556// `Arc<Mutex<…>>` which require `std` (RefCell+Mutex live under
557// `core::cell` / `std::sync` — the latter only available with std).
558#[cfg(feature = "std")]
559pub use anchors::{ArcRecursion, ArcRecursive, RcRecursion, RcRecursive};
560pub use comments::{Comment, CommentKind, load_comments};
561pub use de::RequireIndent;
562pub use de::{
563    Deserializer, DuplicateKeyPolicy, MergeKeyPolicy, ParserConfig, YamlVersion, from_slice,
564    from_slice_with_config, from_str, from_str_borrowing, from_str_borrowing_with_config,
565    from_str_with_config, from_value,
566};
567#[cfg(feature = "std")]
568pub use de::{from_reader, from_reader_with_config};
569#[cfg(all(feature = "std", feature = "strict-deserialise"))]
570pub use de::{from_reader_strict, from_slice_strict, from_str_strict};
571#[cfg(feature = "std")]
572pub use document::{DocumentReadIterator, read, read_with_config};
573pub use document::{load_all, load_all_as, load_all_with_config, try_load_all};
574pub use error::{BudgetBreach, CroppedRegion, Error, ErrorKind, Location, RenderOptions, Result};
575pub use flattened::Flattened;
576pub use fmt::{Commented, FlowMap, FlowSeq, FoldStr, FoldString, LitStr, LitString, SpaceAfter};
577pub use path::Path;
578pub use schema::{
579    is_yaml_failsafe_compatible, is_yaml_json_compatible, validate_yaml_core_schema,
580    validate_yaml_failsafe_schema, validate_yaml_json_schema,
581};
582#[cfg(feature = "schema")]
583#[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
584pub use schema_codegen::{JsonSchema, schema_for, schema_for_yaml};
585#[cfg(feature = "validate-schema")]
586#[cfg_attr(docsrs, doc(cfg(feature = "validate-schema")))]
587pub use schema_validate::{coerce_to_schema, validate_against_schema, validate_against_schema_str};
588pub use ser::{
589    FlowStyle, ScalarStyle, Serializer, SerializerConfig, to_fmt_writer, to_fmt_writer_with_config,
590    to_string, to_string_multi, to_string_multi_with_config, to_string_value,
591    to_string_value_with_config, to_string_with_config, to_value,
592};
593#[cfg(feature = "std")]
594pub use ser::{
595    to_string_tracking_shared, to_string_tracking_shared_with_config, to_writer_tracking_shared,
596    to_writer_tracking_shared_with_config,
597};
598#[cfg(feature = "std")]
599pub use ser::{
600    to_writer, to_writer_multi, to_writer_multi_with_config, to_writer_value,
601    to_writer_value_with_config, to_writer_with_config,
602};
603pub use spanned::Spanned;
604pub use streaming::StreamingDeserializer;
605pub use tag_registry::TagRegistry;
606#[cfg(feature = "garde")]
607#[cfg_attr(docsrs, doc(cfg(feature = "garde")))]
608pub use validated::Validated;
609#[cfg(feature = "validator")]
610#[cfg_attr(docsrs, doc(cfg(feature = "validator")))]
611pub use validated::ValidatedValidator;
612pub use value::{
613    Mapping, MappingAny, MaybeTag, Number, ParseNumberError, Sequence, Tag, TaggedValue, Value,
614    ValueIndex, check_for_tag, nobang,
615};