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

TOML

omnist::formats::toml::{read_toml, write_toml, check_toml}. Ported from ~/dev/omnist/omnist/formats.py’s read_toml/write_toml/check_toml; see omnist/src/formats/toml.rs’s module doc for the full detail.

#![allow(unused)]
fn main() {
use indexmap::IndexMap;
use omnist::document::{Doc, Value};
use omnist::formats::toml::{read_toml, write_toml};

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_toml(&doc, true, None).unwrap();
let doc2 = read_toml(&text).unwrap();
assert!(doc.eq_doc(&doc2));
}

No null – the one lossy TOML adjustment

TOML has no null at all. Writing a node that contains a null fails the write unconditionally with write.unsupported-value (spec sections 8.3.8 and 8.3.9), whatever strict says: dropping the field, as this port once did and recorded as a null.omitted warning, erased the edge’s existence with no trace on read-back (omnist-rs#160 retired that behaviour). The XML writer refuses a null the same way (spec C-10); see XML.

Integers are arbitrary precision; the digit limit counts decimal digits

A TOML integer is arbitrary precision here, in every radix (spec D-2). The limit is Limits::max_int_digits (default 4300; read_toml_with changes it), and it counts the decimal digits of the value (D-28), whatever the spelling: 0xFFF is 4095, four digits; underscores are separators, not digits. A value over the limit is a DocumentError with code document.limit.int-digits at the integer’s Document path, never a syntax error. A 3,600-digit hex literal is about 4,335 decimal digits, so it is refused at the default; a 4,301-digit binary literal is about 1,295, so it reads.

toml_edit holds an integer in 64 bits and refuses a longer one with no value to read. This reader works around that: it takes the byte range from the refusal, reads the literal itself (any radix, optional sign, underscores), swaps in a one-character stand-in of the same width so every other offset keeps its place, parses again, and puts the real value back by offset when it builds the Document. A decimal literal already longer than the limit in its own spelling is not converted at all; a placeholder stands in for it, so it is refused without paying the superlinear conversion. A syntax error elsewhere in the text still wins over a limit. The literals are found in one scan, so the cost stays linear however many there are.

Python’s tomllib exempts hex, octal and binary literals from CPython’s 4,300-digit string-conversion guard; this port does not, because D-28 counts the value’s decimal digits for every radix.

A float whose magnitude overflows (1e999, -1e999) reads as positive or negative infinity (D-29), by the same route.

Writing an oversized integer to TOML needs no workaround: the writer renders integers as plain digit text.

Nesting past about 80 levels

toml_edit has a recursion limit of its own, about 80 levels of table header, inline table, array or dotted key. TOML nested between about 81 levels and the configured maximum depth (200 by default) therefore fails with document.limit.depth at $ even though the configured limit has not been reached; the message names toml_edit as the cause. This is a limit of the parser library, not of the format or of the Document model, and raising max_depth does not lift it.

Native temporal types, at most six fractional digits

TOML has four first-class temporal literal forms (local date, local time, local datetime, offset datetime), stricter-shaped than YAML’s – toml_edit itself fully validates calendar/clock fields at parse time (2024-02-30, 25:00:00, and a +25:00 offset are all parse errors, not accepted-then-rejected-later). A fractional second holds at most six digits (the OML grammar’s 1*6DIGIT): zeros beyond the sixth are insignificant and dropped, but a non-zero seventh or later digit fails the read (00:32:00.9999999 is a parse.codec-syntax error) rather than being truncated as tomllib does (fail, don’t invent; through 0.11.0-alpha it was silently truncated). A numeric UTC offset is preserved exactly across read+write (no offset-erasure bug).

On read, toml_value_to_value constructs the real Value::Date/Time/ Datetime variant directly from toml_edit::Datetime’s own already- validated date/time fields (issue #105) – previously this discarded into a plain Value::Str. On write, only a genuine Date/Time/ Datetime variant is written as a native, unquoted TOML literal; a plain Scalar::Str that merely looks like a date always writes as a quoted string, matching Python’s write_toml exactly (a plain Python string that looks like a date, e.g. tomli_w.dumps({'a': '1979-05-27'}), also writes quoted). This resolves the shape-guessing divergence issue #99 first introduced for OML and issue #105 now closes here too – see Python divergences. A Time value carrying a UTC offset has no native TOML spelling at all (only datetime can carry an offset) and always writes double-quoted, with format.temporal-stringified reported at its path (E-37). Every writer writes a time’s seconds (12:00 is written 12:00:00) and keeps the offset and every fractional digit; a datetime uses the T separator.