feat: add Statements, the top-level statement iterator
Test / test (push) Successful in 1m36s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-22 01:12:04 +02:00
parent d92bb56853
commit a7a942a8e1
6 changed files with 233 additions and 0 deletions
+65
View File
@@ -25,6 +25,8 @@ import (
"context"
"errors"
"fmt"
"io"
"iter"
"os"
"reflect"
"slices"
@@ -526,6 +528,69 @@ func Marshal(v any) ([]byte, error) {
return MarshalContext(context.Background(), v)
}
// A Statement is one top-level statement of a document, what Statements
// yields: a key with its value, a table with its node, or one element of an
// array of tables with its node.
type Statement struct {
// Key is the key as the document wrote it.
Key string
// Value is the value of a key/value statement, and the value map of a
// table statement.
Value any
// Table is the node of a table or array-of-tables statement, carrying the
// written key order and the comments; nil for a plain key/value.
Table *Table
// Index is the element's position when the statement is one element of an
// array of tables, and -1 otherwise.
Index int
}
// Statements reads a TOML document from r and returns an iterator over its
// top-level statements in written order: key/value statements, a [table]
// header as one statement carrying its Table node, and an [[array of
// tables]] as one statement per element, each with the element's node and
// its Index. Iteration stops at the first error, which arrives as the second
// value, and at a false yield: a caller that breaks after the statement it
// wanted reads no further ones.
//
// The reader is consumed in full before the first statement is yielded,
// because the parser scans the source in place; processing the yielded
// statements one at a time is what bounds what the caller holds, and a
// later direct-to-target parse removes the whole-source hold.
func Statements(r io.Reader) iter.Seq2[Statement, error] {
return func(yield func(Statement, error) bool) {
data, err := io.ReadAll(r)
if err != nil {
yield(Statement{Index: -1}, err)
return
}
doc, err := Parse(data)
if err != nil {
yield(Statement{Index: -1}, err)
return
}
for _, e := range doc.Root().Entries() {
if els := e.Elements(); len(els) > 0 {
for i, el := range els {
if !yield(Statement{Key: e.Key(), Value: e.Value(), Table: el, Index: i}, nil) {
return
}
}
continue
}
if child := e.Table(); child != nil {
if !yield(Statement{Key: e.Key(), Value: e.Value(), Table: child, Index: -1}, nil) {
return
}
continue
}
if !yield(Statement{Key: e.Key(), Value: e.Value(), Index: -1}, nil) {
return
}
}
}
}
// MarshalAppend appends the TOML encoding of v to buf and returns the extended
// buffer, the shape json.MarshalAppend has. A failed encoding leaves buf
// untouched and comes back with a nil slice.