1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2026 Noyalib. All rights reserved.
//! # 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 a `Value`/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 via `doc.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). See `examples/lossless_edit.rs`.
//!
//! ## Quick Start
//!
//! ```rust
//! 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
//!
//! ```rust,no_run
//! # use noyalib::Value;
//! # let yaml = "key: value";
//! # let bytes = b"key: value";
//! # let file = std::io::Cursor::new(yaml);
//! # let value = Value::Null;
//! // 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
//!
//! ```rust,no_run
//! # use noyalib::Value;
//! # let value = Value::Null;
//! // 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` /
//! `Deserialize` type.
//! - **Ordered mappings** — [`IndexMap`](indexmap::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::BorrowedValue`] borrows strings from input.
//! - **Path queries** — `value.query("items[*].name")` with wildcards.
//! - **`no_std`** — works with `alloc` only (`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 a
//! `pub` module (e.g. helpers in [`borrowed`], [`cst`],
//! [`policy`]) are also public; everything in a `pub(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 documented
//! `new` / `default` / `strict` constructors 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 [`Value`] enum'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 `'static` half is the
//! contract every real-world `DeserializeOwned` type already
//! satisfies (the HRTB itself disallows borrowed lifetimes); it
//! is documented explicitly because a small number of
//! externally-defined trait signatures (e.g. `figment`'s
//! `Format::from_str`) drop the `'static` from their own bound
//! — for those, noyalib provides feature-gated internal entry
//! points that bypass the [`Value`]-tag-preserving fast path.
//! - **What may change without a major bump:** non-default
//! `ParserConfig` semantics under explicit opt-in (e.g. a future
//! `legacy_*` 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-checks` runs in CI on
//! every PR.
//!
//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
//!
//! ## MSRV policy
//!
//! - **Core library (`noyalib`)** — Rust **1.75.0** stable. CI's
//! `msrv-1-75-core` job builds the `default-features = false`
//! and the standard `default` set on `rustc 1.75.0` for every
//! PR. The MSRV is treated as part of the public contract: a
//! bump within `0.0.x` is 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 — currently
//! `1.80`–`1.86` depending on the feature. The CI matrix runs
//! each one against the dep's declared `rust-version`.
//! - **Companion crates** ([`noya-cli`], [`noyalib-lsp`]) carry
//! their own higher MSRVs because their dep tree includes
//! edition-2024 transitives — `1.85.0` for both at time of
//! writing.
//! - **`nightly-simd`** is the only feature that requires nightly
//! rustc (`#![feature(portable_simd)]`); a `build.rs` cfg-detect
//! probe means stable builds with `--all-features` still
//! compile by treating `nightly-simd` as a no-op.
//!
//! [`noya-cli`]: https://crates.io/crates/noya-cli
//! [`noyalib-lsp`]: https://crates.io/crates/noyalib-lsp
//!
//! ## 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`](crate::figment) 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>`],
//! [`Error`] are `Send + Sync`. Cloning a `Value` is `O(n)` in
//! the value graph; share ownership via
//! [`Arc`](std::sync::Arc)`<Value>` when that cost matters.
//! - [`policy::Policy`] requires `Send + Sync` so policies can be
//! shared by reference across threads. Stateful policies should
//! hold their state behind interior mutability
//! ([`std::sync::Mutex`] or equivalent).
//! - [`Spanned<T>`] deserialisation uses a thread-local span
//! context (`std` feature). The TLS guard installs on entry to
//! [`from_str_with_config`] and 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 [`parallel`] module pre-scans document
//! boundaries on the calling thread, then dispatches each
//! document to the global Rayon pool — `T: Send` is required.
//! - [`anchors::ArcAnchorRegistry`] / [`anchors::ArcAnchor`] use
//! `Arc` + `Weak` and are explicitly multi-thread-safe; the
//! `Rc`-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 `libyaml` C-FFI CVE class.
//! - **No arbitrary object instantiation from tags** — custom
//! tags surface as [`Value::Tagged`] data; opt-in dispatch via
//! [`TagRegistry`]. There is no path from a parsed YAML
//! document to running attacker-chosen code.
//! - **Resource budgets** — seven configurable limits in
//! [`ParserConfig`] cap depth, document size, alias
//! expansions, mapping keys, sequence length, duplicate-key
//! policy, and boolean strictness. [`ParserConfig::strict`]
//! tightens every budget for untrusted input. Alias-byte
//! accumulation uses `saturating_add` so a crafted overflow
//! still trips the cap.
//! - **Pluggable policies** — [`policy::DenyAnchors`],
//! [`policy::DenyTags`], [`policy::MaxScalarLength`] for
//! organisational "Safe YAML" enforcement. Custom policies
//! implement [`policy::Policy`].
//! - **Supply chain** — `cargo audit`, `cargo deny`, `cargo vet`
//! gate every PR. Releases ship SLSA L3 provenance and
//! sigstore signatures (verification cookbook in
//! [`pkg/VERIFY.md`](https://github.com/sebastienrousseau/noyalib/blob/main/pkg/VERIFY.md)).
//! No archived or unmaintained crate appears in the dependency
//! graph.
//!
//! Disclosure policy: see
//! [`SECURITY.md`](https://github.com/sebastienrousseau/noyalib/blob/main/SECURITY.md).
//!
//! ## Performance and complexity
//!
//! - **Parser** — single-pass, `O(n)` in input bytes for the
//! scanner; loader is `O(n)` events. `IndexMap` insert is
//! amortised `O(1)`; `FxHasher` keeps key hashing cheap on
//! short keys.
//! - **Streaming deserialise** — bypasses the dynamic `Value`
//! AST when the caller asks for a typed `T`, eliminating
//! intermediate allocations. ~30% faster than the
//! AST-via-`Value` path on real workloads.
//! - **Zero-copy scanner** — string scalars come out as
//! `Cow::Borrowed` when no escape sequence forces an
//! allocation. [`borrowed::BorrowedValue`] surfaces this all
//! the way to the caller.
//! - **SIMD primitives** — [`simd::find_any_of`] dispatches to
//! `memchr` SSE2/NEON for arity 1/2/3 and SWAR for arity 4+.
//! With `nightly-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 `u64`
//! cycle. ~2× faster than `<i64 as FromStr>::from_str` on big
//! numbers.
//! - **Serialiser** — branchless integer (`itoa`) and float
//! (`ryu`) formatting in the hot path; falls back to
//! `core::fmt` under `--no-default-features`.
//! - **Parallel multi-document** — [`parallel::parse`] scales
//! near-linearly with cores on `---`-separated streams; the
//! pre-scan is `O(input.len())` on the calling thread.
//! - **`Value::clone`** is `O(n)` over the value graph; share
//! via `Arc<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 by `alloc`.
//! The `std`-only items ([`from_reader`], [`to_writer`],
//! [`Spanned<T>`] deserialisation via TLS, the [`cst`]
//! module) are gone; the rest of the surface compiles. CI
//! enforces `cargo check --no-default-features` on every PR.
//! - **WASM**: `wasm32-unknown-unknown` via the `noyalib-wasm`
//! companion crate. 338 KB release binary (LTO). Browser
//! demo in `crates/noyalib/examples/wasm/`.
//! - **Big-endian**: validated under Miri's
//! `mips64-unknown-linux-gnuabi64` simulation in the weekly
//! `miri-bigendian` job.
//!
//! ## Error model
//!
//! Every fallible function returns [`Result<T>`](crate::Result)
//! 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.
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2026 Noyalib. All rights reserved.
// Opt-in coverage annotations. `noyalib_coverage` is set by the
// build script when `NOYALIB_COVERAGE=1` is exported (typically by
// the CI coverage job running on nightly). When active, items
// annotated with `#[cfg_attr(noyalib_coverage, coverage(off))]`
// are excluded from coverage instrumentation. Stable builds and
// regular nightly builds never see the `coverage_attribute`
// feature flag, so the annotations are no-ops there.
// README doctest coverage: every ```rust block in
// crates/noyalib/README.md is exercised by `cargo test --doc`.
// The hidden module exists only when doctesting so the README
// content does not leak into the docs.rs page (the lib's own
// crate-level docs above are the canonical surface there).
extern crate alloc;
/// Internal prelude for no_std compatibility.
/// Provides String, Vec, Box, etc. from alloc when std is absent.
pub
/// Internal prelude for std compatibility.
pub
/// Zero-copy YAML values that borrow from the input.
/// [`ariadne`] adapter — render `crate::Error` as an
/// `ariadne::Report` with the offending byte range labelled.
/// Behind the `ariadne` Cargo feature.
/// Internal RFC 4648 base64 codec for `!!binary` scalars.
/// `!include` directive — resolver types
/// (`IncludeResolver`, `IncludeRequest`, `InputSource`,
/// `SymlinkPolicy`, `SafeFileResolver`). Wired into
/// `ParserConfig::include_resolver`.
/// `Spanned<T>` + garde / validator → `miette::Report` bridge.
/// Behind the `miette` Cargo feature; the actual conversion
/// functions are gated on `miette + garde` or `miette + validator`.
/// 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_yaml`] for the
/// `serde_yaml` 0.9 surface.
/// Side-table CST for byte-faithful round-tripping with typed
/// path-targeted edits.
///
/// See `docs/design/green-tree.md` for the architectural plan. The
/// `Document` API depends on the parser's `SpanTree`, which lives
/// under the `std` feature.
/// Spanned-to-miette diagnostic bridge (requires `miette` feature).
/// Multi-document loading and iteration.
/// [`figment`] provider integration. Pulls in `figment` 0.10
/// when the `figment` Cargo feature is enabled.
/// Formatting wrappers for per-value YAML output style control.
/// Key interning for memory-efficient repeated-key workloads.
/// Parallel multi-document YAML parsing via Rayon. Gated by the
/// `parallel` feature.
/// Pluggable parser policies for "Safe YAML" enforcement.
/// Robotics and scientific numeric types (requires `robotics` feature).
/// JSON Schema codegen via [`schemars`] — derive
/// [`schemars::JsonSchema`] for a Rust type and call
/// [`schema_for`] / [`schema_for_yaml`] to obtain the schema as a
/// [`crate::Value`] or as YAML text. Requires the `schema` feature.
/// Schema *validation* — enforce a JSON Schema 2020-12 contract
/// against a parsed [`Value`]. Pairs with [`schema_codegen`].
/// Requires the `validate-schema` feature (which implies `schema`).
/// SIMD-friendly multi-byte search primitives.
///
/// Pure-safe Rust (no `unsafe`, no platform intrinsics, no
/// hardware-specific deps). The vectorisation comes from
/// `memchr`'s SSE2 / NEON dispatch for arity 1/2/3 and SWAR
/// (SIMD-Within-A-Register) for arity 4+. The parser hot path
/// uses these primitives unconditionally; the `simd` Cargo
/// feature is retained as a no-op for forward compatibility.
pub
pub
/// Declarative post-deserialise validation via [`garde`] or [`validator`]
/// (requires the corresponding feature).
pub use ;
// Recursive anchor wrappers depend on `Rc<RefCell<…>>` /
// `Arc<Mutex<…>>` which require `std` (RefCell+Mutex live under
// `core::cell` / `std::sync` — the latter only available with std).
pub use ;
pub use ;
pub use RequireIndent;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use Flattened;
pub use ;
pub use Path;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use Spanned;
pub use StreamingDeserializer;
pub use TagRegistry;
pub use Validated;
pub use ValidatedValidator;
pub use ;