Help us shape the future of Go! Take the 2026 Go Developer Survey

tomledit

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 8 Imported by: 0

README

tomledit

Format-preserving TOML for Go. Read a TOML file, change a few values, write it back: comments, blank lines, indentation, key quoting and the spelling of untouched values stay byte for byte the same.

  • TOML 1.1 (multi-line inline tables, \xHH and \e escapes, optional seconds), with an opt-in TOML 1.0 strict mode.
  • Lossless: Parse(src).String() == src for every valid document.
  • Conformant: passes all valid and invalid cases of toml-test for 1.0 and 1.1.
  • No runtime dependencies.
go get github.com/npikall/toml-edit

Usage

doc, err := tomledit.Parse(src)
if err != nil {
	return err
}

port, err := doc.GetInt("server", "port")

err = doc.Set([]string{"server", "port"}, 9090)                          // replace a value
err = doc.Insert([]string{"server", "workers"}, 4)                       // add a key
err = doc.Insert([]string{"server", "limits"}, map[string]any{"cpu": 2}) // inline table
err = doc.Delete("server", "debug")                                      // remove a key or table

os.WriteFile("config.toml", []byte(doc.String()), 0o644)

Given

# server settings
[server]
host = "localhost"  # bind address
port = 8080
debug = true

Set, Insert and Delete above produce a minimal diff:

 # server settings
 [server]
 host = "localhost"  # bind address
-port = 8080
-debug = true
+port = 9090
+workers = 4
+limits = { cpu = 2 }
Editing rules
Operation Behavior
Set Replaces only the value text. Key, spacing and trailing comment stay. The path must exist and hold a value, not a table. An array written across several lines stays one element per line, with its indentation and a trailing comma; elements that remain keep their spelling and comments.
Insert Appends to the end of the table section that owns the key, indented like its last key. Keys under a dotted-key table stay dotted, keys of inline tables go into the braces, keys of missing tables get a new [header] at the end of the file. With Multiline(), an array is written one element per line.
Delete Removes the key with the comments and blank lines before it. Deleting a table removes its header, body and sub-tables. Commas in inline tables are fixed up.

Every edit is re-validated. An edit that would make the document invalid (for example a conflicting key) is rolled back and returns an error. Errors wrap ErrNotFound, ErrNotValue, ErrExists, ErrNotTable or ErrType for use with errors.Is.

Values
TOML Go (read) Go (write)
string string string
integer int64 any integer type (up to math.MaxInt64)
float float64 float32, float64
boolean bool bool
offset date-time time.Time time.Time
local date-time / date / time eval.LocalDateTime / eval.LocalDate / eval.LocalTime same
array []any slices and arrays
table *eval.Table map[string]V (written as inline table)

New text is always valid TOML 1.0 and 1.1.

TOML 1.0
doc, err := tomledit.Parse(src, tomledit.Strict10())

rejects syntax added in 1.1. Edits never introduce 1.1 syntax, so a strict document stays valid 1.0.

Packages

Package Role
tomledit Public edit API
token, lexer Tokens and context-sensitive lexer
ast Lossless concrete syntax tree
parser Recursive-descent parser (syntax only)
eval Semantic validation and value decoding
format Go values to canonical TOML text
cmd/toml-test-decoder Decoder for the toml-test runner

Development

Requires Go and Task.

task test        # unit, golden and toml-test conformance tests
task lint        # golangci-lint
task bench       # benchmarks
task toml-test   # official toml-test runner, TOML 1.0 and 1.1
task fuzz        # lexer; fuzz:parser and fuzz:eval for the others

Documentation

Overview

Package tomledit reads and edits TOML documents while preserving their formatting: comments, whitespace, key quoting and the spelling of values that are not changed survive every edit byte for byte.

Parse accepts TOML 1.1 by default, or only TOML 1.0 with the Strict10 option. A parsed Document returns its source unchanged from String until it is edited with Set, Insert or Delete. Every edit is validated; an edit that would make the document invalid is rolled back and reported.

Values are read with Get or the typed getters (GetInt, GetString, ...). Integers are int64, floats float64, offset date-times time.Time, and local dates and times the types of package eval. New values are written from Go strings, booleans, integers, floats, time.Time, the eval date/time types, slices (as arrays) and maps with string keys (as inline tables).

Example
package main

import (
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

const exampleConfig = `# server settings
[server]
host = "localhost"  # bind address
port = 8080
debug = true
`

func main() {
	doc, err := tomledit.Parse(exampleConfig)
	if err != nil {
		panic(err)
	}
	port, _ := doc.GetInt("server", "port")
	fmt.Println("port:", port)

	_ = doc.Set([]string{"server", "port"}, 9090)
	_ = doc.Insert([]string{"server", "workers"}, 4)
	_ = doc.Delete("server", "debug")
	fmt.Print(doc)
}
Output:
port: 8080
# server settings
[server]
host = "localhost"  # bind address
port = 9090
workers = 4

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound is returned when a key path does not exist.
	ErrNotFound = errors.New("key not found")
	// ErrNotValue is returned by Set when a key path names a table defined
	// by a header or dotted keys, or an array of tables.
	ErrNotValue = errors.New("key does not hold a value")
	// ErrType is returned by the typed getters when the value has a
	// different type.
	ErrType = errors.New("wrong value type")
	// ErrExists is returned by Insert when the key path already exists.
	ErrExists = errors.New("key already exists")
	// ErrNotTable is returned by Insert when the parent of the key path is
	// not a table.
	ErrNotTable = errors.New("key does not hold a table")
)

Functions

This section is empty.

Types

type Document

type Document struct {
	// contains filtered or unexported fields
}

Document is a parsed TOML document. Its String method returns the source with all edits applied.

func Parse

func Parse(src string, opts ...Option) (*Document, error)

Parse parses and validates src as TOML 1.1, or 1.0 with Strict10. The result's String method returns src unchanged until it is edited.

func (*Document) Delete

func (d *Document) Delete(keys ...string) error

Delete removes the key at the key path together with the comments and blank lines before it. Deleting a table removes its header, its key/values and all its sub-tables.

Example
package main

import (
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

func main() {
	doc, _ := tomledit.Parse(`point = { x = 1, y = 2 }

# old section
[legacy]
a = 1
`)
	_ = doc.Delete("point", "x")
	_ = doc.Delete("legacy")
	fmt.Print(doc)
}
Output:
point = { y = 2 }

func (*Document) Get

func (d *Document) Get(keys ...string) (any, bool)

Get returns the value at the key path: a string, int64, float64, bool, time.Time, eval.LocalDateTime, eval.LocalDate, eval.LocalTime, []any or *eval.Table. No keys return the root table.

Example
package main

import (
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

func main() {
	doc, _ := tomledit.Parse("tags = [\"a\", \"b\"]\npoint = { x = 1 }\n")
	tags, _ := doc.Get("tags")
	x, _ := doc.Get("point", "x")
	_, ok := doc.Get("missing")
	fmt.Println(tags, x, ok)
}
Output:
[a b] 1 false

func (*Document) GetBool

func (d *Document) GetBool(keys ...string) (bool, error)

GetBool returns the boolean at the key path.

func (*Document) GetFloat

func (d *Document) GetFloat(keys ...string) (float64, error)

GetFloat returns the float at the key path.

func (*Document) GetInt

func (d *Document) GetInt(keys ...string) (int64, error)

GetInt returns the integer at the key path.

func (*Document) GetString

func (d *Document) GetString(keys ...string) (string, error)

GetString returns the string at the key path.

func (*Document) Insert

func (d *Document) Insert(path []string, value any, opts ...EditOption) error

Insert adds a new key with value at the end of the table section that owns it, indented like the key/value before it.

Example
package main

import (
	"errors"
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

func main() {
	doc, _ := tomledit.Parse("[a]\nx = 1\n")
	_ = doc.Insert([]string{"a", "point"}, map[string]any{"x": 1, "y": 2})
	_ = doc.Insert([]string{"b", "c", "d"}, []int{1, 2})
	err := doc.Insert([]string{"a", "x"}, 2)
	fmt.Print(doc)
	fmt.Println(errors.Is(err, tomledit.ErrExists))
}
Output:
[a]
x = 1
point = { x = 1, y = 2 }

[b.c]
d = [1, 2]
true

func (*Document) Set

func (d *Document) Set(path []string, value any) error

Set replaces the value of an existing key. Only the value's text changes; the key, the whitespace around the value and a trailing comment are kept. An array written across several lines stays one element per line with a trailing comma; elements that remain keep their spelling and comments.

Example
package main

import (
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

func main() {
	doc, _ := tomledit.Parse("name = 'old'   # keep me\n")
	_ = doc.Set([]string{"name"}, "new")
	fmt.Print(doc)
}
Output:
name = "new"   # keep me

func (*Document) String

func (d *Document) String() string

String returns the document as TOML text.

type EditOption

type EditOption func(*editConfig)

EditOption configures Insert. Set takes none: it follows the layout of the value it replaces.

func Multiline

func Multiline() EditOption

Multiline makes Insert write an array value one element per line, each followed by a comma and indented two spaces deeper than the line of its key.

type Option

type Option = parser.Option

Option configures Parse.

func Strict10

func Strict10() Option

Strict10 makes Parse accept TOML 1.0 only, rejecting the syntax that 1.1 added. Edits never add 1.1 syntax, so an edited document stays valid 1.0.

Example
package main

import (
	"fmt"

	tomledit "github.com/npikall/toml-edit"
)

func main() {
	_, err := tomledit.Parse("t = 07:32\n", tomledit.Strict10())
	fmt.Println(err)
}
Output:
parse: 1:5: expected a value, found invalid "07:32"

Directories

Path Synopsis
Package ast defines the concrete syntax tree (CST) of a TOML document.
Package ast defines the concrete syntax tree (CST) of a TOML document.
cmd
toml-test-decoder command
Command toml-test-decoder reads TOML on stdin and writes toml-test's tagged JSON on stdout.
Command toml-test-decoder reads TOML on stdin and writes toml-test's tagged JSON on stdout.
Package eval turns a CST into semantic values and enforces the TOML rules the parser cannot check: duplicate keys, table redefinition, value ranges.
Package eval turns a CST into semantic values and enforces the TOML rules the parser cannot check: duplicate keys, table redefinition, value ranges.
Package format turns Go values into CST nodes with canonical TOML text.
Package format turns Go values into CST nodes with canonical TOML text.
internal
tagged
Package tagged converts evaluated TOML into toml-test's tagged JSON form, where every scalar is {"type": ..., "value": ...}.
Package tagged converts evaluated TOML into toml-test's tagged JSON form, where every scalar is {"type": ..., "value": ...}.
Package parser turns TOML source into a lossless concrete syntax tree.
Package parser turns TOML source into a lossless concrete syntax tree.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL