cst

package
v0.4.7 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 6 Imported by: 3

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChildScope added in v0.3.0

func ChildScope(root, parent *Node) (int, int)

ChildScope returns the range [start, end) in root.Children covering parent and the nested tables/array-tables that belong to it — those whose header is prefixed by parent's key + ".". Unlike parentScope (which stops at the next entry with the *same* header and so over-extends across siblings for nested entries), ChildScope stops at the first table/array-table whose header is not so prefixed, bounding a single entry's descendants correctly at any depth. If parent is root, returns the whole range.

func DeleteAllValues

func DeleteAllValues(container *Node)

DeleteAllValues removes all key-value children from a container, preserving table/array-table headers and other non-KV nodes.

func DeleteValue

func DeleteValue(container *Node, key string)

DeleteValue removes a key-value from the container.

func EscapeString

func EscapeString(s string) string

EscapeString escapes special characters for a TOML basic string.

func ExtractBool

func ExtractBool(kv *Node) (bool, bool)

ExtractBool extracts a bool value from a NodeKeyValue.

func ExtractBoolSlice added in v0.3.0

func ExtractBoolSlice(kv *Node) ([]bool, bool)

ExtractBoolSlice extracts a []bool from a NodeKeyValue whose value is a NodeArray.

func ExtractFloat64

func ExtractFloat64(kv *Node) (float64, bool)

ExtractFloat64 extracts a float64 value from a NodeKeyValue.

func ExtractFloat64Slice added in v0.3.0

func ExtractFloat64Slice(kv *Node) ([]float64, bool)

ExtractFloat64Slice extracts a []float64 from a NodeKeyValue whose value is a NodeArray. Integer-valued array elements are accepted too (a whole-number float like `[1, 2.5]` encodes its 1 as a NodeInteger).

func ExtractInt

func ExtractInt(kv *Node) (int, bool)

ExtractInt extracts an int value from a NodeKeyValue.

func ExtractInt64

func ExtractInt64(kv *Node) (int64, bool)

ExtractInt64 extracts an int64 value from a NodeKeyValue.

func ExtractInt64Slice added in v0.3.0

func ExtractInt64Slice(kv *Node) ([]int64, bool)

ExtractInt64Slice extracts a []int64 from a NodeKeyValue whose value is a NodeArray.

func ExtractIntSlice

func ExtractIntSlice(kv *Node) ([]int, bool)

ExtractIntSlice extracts a []int from a NodeKeyValue whose value is a NodeArray.

func ExtractRaw

func ExtractRaw(kv *Node) (any, bool)

ExtractRaw extracts the value from a NodeKeyValue as a natural Go type (string, int64, float64, bool, or []any). Used for custom unmarshalers.

func ExtractString

func ExtractString(kv *Node) (string, bool)

ExtractString extracts a string value from a NodeKeyValue.

func ExtractStringMap

func ExtractStringMap(container *Node) map[string]string

ExtractStringMap reads all key-value pairs from a container node (typically a NodeTable) as map[string]string.

func ExtractStringSlice

func ExtractStringSlice(kv *Node) ([]string, bool)

ExtractStringSlice extracts a []string from a NodeKeyValue whose value is a NodeArray. A non-string element makes it return false (rather than silently dropping the element), so a heterogeneous array surfaces as a type error instead of a partial slice.

func ExtractUint64

func ExtractUint64(kv *Node) (uint64, bool)

ExtractUint64 extracts a uint64 value from a NodeKeyValue.

func ExtractUint64Slice added in v0.3.0

func ExtractUint64Slice(kv *Node) ([]uint64, bool)

ExtractUint64Slice extracts a []uint64 from a NodeKeyValue whose value is a NodeArray.

func HasValue

func HasValue(container *Node, key string) bool

HasValue checks if a key-value exists in the container.

func KeyNeedsQuoting added in v0.3.0

func KeyNeedsQuoting(key string) bool

KeyNeedsQuoting reports whether a TOML key must be quoted: a bare key may only contain ASCII letters, digits, '_' and '-'. An empty key, or any other character (dots, spaces, unicode, etc.), requires a quoted key.

func KeyValueName

func KeyValueName(kv *Node) string

KeyValueName returns the key name from a NodeKeyValue node. For simple keys like `name = "value"`, returns "name". For dotted keys like `a.b.c = "value"`, returns "a.b.c".

