Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

YAML

omnist::formats::yaml::{read_yaml, write_yaml, check_yaml}. Ported from ~/dev/omnist/omnist/formats.py’s read_yaml/write_yaml/check_yaml; see omnist/src/formats/yaml.rs’s module doc for the full detail.

#![allow(unused)]
fn main() {
use indexmap::IndexMap;
use omnist::document::{Doc, Value};
use omnist::formats::yaml::{read_yaml, write_yaml};

let mut fields = IndexMap::new();
fields.insert("name".to_string(), Value::Str("Ada".to_string()));
fields.insert("age".to_string(), Value::Int(37));
let doc = Doc::of(&Value::Object(fields)).unwrap();

let text = write_yaml(&doc, true, None).unwrap();
let doc2 = read_yaml(&text).unwrap();
assert!(doc.eq_doc(&doc2));
}

Scalar-tag resolution: PyYAML’s rules, not yaml_rust2’s

yaml_rust2’s own resolver only recognizes true/false for booleans (YAML 1.2 core schema). This module ignores that and re-implements PyYAML’s YAML-1.1 implicit-resolver regexes instead, live-checked against PyYAML (this project’s Python reference): yes/no/on/off (and case variants) are also booleans; bare y/n are not and stay strings. Quoted scalars are never auto-typed, matching PyYAML (implicit resolution only applies to plain-style scalars).

Merge keys (<<)

yaml_rust2 has no built-in merge-key support; this module implements the YAML merge-key spec directly (an unquoted << key’s value must be a mapping or sequence of mappings, merged in order, explicit keys taking precedence).

Native temporal type on read, but no bare-time literal, and a looser input grammar than JSON

A bare YAML timestamp reads as a genuine Scalar::Date (no T) or Scalar::Datetime (has one) (issue #105) – never Scalar::Time: YAML’s own normalize_timestamp grammar always requires a date component, so there’s no bare-time literal to produce one from. YAML’s timestamp grammar is looser than JSON’s (space-separated date/time, single-digit month/day, a bare Z suffix, no zero-padding); this module normalizes any such spelling to the same canonical, zero-padded, T-joined ISO shape PyYAML’s own datetime.isoformat() would produce – so 2001-12-14 21:59:43.10 -5 round-trips to 2001-12-14T21:59:43.100000-05:00, not its original spelling. A timestamp-shaped string naming a calendar/clock value that doesn’t exist (2024-13-01) is a ParseError, matching PyYAML’s own construction-time failure.

On write, only a genuine Scalar::Date/Datetime writes bare; a plain Scalar::Str that merely looks like one always writes quoted, matching Python exactly (see Python divergences). A genuine Scalar::Time (e.g. from OML’s own bare-time grammar, or a schema-directed upgrade) has no native YAML spelling at all and always writes quoted.

Native NaN/Infinity – no lossy adjustment here (unlike JSON)

YAML’s float grammar has native .nan/.inf/-.inf tokens, so unlike write_json, write_yaml never substitutes null for a special float. The only adjustment check_yaml ever records is forcing double-quoted style for a string containing U+0085 (NEL), which PyYAML’s default styles would otherwise normalize away as a line break.

Legacy sexagesimal integers (H:M:S-shaped)

YAML 1.1’s implicit-int resolver also recognizes a colon-separated sexagesimal form – 12:00:00 resolves to Scalar::Int(43200), not a string. This module’s resolver folds each :-separated group as acc*60 + group (checked arithmetic; overflow reports the same out-of-range ParseError as an oversized plain integer), requires no leading zero on the first group, and constrains later groups to 0..=59 – so 01:20, 1:60, and 0:0:1 all stay plain strings (each violates one of those rules), matching PyYAML’s own grammar. Confirmed by omnist-rs issue #87, found while building the conformance harness against omnist-spec.

Mapping keys are implicitly typed too (the “Norway problem”)

Every mapping key is run through the same implicit-type resolver as values, matching PyYAML: a key like on: is rejected (it resolves to Bool(true), not a string), and so is any other non-string-resolving key shape (int-, float-, sexagesimal-shaped, null-shaped). The rejection is a DocumentError at path "$" with a Python-parity message (e.g. object key True is not a string, object key 1.0 is not a string – including keeping the .0 on whole-number floats). Ordinary string keys, and non-boolean words like bare y/n, are unaffected. Confirmed by omnist-rs issue #88, found and fixed alongside #87 above.

Integer digit cap

Same 4300-digit cap as json.rs/oml.rs/toml.rs, applied to a plain decimal integer scalar’s digit run before parsing; arbitrary-precision above that (see formats/json.md). The legacy sexagesimal fold (above) enforces the identical cap on its folded result, not any one group – issue #104: an unbounded BigInt fold with no such check would let a many-group literal build an arbitrarily large integer, a resource-exhaustion regression the fold’s old i64 overflow used to prevent as an unplanned side effect.