TOON’s TypeScript implementation introduced me to the format and prompted me to develop an implementation for Rust. I built toon-rust directly from the specification, with an architecture that separates format processing from the interfaces applications use to access it.
TOON represents structured data in a compact, human-readable form for use with language models. The implementation needed to preserve the format’s semantics while providing the typed APIs, error handling, and tooling expected of a Rust library.
Make the format rules explicit
In the initial implementation, I separated encoding, decoding, shared types, options, and errors. The decoder had distinct scanning, parsing, and validation responsibilities; the encoder had its own primitives and writer.
That separation gives each kind of change a place to live. A change to how input is recognized can be examined separately from a change to how output is written. Validation makes the format’s constraints visible in the code, and structured errors give callers something more useful than an unexplained failure.
The same change included tests for arrays, delimiters, numeric values, objects, Unicode, and round trips. Round-trip tests check whether supported values survive encoding and decoding. They are one part of the evidence for correctness, alongside tests that exercise the format’s rules directly.
Let callers keep their own types
An important API change was moving beyond an interface centered on serde_json::Value.
I made encoding and decoding generic over Serde’s traits. Callers could pass their own serializable types and decode into their own deserializable types, while existing JSON-value usage continued to work.
This removed a manual conversion step from application code. Callers could retain their domain types while the library handled conversion at its public boundary.
Carry the core into tools
The work also included an interactive terminal interface and REPL, with input and output views, conversion settings, and round-trip inspection. That exposed the library’s behavior in a form people could explore without first writing an application around it.
The TOON v3 migration was a separate, explicitly breaking change. A format implementation needs to track the specification it implements; compatibility needs a named version and corresponding tests.
What shipped
These changes were merged into toon-rust: the encoding and decoding foundation, structured validation and errors, a generic Serde-facing API, interactive tools, and the v3 specification update. Together, they connect format implementation with developer-facing interfaces.
The architectural thread is the boundary between those responsibilities. The format decides what the data means. The parser enforces those rules. The API determines how much work callers must do to use them.
For a closer look at the parsing side, see Invariants in the Wild.