From e1048f6805c030dd0dd92ab902eb572142bffeba Mon Sep 17 00:00:00 2001 From: jskoiz <20649937+jskoiz@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:16:50 -1000 Subject: [PATCH] Prepare saneyaml 0.3.0 release --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 4 ++-- docs/ARCHITECTURE.md | 4 ++-- docs/MIGRATION.md | 6 ++--- fuzz/Cargo.lock | 2 +- 7 files changed, 62 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8ae73e..b8e91ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ effect are omitted. ## Unreleased +## 0.3.0 + ### Added - Added `EmitOptions::with_yaml_1_1_safe_strings`, an opt-in emitter setting that @@ -26,6 +28,56 @@ effect are omitted. `serde_yaml::with::singleton_map` writer output without annotating every enum field. +### Changed + +- YAML 1.1 two-part sexagesimal scalars now resolve with base-60 positional + weighting (`1:20` is `80`, `1:20.5` is `80.5`), matching the YAML 1.1 spec and + PyYAML. This intentionally diverges from the pinned Psych/libyaml hour:minute + construction that produced `4800`; three-part sexagesimals are unchanged. Code + that relied on the previous two-part values must adjust. +- Byte serialization is now rejected consistently across `to_value`, the value + serializers, and the string/writer/streaming serializers, instead of `to_value` + silently lowering bytes to a `u8` sequence. Read-side `!!binary` decoding into + byte targets is unchanged. +- Explicit `!!str`-tagged numeric scalars (for example `!!str 7`) now stay strings + for every integer and float Serde target instead of being coerced to integers + only for `i128`/`u128`. `from_str` and `from_value` now agree on these inputs. +- Caller-built owned and borrowed `Node` / `Value` Serde deserializers now expand + `<<` merge keys by default, matching `from_value`; parser-produced reads keep + their schema-driven recovery behavior. +- Floats are now emitted in shortest round-tripping form (for example `1e308` + rather than a 300-plus digit expansion), matching `Number`'s display output. +- `Span` / `Location` columns are documented as one-based UTF-8 byte columns. + +### Fixed + +- Combined UTF-16 surrogate-pair escapes (for example `"😀"`) in + double-quoted scalars into the intended astral code point; lone or mismatched + surrogate escapes are rejected. +- `!!omap` deserialized into a map target now rejects duplicate keys, matching the + crate's strict duplicate-key policy, instead of silently keeping the last value. + Pair-sequence targets still preserve ordered duplicates. +- `Number::partial_cmp` now returns `None` whenever either operand is `NaN`, + including integer/float comparisons, and `NaN` is sign-normalized when + deserialized into `Value`. +- Emitting a verbatim tag whose suffix contains `>` now percent-escapes it, so the + emitted tag round-trips through the parser instead of producing invalid YAML. +- Merge-key expansion depth is bounded consistently across owned and borrowed + `Node` / `Value` deserializers, preventing a stack overflow on deeply nested + caller-built merge chains. +- YAML 1.1 timestamps with more than nine fractional-second digits are truncated + to nanosecond precision instead of rejected. +- Resolved a broad batch of parser, emitter, lossless-editing, numeric-parsing, + and diagnostic correctness issues (#19–#40), including a UTF-8 block-scalar + boundary panic. + +### Security + +- Fixed three quadratic-time parsing denial-of-service vectors that were reachable + under the default limits: unterminated multi-line flow collections, unterminated + multi-line quoted scalars, and long single-line runs of quote characters. Each + now scans in linear time. + ## 0.2.0 ### Added diff --git a/Cargo.lock b/Cargo.lock index e059f0e..e35fe4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,7 +581,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "saneyaml" -version = "0.2.0" +version = "0.3.0" dependencies = [ "dhat", "proptest", diff --git a/Cargo.toml b/Cargo.toml index 0ddb61d..10558dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "saneyaml" -version = "0.2.0" +version = "0.3.0" edition = "2024" rust-version = "1.88" description = "Pure-Rust YAML parser, emitter, and Serde compatibility layer for developer configuration files." diff --git a/README.md b/README.md index 0432ade..9e2e44a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Pure Rust, `#![forbid(unsafe_code)]`. ```toml [dependencies] -saneyaml = "0.2.0" +saneyaml = "0.3.0" ``` Then use it: @@ -84,7 +84,7 @@ hand. saneyaml is serde-first **and** YAML 1.2-correct. ## Status -Pre-1.0 (`0.2.0`), MSRV Rust 1.88, and actively maintained. The public API is a +Pre-1.0 (`0.3.0`), MSRV Rust 1.88, and actively maintained. The public API is a preview surface but is treated as SemVer-visible: breaking changes and MSRV bumps are explicit, documented release decisions. The road to 1.0 is about locking the surface down, not expanding it — stability is the goal. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 5050f4e..44cf10b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -7,7 +7,7 @@ downstream code imports this crate as `saneyaml::...`: ```toml [dependencies] -saneyaml = "0.2.0" +saneyaml = "0.3.0" ``` For drop-in `serde_yaml` migration, Cargo dependency renaming keeps existing @@ -15,7 +15,7 @@ source imports intact: ```toml [dependencies] -serde_yaml = { package = "saneyaml", version = "0.2.0" } +serde_yaml = { package = "saneyaml", version = "0.3.0" } ``` or a local source alias: diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index bc47320..b1d662f 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -18,7 +18,7 @@ the dependency resolves to this crate: ```toml [dependencies] -serde_yaml = { package = "saneyaml", version = "0.2.0" } +serde_yaml = { package = "saneyaml", version = "0.3.0" } ``` With this shape, the covered public surface stays spelled @@ -33,7 +33,7 @@ the old call-site spelling: ```toml [dependencies] -saneyaml = "0.2.0" +saneyaml = "0.3.0" ``` ```rust @@ -566,7 +566,7 @@ testing each adopter's own YAML corpus. | Alias graph identity | Semantic `Node`/`Value` trees intentionally clone acyclic aliases and reject recursive alias expansion. Graph-sensitive callers should use `LosslessStream`; its anchor definitions and alias targets are checked against reference parser anchor events for redefinition, recursive, document-reset, merge, YAML 1.1 merge/comment graph fixtures, post-edit source output, manifest-owned selected YAML-suite anchor/alias cases, and manifest-owned real-world Docker Compose anchor cases including an adapted official Compose Specification fragment. `LosslessStream::effective_mapping_entries` exposes merge-derived entries with alias/anchor provenance for callers that need effective config inspection without losing graph identity. | | Lossless formatting | `LosslessStream` preserves source, comments, trivia, directives, anchors, aliases, tags, and scalar spelling for replay/inspection, including a merge-effective mapping view that leaves the original source untouched. `LosslessEdit` can replace retained node or raw source spans, update scalar-keyed block/flow mapping values, insert or delete block/flow mapping entries, update block/flow sequence items, insert or delete block/flow sequence items, insert source, delete source spans, and validate the final YAML while preserving untouched bytes. `ConfigEditor` reparses between high-level path operations so chained config edits use current-source spans, and `ConfigPath::json_pointer` covers keys containing `/` or `~` without relying on dotted splitting. Manifest-owned real-world replay now gates GitHub Actions comments, flow-style lists, and expression strings, Ansible tagged scalars, plus Kubernetes streams and block scalar fixtures. | | Parser acceptance differences | Some YAML 1.2 inputs rejected by libyaml are accepted, and some malformed libyaml-tolerated inputs are rejected. Divergence records now carry per-case migration impact. | -| Package status | `Cargo.toml` declares `saneyaml` 0.2.0 under the MIT license. | +| Package status | `Cargo.toml` declares `saneyaml` 0.3.0 under the MIT license. | ## Known Follow-Up diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 77be0fc..cecef8a 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -130,7 +130,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "saneyaml" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ryu", "serde",