Files
interpres/CHANGELOG.md
T

7.2 KiB

Changelog

All notable changes to interpres are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[development]

Added

  • TOML 1.1 support, on by default: date-times and times without seconds (07:32, 1979-05-27T07:32, normalised to full seconds on output), the \e and \xHH escape sequences, and multi-line inline tables with comments and trailing commas. The compliance suite runs in TOML 1.1 mode: 214 valid and 467 invalid cases, zero failures. Every TOML 1.0 document parses exactly as before.
  • interpres-decode -validate [file ...]: a validate mode beside the toml-test adapter. It parses each named file, or stdin when none are named, prints one line per invalid document to stderr, and exits 0 when all are valid, 1 when one is not, and 2 on a usage or read failure. Install it with go install .../cmd/interpres-decode@latest; releases still ship no binaries.
  • DecodeError and EncodeError: decode and encode failures are wrapped in typed errors carrying the key path, read with errors.AsType instead of parsing the message text. The rendered messages keep their shape; the only visible change is that an encode failure on a top-level field no longer gains a meaningless leading dot in its path.
  • omitzero and omitempty tag options on encode: toml:"name,omitzero" skips a field whose value is the zero value of its type (a type with an IsZero() bool method decides through the method), and toml:"name,omitempty" skips a nil or empty slice, array, or map. The decoder ignores both options.

Changed

  • The compliance suite is toml-test v2.2.0, run in TOML 1.0 mode. The new corpus holds 205 valid and 474 invalid cases (v1.6.0 had 185 and 371), and it caught the two documents the parser still accepted, fixed below.

Fixed

  • An array-of-tables header whose path runs through an inline table (a = {b = {}} followed by [[a.b.c]]) is rejected. The frozen-inline-table check covered [table] headers and dotted keys but not the intermediate steps of an array-of-tables header, so such a document silently extended the inline table.
  • A new element of an array of tables starts a fresh scope for dotted-key paths and nested arrays of tables: [[a]], b.c = 1, [[a]], [a.b] parses, as the TOML examples in the spec shape it. The records of the previous element falsely rejected the same paths in the next one.
  • UseLiteralMultiline falls back to the escaped basic string when the value cannot be carried verbatim by the literal form: a run of three single quotes, a control character, or a lone carriage return. Such values previously produced output that did not re-parse.
  • Marshal returns an error for a table header key or an inline-table key that is not valid UTF-8, the way scalar keys already did, instead of silently emitting corrupt TOML (a header that lost its key, an inline table with a missing key).
  • A float with an exponent marker but no digits (1e, 0.0E) is rejected; the exponent requires at least one digit.
  • A date-time offset outside 00:00 through 23:59 is rejected; such offsets were accepted and silently rolled over (+00:60 decoded as +01:00).
  • Untagged embedded fields now decode symmetrically with encode: an embedded struct receives its keys inline (a nil embedded pointer struct is allocated), an embedded map catches the keys no field claims, and a name clash resolves in favour of the shallower field. A struct with an untagged embedded field previously decoded with all inline keys dropped and did not round-trip.
  • Marshal re-emits arrays that mix tables with scalars: the table elements render as inline tables inside the value array. A tree that Parse accepts from such a document previously failed with cannot encode map[string]interface {}.

[1.0.0] - 2026-08-20

First stable release: a dependency-free TOML 1.0 parser and encoder for Go that uses only the standard library and passes the entire toml-test suite (185 valid and 371 invalid cases, 0 failures).

Added

Parsing

  • Parse(data []byte) (map[string]any, error): decode a TOML document into an untyped tree.
  • Full TOML 1.0 syntax: comments; bare, quoted, and dotted keys; tables ([a.b]) and arrays of tables ([[a]]); basic and literal strings, including multiline (""" / ''') with escape sequences and line-ending backslash trimming; integers in decimal, hex (0x), octal (0o), and binary (0b) with _ separators; floats with exponents and inf / nan; booleans; offset/local date-times, dates, and times; arrays; and inline tables.
  • Distinct date-time types: offset date-times decode to time.Time, while LocalDateTime, LocalDate, and LocalTime represent the local variants, each with a String() method returning the TOML-canonical rendering.
  • Strict, spec-conformant validation that rejects invalid documents: leading zeros, misplaced underscores, malformed floats and radix literals, control characters in strings and comments, bare carriage returns, non-UTF-8 input, out-of-range Unicode escapes, multiline strings used as keys, single-digit date-time components, duplicate/overwriting inline-table keys, and the full family of table redefinitions (header vs. header, header vs. dotted key, array of tables vs. table, and dotted-key appends to defined tables).
  • SyntaxError carrying the 1-based line of a malformed document.

Decoding

  • Unmarshal(data []byte, v any): parse and map onto a struct or map[string]any via reflection, with overflow-checked numeric conversion, nested structs, slices, and map[string]T.
  • toml:"name" field tags, case-insensitive name fallback, and toml:"-" to skip a field.
  • Decoder with DisallowUnknownFields for strict decoding that rejects keys without a destination field, at every struct depth.
  • Unmarshaler interface (UnmarshalTOML(data any) error) for types that take full control of their decode.

Encoding

  • Marshal(v any) ([]byte, error): encode a struct or map[string]V value to a TOML 1.0 document that re-parses to an equivalent value tree.
  • Encoder with chainable policy options: GroupByKind (group-by-kind layout versus declaration order), OmitEmptyArrays, and UseLiteralMultiline.
  • Marshaler interface (MarshalTOML() (any, error)) for types that need a custom TOML shape; the returned value is encoded normally.

Cancellation

  • ParseContext, UnmarshalContext, MarshalContext, (*Decoder).DecodeContext and (*Encoder).MarshalContext honour a context.Context, checked up front and every 64 statements or fields.

Project

  • Standard library only: zero third-party modules.
  • cmd/interpres-decode, a toml-test harness adapter (TOML on stdin, tagged JSON on stdout).
  • A runnable example, a table-driven Go test suite, and a canonical just recipe set whose gates recipe is the definition of done.
  • Hand-written CI pipelines for the test, race and release gates.
  • SECURITY.md for private vulnerability reports.