petrbalvin 0ba145ba0c
Test / test (push) Canceled after 39s
feat: add the required tag option and UnmarshalerContext
Assisted-by: GLM 5.3 Flash
2026-09-22 00:04:16 +02:00
2026-08-19 08:12:00 +02:00
2026-09-19 00:14:39 +02:00
2026-08-19 08:12:00 +02:00

interpres

A TOML 1.1 parser and encoder for Go, written with the standard library alone. interpres (Latin for interpreter) gives zero-dependency programs an encoding/json-style API for reading and writing TOML, and passes the entire official toml-test suite: 214 valid, 467 invalid and 214 encoder cases, zero failures.

Features

  • Full TOML 1.1: bare, quoted and dotted keys; tables and arrays of tables; basic and literal strings including multiline, with the 1.1 \e and \xHH escapes; integers in the four radixes with _ separators; floats with exponents, inf and nan; booleans; the four date-time kinds, seconds optional as of 1.1; arrays and inline tables, multi-line as of 1.1.
  • Decoding and encoding: Parse for an untyped tree, Unmarshal and Marshal for structs and maps, mirroring encoding/json.
  • Strict decoding: NewDecoder().DisallowUnknownFields() rejects keys that match no destination field, at every struct depth.
  • Custom types: Marshaler and Unmarshaler let a type control its own TOML representation in both directions, and encoding.TextMarshaler and TextUnmarshaler are honoured by default, so net.IP, time.Duration and user types with text methods need no configuration.
  • Cancellation: every entry point has a *Context sibling that honours a context.Context.
  • Ordered documents: Parse gives a *Document that keeps the key order, tells an inline table from a header one, and carries the comments; ParseMap gives the plain map[string]any tree.
  • Configurable emission: Encoder options for declaration-order output, omitting empty arrays, literal multiline strings, and inlining small sub-tables.

Install

As a library:

go get sourcedock.dev/petrbalvin/interpres/v2

Requires Go 1.27.1 or newer. The module imports only the standard library.

Quick start

git clone https://sourcedock.dev/petrbalvin/interpres.git
cd interpres
just example

just example runs the tour in examples/basic: it decodes an embedded document into a struct, prints it, and re-encodes it under both Encoder layouts.

Usage

Decode into a struct

type Config struct {
	Title  string `toml:"title"`
	Server struct {
		Host string `toml:"host"`
		Port int    `toml:"port"`
	} `toml:"server"`
}

var cfg Config
err := interpres.Unmarshal(data, &cfg)

Fields match by the toml:"name" tag, or by the lower-cased field name when no tag is present; toml:"-" skips a field. Parse returns a *Document that also carries the key order and the comments, ParseMap returns the plain map[string]any tree, and UnmarshalContext accepts a context.

Encode from a struct

out, err := interpres.Marshal(cfg)

writes:

title = "example"

[server]
host = "127.0.0.1"
port = 9090

Tables are laid out scalars first, then sub-tables, then arrays of tables, which is the layout that re-parses to the same tree.

Strict decoding

err := interpres.NewDecoder().
	DisallowUnknownFields().
	Decode(data, &cfg)

A key with no matching field becomes an error instead of a silent drop.

Custom types

type Port int

func (p Port) MarshalTOML() (any, error) {
	return int64(p), nil
}

type IP struct{ net.IP }

func (ip *IP) UnmarshalTOML(data any) error {
	s, ok := data.(string)
	if !ok {
		return fmt.Errorf("ip: not a string")
	}
	ip.IP = net.ParseIP(s)
	return nil
}

The value MarshalTOML returns is encoded in place of the receiver; UnmarshalTOML receives the parsed value verbatim.

Encoder options

out, err := interpres.NewEncoder().
	GroupByKind(false).      // preserve declaration order
	OmitEmptyArrays().       // skip empty scalar arrays
	UseLiteralMultiline(80). // long multi-line strings as literal blocks
	Marshal(cfg)

Cancellation

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

out, err := interpres.MarshalContext(ctx, cfg)

ParseContext, UnmarshalContext, (*Decoder).DecodeContext and (*Encoder).MarshalContext follow the same pattern.

The full rules for field matching, numeric conversion and emission live in docs/API.md.

Development

just build       # compile bin/interpres-decode
just test        # the suite, with the 80 percent coverage floor
just toml-test   # the official compliance suite, needs toml-test on PATH
just fmt         # gofmt in place

See docs/DEVELOPMENT.md for the full workflow, and CONTRIBUTING.md for how to contribute.

Documentation

Licence

MIT. See LICENSE.

Copyright © 2026 Petr Balvín

S
Description
TOML 1.1 parser and encoder in pure Go: an encoding/json-style API, documents that keep key order and comments through a round trip, strict decoding and cancellation; standard library only, passing the entire official toml-test suite.
Readme MIT
2.8 MiB
v2.0.0
Latest
2026-09-22 19:52:42 +00:00
Languages
Go 98.4%
Just 1.2%
Perl 0.4%