package engine import "gitea.maximumdirect.net/eric/ConfigKit/internal/document" // walkDocument visits value nodes in preorder, preserving stored mapping order // and sequence index order. Mapping keys are not separate nodes in this walk. // The callback borrows paths and nodes; clone any mutable state it retains. // Specialized origin, pruning, and budget traversals retain their own semantics. func walkDocument(node *document.Node, path Path, visit func(Path, *document.Node)) { if node == nil { return } visit(path, node) switch node.Kind { case document.Mapping: for _, entry := range node.Mapping { walkDocument(entry.Value, path.Key(entry.Key), visit) } case document.Sequence: for index, child := range node.Sequence { walkDocument(child, path.Index(index), visit) } } } // indexDocument borrows nodes from its caller-owned tree. func indexDocument(node *document.Node, path Path, index map[string]*document.Node) { walkDocument(node, path, func(path Path, node *document.Node) { index[path.String()] = node }) } func documentAtPath(root *document.Node, path Path) (*document.Node, bool) { if root == nil || !path.valid() { return nil, false } current := root for _, segment := range path.segments { switch current.Kind { case document.Mapping: if segment.kind == pathSegmentIndex { return nil, false } found := false for _, entry := range current.Mapping { if entry.Key == segment.key { current = entry.Value found = true break } } if !found { return nil, false } case document.Sequence: index, ok := schemaIndex(segment) if !ok || index >= len(current.Sequence) { return nil, false } current = current.Sequence[index] default: return nil, false } } return current, true } // countInputNodes includes mapping-key nodes, unlike walkDocument's value-only // traversal. Aggregate source budgets charge every input occurrence. func countInputNodes(node *document.Node) int { if node == nil { return 0 } count := 1 if node.Kind == document.Mapping { count += len(node.Mapping) for _, entry := range node.Mapping { count += countInputNodes(entry.Value) } } else if node.Kind == document.Sequence { for _, child := range node.Sequence { count += countInputNodes(child) } } return count }