feat: add the Document edit pipeline with comment-preserving write
Test / test (push) Successful in 1m35s
Test / test (push) Successful in 1m35s
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+151
@@ -3,6 +3,11 @@
|
||||
|
||||
package interpres
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// A Document is a parsed TOML document: the values, plus what the map shape
|
||||
// cannot carry, which is the order the keys were written in, whether a table
|
||||
// was written inline or under a header, and the comments.
|
||||
@@ -39,6 +44,38 @@ func (d *Document) Footer() []string { return d.footer }
|
||||
// SetFooter replaces those lines.
|
||||
func (d *Document) SetFooter(lines []string) { d.footer = lines }
|
||||
|
||||
// The document-level convenience forms of the Table edit API; they act on
|
||||
// the root table.
|
||||
|
||||
// Get returns the root table's entry for key, and whether the document has
|
||||
// one. See Table.Get.
|
||||
func (d *Document) Get(key string) (*Entry, bool) { return d.Root().Get(key) }
|
||||
|
||||
// GetString returns the string the key holds, and whether it holds one.
|
||||
func (d *Document) GetString(key string) (string, bool) { return d.Root().GetString(key) }
|
||||
|
||||
// GetInt returns the integer the key holds, and whether it holds one.
|
||||
func (d *Document) GetInt(key string) (int64, bool) { return d.Root().GetInt(key) }
|
||||
|
||||
// GetFloat returns the float the key holds, and whether it holds one.
|
||||
func (d *Document) GetFloat(key string) (float64, bool) { return d.Root().GetFloat(key) }
|
||||
|
||||
// GetBool returns the boolean the key holds, and whether it holds one.
|
||||
func (d *Document) GetBool(key string) (bool, bool) { return d.Root().GetBool(key) }
|
||||
|
||||
// GetArray returns the value array the key holds, and whether it holds one.
|
||||
func (d *Document) GetArray(key string) ([]any, bool) { return d.Root().GetArray(key) }
|
||||
|
||||
// GetTable returns the node of the table the key holds, and whether it holds
|
||||
// one.
|
||||
func (d *Document) GetTable(key string) (*Table, bool) { return d.Root().GetTable(key) }
|
||||
|
||||
// Set stores value under the key in the root table. See Table.Set.
|
||||
func (d *Document) Set(key string, value any) { d.Root().Set(key, value) }
|
||||
|
||||
// Delete removes the key from the root table. See Table.Delete.
|
||||
func (d *Document) Delete(key string) { d.Root().Delete(key) }
|
||||
|
||||
// A Table is one TOML table: its values, its keys in written order, and the
|
||||
// comments around the header or the key that introduced it.
|
||||
type Table struct {
|
||||
@@ -199,3 +236,117 @@ func (e *Entry) Trailing() string { return e.trailing }
|
||||
|
||||
// SetTrailing replaces that comment.
|
||||
func (e *Entry) SetTrailing(line string) { e.trailing = line }
|
||||
|
||||
// GetString returns the string the key holds, and whether it holds one.
|
||||
func (t *Table) GetString(key string) (string, bool) {
|
||||
v, ok := t.values[key]
|
||||
s, ok := v.(string)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// GetInt returns the integer the key holds, and whether it holds one.
|
||||
func (t *Table) GetInt(key string) (int64, bool) {
|
||||
v, ok := t.values[key]
|
||||
i, ok := v.(int64)
|
||||
return i, ok
|
||||
}
|
||||
|
||||
// GetFloat returns the float the key holds, and whether it holds one.
|
||||
func (t *Table) GetFloat(key string) (float64, bool) {
|
||||
v, ok := t.values[key]
|
||||
f, ok := v.(float64)
|
||||
return f, ok
|
||||
}
|
||||
|
||||
// GetBool returns the boolean the key holds, and whether it holds one.
|
||||
func (t *Table) GetBool(key string) (bool, bool) {
|
||||
v, ok := t.values[key]
|
||||
b, ok := v.(bool)
|
||||
return b, ok
|
||||
}
|
||||
|
||||
// GetArray returns the value array the key holds, and whether it holds one.
|
||||
func (t *Table) GetArray(key string) ([]any, bool) {
|
||||
v, ok := t.values[key]
|
||||
a, ok := v.([]any)
|
||||
return a, ok
|
||||
}
|
||||
|
||||
// GetTable returns the node of the table the key holds, and whether it holds
|
||||
// one, whichever way the document wrote the table.
|
||||
func (t *Table) GetTable(key string) (*Table, bool) {
|
||||
c := t.child(key)
|
||||
return c, c != nil
|
||||
}
|
||||
|
||||
// Set stores value under key. A key the table already has keeps its position
|
||||
// and its comments; a new one joins the end. A value of map[string]any
|
||||
// becomes a table node of its own, written under a header like any other
|
||||
// table; a Go map carries no order, so its keys take sorted order. A value
|
||||
// of []map[string]any becomes an array-of-tables node.
|
||||
func (t *Table) Set(key string, value any) {
|
||||
e, ok := t.index[key]
|
||||
if !ok {
|
||||
t.values[key] = value
|
||||
e = t.addValue(key, value, false)
|
||||
if m, isMap := value.(map[string]any); isMap {
|
||||
e.child = newOrderedTable(m)
|
||||
}
|
||||
if items, isArray := value.([]map[string]any); isArray {
|
||||
e.elements = make([]*Table, len(items))
|
||||
for i, item := range items {
|
||||
e.elements[i] = newOrderedTable(item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
t.values[key] = value
|
||||
switch v := value.(type) {
|
||||
case map[string]any:
|
||||
if e.child == nil {
|
||||
e.child = newOrderedTable(v)
|
||||
} else {
|
||||
e.child.values = v
|
||||
}
|
||||
e.elements = nil
|
||||
case []map[string]any:
|
||||
e.child = nil
|
||||
e.elements = make([]*Table, len(v))
|
||||
for i, item := range v {
|
||||
e.elements[i] = newOrderedTable(item)
|
||||
}
|
||||
default:
|
||||
e.child = nil
|
||||
e.elements = nil
|
||||
}
|
||||
}
|
||||
|
||||
// newOrderedTable builds a table node for a value the caller set, its keys
|
||||
// entered as entries in sorted order, the order Marshal writes maps in.
|
||||
func newOrderedTable(m map[string]any) *Table {
|
||||
t := newTable(m)
|
||||
for _, k := range slices.Sorted(maps.Keys(m)) {
|
||||
v := m[k]
|
||||
_, isMap := v.(map[string]any)
|
||||
e := t.addValue(k, v, false)
|
||||
if isMap {
|
||||
e.child = newOrderedTable(v.(map[string]any))
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Delete removes key and everything it holds.
|
||||
func (t *Table) Delete(key string) {
|
||||
if _, ok := t.values[key]; !ok {
|
||||
return
|
||||
}
|
||||
delete(t.values, key)
|
||||
delete(t.index, key)
|
||||
for i, e := range t.entries {
|
||||
if e.key == key {
|
||||
t.entries = append(t.entries[:i], t.entries[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user