Like TableHeaderKey, the join is lossy: a quoted dot-containing segment is indistinguishable from nesting. Callers that must tell them apart (#103) use keyValueSegments.

func QuoteKey added in v0.3.0

func QuoteKey(key string) string

QuoteKey returns key rendered as a TOML key node: bare if it qualifies as a bare key, otherwise a basic-string-quoted, escaped key (e.g. `"a.b"`). The inverse of the decode-side StripQuotes applied by KeyValueName/TableHeaderKey.

func RespellDottedKeys added in v0.3.1

func RespellDottedKeys(toml []byte) ([]byte, error)

RespellDottedKeys rewrites a single-segment leaf table into top-level dotted-key assignments: `[a]\nname = "x"\nport = 1` becomes `a.name = "x"\na.port = 1`. It is the dual of a nested struct expressed as a header table.

Cycle-1 scope: fires only on single-segment leaf tables with bare-key headers and bare-key bodies, and only when no other table shares the `a` prefix (TOML forbids redefining a table, so `a.name = ...` alongside `[a.b]` would be illegal). Anything else is left canonical. No-op when nothing qualifies.

The emitted `a.x = v` dotted key-values are HOISTED above the first remaining table header: a root-level key-value after a `[table]` binds to that table, so a dotted key for a late-positioned `[a]` would otherwise rebind under a preceding header (changing the value). Mirrors RespellInlineTables' hoist.

func RespellImplicitParents added in v0.4.0

func RespellImplicitParents(toml []byte) ([]byte, error)

RespellImplicitParents removes a bare `[parent]` header whose body is empty AND whose immediately-following header extends it (`[parent.x]` / `[[parent.x]]`) — the standalone-dotted spelling (#113/#64/#117). Per the TOML spec a dotted header defines its parent tables implicitly, so such a `[parent]` is redundant; the encoder never produces this form (it always emits the bare parent), so the round-trip fuzzers never reach it.

The "immediately following" test (rather than a document-wide prefix scan) is what makes the rewrite VALUE-preserving across array-of-tables: canonical output always places a table's sub-tables right after it, so an empty `[parent]` whose next header does NOT extend it is the sole (empty) definition in its scope — e.g. an empty map entry in one `[[xs]]` entry whose same-keyed sibling in ANOTHER entry has deeper children — and must be kept. Only empty-bodied parents are removed (one with key-values would lose them); chains collapse. A no-op when nothing qualifies.

func RespellInlineArrays added in v0.3.1

func RespellInlineArrays(toml []byte) ([]byte, error)

RespellInlineArrays rewrites a run of single-segment leaf array-of-tables entries into one inline array of inline tables: `[[xs]]\nh = "a"\n[[xs]]\nh = "b"` becomes `xs = [ { h = "a" }, { h = "b" } ]`.

Cycle-1 scope: fires only on single-segment array-tables whose entries are all leaves (scalar-only bodies, no nested sub-tables/array-tables). Entries with nested structure, or multi-segment headers, are left canonical. No-op when nothing qualifies.

func RespellInlineTables added in v0.3.1

func RespellInlineTables(toml []byte) ([]byte, error)

RespellInlineTables rewrites a header-table subtree into a single nested inline-table key-value, the inline-spelling dual of `[a.b]` sub-tables. For each single-segment table `[a]` it folds `[a]` AND every `[a.…]` sub-table under it into one root-level `a = { … }` value (#111): `[env]\nk = "v"` becomes `env = { k = "v" }`; `[a]\n[a.b]\nk = "v"` becomes `a = { b = { k = "v" } }`; deeper nesting folds the same way. The inlined key-value is hoisted ABOVE the first remaining table header (a bare root key-value after a `[table]` would bind to that table, not the document), and the folded headers are removed.

A subtree is left fully canonical when it cannot become one inline value: it contains a descendant array-table, or a leaf carries a multiline string (a newline is invalid inside `{ }`). A multi-segment table with no single-segment root (e.g. a top-level map[string]struct's `[actions.build]` with no `[actions]`) has no leader and is left canonical too. No-op when nothing qualifies.

func SetAny

func SetAny(container *Node, key string, value any) error

SetAny encodes a Go value and sets it as a key-value in the container.

func SetMultilineString

func SetMultilineString(container *Node, key, value string) error

SetMultilineString sets a string value using TOML multiline basic string syntax.

func SetValue

func SetValue(container *Node, key string, encoded []byte, kind NodeKind) error

SetValue finds or creates a key-value in container and sets its value.

func StripQuotes

func StripQuotes(s string) string

StripQuotes removes TOML quotes from a raw string value. Handles basic (""), literal (”), multiline ("""/”'), and bare strings.

func TableHeaderKey

func TableHeaderKey(table *Node) string

TableHeaderKey returns the dotted key from a NodeTable or NodeArrayTable header. For `[a.b.c]`, returns "a.b.c".

It joins segments with ".", which is lossy: a single quoted segment `"a.b"` and the nesting `a.b` both yield "a.b". Callers that must distinguish a dot-containing key from nesting (#103) use TableHeaderSegments instead.

func TableHeaderSegments added in v0.3.0

func TableHeaderSegments(table *Node) []string

TableHeaderSegments returns the per-segment keys of a NodeTable or NodeArrayTable header, each with quotes stripped. For `[a."b.c".d]` it returns ["a", "b.c", "d"] — unlike TableHeaderKey, which joins them into the ambiguous "a.b.c.d". Preserving segment boundaries lets a quoted key that contains dots or spaces be distinguished from genuine table nesting (#103).

func UnescapeString

func UnescapeString(s string) string

UnescapeString processes TOML escape sequences in a basic string.

Types

type Field added in v0.4.0

type Field struct {
	Key string
	Val Value
}

Field is one entry of a VTable.

type Node

type Node struct {
	Kind     NodeKind
	Raw      []byte
	Children []*Node
}

func AppendArrayTableEntryAfter

func AppendArrayTableEntryAfter(root *Node, key string) *Node

AppendArrayTableEntryAfter appends a new [[key]] array-table entry, inserting it after the last existing [[key]] or at the end. key is a static dotted path of bare field-name segments (runtime map keys flow through EnsureChildSubTable instead), so splitting it on "." into per-segment NodeKeys is lossless and gives the same per-segment header shape the parser produces — which a deeper sub-table built on top of this entry relies on (#103).

func AppendChildArrayTableEntry added in v0.3.0

func AppendChildArrayTableEntry(root, parent *Node, key string) *Node

AppendChildArrayTableEntry appends a new [[parentKey.key]] entry scoped to parent: after parent's last existing [[parentKey.key]], else after parent's other children. When parent is root it degrades to a root-level append, matching AppendArrayTableEntryAfter.

func EnsureChildSubTable

func EnsureChildSubTable(root *Node, parent *Node, prefix, key string) *Node

EnsureChildSubTable finds or creates a [prefix.key] sub-table scoped to parent.

func EnsureChildTable

func EnsureChildTable(root *Node, parent *Node, key string) *Node

EnsureChildTable finds or creates a [key] table as a child of parent. Unlike the document API's EnsureTable which always appends at root end, this inserts the new table immediately after parent in root.Children, fixing scoping for tables inside array-table entries.

func FindArrayTableNodes

func FindArrayTableNodes(root *Node, key string) []*Node

FindArrayTableNodes returns all [[key]] array-table nodes from root children.

func FindChildArrayTableNodes added in v0.3.0

func FindChildArrayTableNodes(root, parent *Node, key string) []*Node

FindChildArrayTableNodes returns the [[parentKey.key]] entries scoped to parent, in document order. Scoped variant of FindArrayTableNodes for nested arrays.

func FindChildSubTables added in v0.3.0

func FindChildSubTables(root, parent *Node, field string) []*Node

FindChildSubTables returns the immediate [parentKey.field.<k>] sub-tables (one segment under field) scoped to parent — the map[string]struct entries for a map field nested inside an array-table entry.

Matching is structural (per-segment), not joined-string: a child qualifies iff its header has exactly len(parent segments)+2 segments, its leading segments equal parent's, and the next segment is field. The final segment is the map key, which may itself contain dots or spaces (`[parent.field."k.0"]`) — the old joined-prefix + Contains(".") test wrongly dropped those and could admit a deeper sibling whose joined header coincidentally shared the prefix (#103).

func KeyValueValue

func KeyValueValue(kv *Node) *Node

KeyValueValue returns the value node from a NodeKeyValue node. The value is the first non-whitespace child after the NodeEquals.

func Parse

func Parse(input []byte) (*Node, error)

Parse consumes raw TOML input and returns a CST document node. Every token becomes a leaf node; structural nodes group their children. Concatenating all leaf Raw bytes reproduces the original input byte-for-byte.

func ParseReader

func ParseReader(r io.Reader) (*Node, error)

ParseReader consumes TOML from an io.Reader and returns a CST document node.

func (*Node) Bytes

func (n *Node) Bytes() []byte

type NodeKind

type NodeKind int
const (
	NodeDocument   NodeKind = iota
	NodeTable               // [table]
	NodeArrayTable          // [[array-of-tables]]
	NodeKeyValue            // key = value
	NodeKey                 // bare or quoted key
	NodeDottedKey           // a.b.c
	NodeEquals              // =

	// Values
	NodeString
	NodeInteger
	NodeFloat
	NodeBool
	NodeDateTime
	NodeArray       // [1, 2, 3]
	NodeInlineTable // {a = 1, b = 2}

	// Trivia
	NodeComment    // # ...
	NodeWhitespace // spaces, tabs
	NodeNewline    // \n, \r\n

	// Punctuation
	NodeBracketOpen  // [
	NodeBracketClose // ]
	NodeBraceOpen    // {
	NodeBraceClose   // }
	NodeComma        // ,
	NodeDot          // .
)

func EncodeMultilineString

func EncodeMultilineString(value string) ([]byte, NodeKind)

EncodeMultilineString encodes a string as TOML multiline basic string.

func EncodeValue

func EncodeValue(value any) ([]byte, NodeKind, error)

EncodeValue converts a Go value to TOML bytes and node kind.

type Value added in v0.4.0

type Value struct {
	Kind   ValueKind
	Leaf   *Node   // VLeaf: the source NodeKeyValue (Extract* operate on it)
	Fields []Field // VTable: in first-seen order
	Items  []Value // VArray: entries, each Kind==VTable
	Node   *Node   // source structural node (header table / array-table entry /
	// contains filtered or unexported fields
}

Value is a node of the canonical value model.

func Decompose added in v0.4.0

func Decompose(root *Node) (*Value, error)

Decompose builds the canonical value model from a parsed document root.

func DecomposeBytes added in v0.4.3

func DecomposeBytes(data []byte) (*Value, error)

DecomposeBytes parses TOML and returns the canonical value model in one call, equivalent to Decompose(Parse(data)). It is the entry point for computing undecoded ("unknown") keys WITHOUT decoding through a generated decoder or reflection marshal: parse, mark the keys you recognize via Get/GetPath + MarkConsumed/MarkSeen, then call Undecoded. Because the model normalizes every TOML spelling first (inline vs header table, dotted vs nested key), this is spelling-correct where a raw-CST key walk — the retired document.UndecodedKeys — was not.

func (*Value) Get added in v0.4.0

func (v *Value) Get(key string) (*Value, bool)

Get returns the field value for key and whether it is present. VTable only.

func (*Value) GetPath added in v0.4.3

func (v *Value) GetPath(dotted string) (*Value, bool)

GetPath navigates a dotted key-path from this table (e.g. "server.tls"), returning the value at the path and whether it is present. Each segment descends one VTable level; a missing segment or a non-table encountered mid-path yields (nil, false). It is the ergonomic way to reach a nested value or whole subtree to MarkSeen/MarkConsumed before calling Undecoded. Segments are split on "." (bare-key paths), matching the dotted paths Undecoded reports.

func (*Value) IsEmptyArray added in v0.4.0

func (v *Value) IsEmptyArray() bool

IsEmptyArray reports whether v is a leaf carrying an empty inline array (`key = []`). Decompose keeps an empty array a VLeaf because emptiness erases the array-of-tables-vs-scalar-array distinction (#94): there are no inline tables to inspect, so inlineTableElements declines it. A struct-slice decoder consults this to treat `xs = []` as an empty array-of-tables (an empty slice) rather than a type error, matching how an empty scalar array already decodes to an empty []int.

func (*Value) MarkConsumed added in v0.4.0

func (v *Value) MarkConsumed()

MarkConsumed records that this value AND its entire subtree are accounted for — a scalar leaf the decoder read, or a map field that absorbs every entry. Undecoded does not descend a consumed value. Generated decoders call it.

func (*Value) MarkSeen added in v0.4.0

func (v *Value) MarkSeen()

MarkSeen records that the decoder recognized this value's key. For a struct table or array it means "entered" — Undecoded still descends to surface any unconsumed child (an unknown struct field). Generated decoders call it.

func (*Value) Undecoded added in v0.4.0

func (v *Value) Undecoded() []string

Undecoded returns the dotted key-paths present in the model that no decoder consumed — the spelling-independent replacement for document.UndecodedKeys. The receiver is treated as entered (its fields are walked); a field whose value was never seen is reported (without descending), a seen struct/array is descended, and a consumed value contributes nothing.

type ValueKind added in v0.4.0

type ValueKind int

ValueKind discriminates the value model node.

const (
	VLeaf  ValueKind = iota // scalar, scalar array, or custom/text leaf
	VTable                  // a table: ordered named values
	VArray                  // an array of tables ([[x]] or inline xs = [ {…} ])
)

Jump to

Keyboard shortcuts

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