Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "saneyaml"
version = "0.2.0"
version = "0.3.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the fuzz lockfile for the release version

Bumping the root package to 0.3.0 leaves the non-workspace fuzz crate's lockfile stale: fuzz/Cargo.lock still records the path dependency saneyaml as 0.2.0, while CI runs cargo clippy --locked --manifest-path fuzz/Cargo.toml --all-targets -- -D warnings in .github/workflows/ci.yml. In that locked fuzz job, Cargo will need to update the path dependency entry to 0.3.0 and will fail because --locked forbids lockfile changes, so this release commit breaks the fuzz CI until fuzz/Cargo.lock is regenerated.

Useful? React with 👍 / 👎.

edition = "2024"
rust-version = "1.88"
description = "Pure-Rust YAML parser, emitter, and Serde compatibility layer for developer configuration files."
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Pure Rust, `#![forbid(unsafe_code)]`.

```toml
[dependencies]
saneyaml = "0.2.0"
saneyaml = "0.3.0"
```

Then use it:
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ 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
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:
Expand Down
6 changes: 3 additions & 3 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,7 +33,7 @@ the old call-site spelling:

```toml
[dependencies]
saneyaml = "0.2.0"
saneyaml = "0.3.0"
```

```rust
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading