English

Developer tools · Syntax converters

Why TOML exists: the design goals behind Cargo.toml and pyproject.toml

· Background

toml data-formats developer-workflow

A TOML table with typed values mapped into a plain object while comments stay behind
Original ToolAcre vector illustration

TOML was created in 2013 as a reaction to both JSON's austerity and YAML's ambiguity. This post explains its stated design goals, the choices they produced, and why Rust and Python standardised on it for project configuration.

Three config formats in one repository — JSON for the editor, YAML for CI, TOML for the build, and the question of why the third exists

A repository can use JSON, YAML and TOML for different configuration surfaces. ToolAcre cannot explain every project’s choice, but conversion makes the structural differences concrete: TOML begins as a root table, uses headers and dotted paths for nesting, and carries temporal values unavailable in JSON.

Load the example rather than arguing from appearance. Nested tables become objects, double-bracket tables become arrays, and comments disappear when values enter JSON. Those observed boundaries are more actionable than a generic claim that one syntax is inherently better.

The design goals — minimal, obvious semantics, easy to read, and a format that maps unambiguously to a hash table

The shipped parser exposes obvious table semantics: headers are paths, assignments belong to the active table, and scalar tokens have defined TOML types. Strings do not become typed merely because their contents look like dates; actual temporal syntax produces date objects that ToolAcre normalizes deliberately.

The repository does not source the format’s creators, dates or stated philosophy, so this article avoids presenting remembered history as fact. It reports behaviour tested in smol-toml and the converter’s own normalization layer.

Observable design properties in the shipped parser, without unsourced origin claims

TOML has no null, and its document root cannot be an array or scalar. It does not provide YAML-style anchors or aliases in this mapping. Comments exist in authored TOML but are not retained by the value parser and therefore cannot survive conversion through JSON or YAML.

Unquoted bare values follow TOML grammar rather than YAML’s schema selection. The parser either accepts a typed value or reports invalid TOML with position information. ToolAcre does not add an implicit string mode for malformed assignments.

What the supported value model excludes or handles differently

The model includes strings, signed integers, floats, booleans, four temporal kinds, arrays and tables. Arrays of tables express repeated object records. Large signed integers beyond 2^53 become decimal strings on conversion so JavaScript does not round them silently.

Temporal values become the source-oriented text for offset date-time, local date-time, local date or local time. The warning preserves the kind in prose, but JSON receives only a string. Converting back therefore quotes it and loses the native TOML type.

Adoption — Cargo from Rust's earliest days, PEP 518 choosing pyproject.toml, and the 1.0.0 specification in 2021

Cargo and pyproject adoption are historical and ecosystem claims requiring sources not present in the converter repository. They are intentionally omitted here. A path or filename is not evidence for a chronology, specification release or standards decision.

The operational question is whether the target tool reads TOML and which tables it expects. Check that tool’s current documentation. Syntax converters knows syntax and value mapping, not package-manager configuration contracts.

Ecosystem adoption history is omitted without repository sources

Deep nesting can be harder to scan because table context persists across lines, while large arrays of tables spread one logical list across repeated headers. JSON makes the complete hierarchy explicit but adds braces and quotes. Neither representation removes complexity from the underlying configuration.

The parser caps nesting at 100 and source length at two million characters. Those are refusal boundaries, not statements about ideal configuration size or universal TOML limits.

What this does not cover — TOML parser choice in each language, and the read-only nature of some standard-library implementations

Parser choice in each language is outside scope, as are standard-library write capabilities. ToolAcre uses smol-toml dynamically and wraps its errors. Another implementation can format valid output differently or expose another API while representing the same data.

Use cross-tool fixtures for temporal values, large integers, arrays and dotted keys when interoperability matters. A file accepted here is not automatically accepted by every TOML consumer.

Takeaway: TOML is opinionated about being a config format — and how the Syntax converters panel lets you see any JSON or YAML file in that shape

TOML is opinionated in observable ways: table root, explicit typed values, native temporal kinds and no null. Conversion exposes those choices and their incompatibilities with JSON-shaped targets without needing an origin myth.

Use the panel to inspect a tree and identify warnings. Then return to the destination’s schema and author the table layout humans will maintain. The converter supplies evidence about values, not a verdict on format preference.

The same discipline applies when TOML is only an intermediate view. Preserve the source, compare normalized values, and note every temporal or wide-integer conversion before judging readability. A compact table layout can still hide a changed type, while a verbose array of tables can be semantically exact. Format choice should follow the configuration contract and maintenance workflow, not the visual neatness of one generated sample.