Separate explanation replay snapshots and rendering
This commit is contained in:
63
snapshot.go
Normal file
63
snapshot.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package configkit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
||||
)
|
||||
|
||||
// Snapshot projection is the shared redaction boundary for effective YAML
|
||||
// and explanation values. It borrows frozen trees and returns a detached tree.
|
||||
func valueSnapshot(root *document.Node, path Path, secrets map[string]bool) ValueSnapshot {
|
||||
node, present := documentAtPath(root, path)
|
||||
if !present {
|
||||
return ValueSnapshot{}
|
||||
}
|
||||
safe, redacted := safeSnapshotDocument(node, secrets, path)
|
||||
var output bytes.Buffer
|
||||
if err := yamladapter.Write(&output, safe); err != nil {
|
||||
return ValueSnapshot{Present: true, Redacted: true, YAML: "'[REDACTED]'\n"}
|
||||
}
|
||||
return ValueSnapshot{Present: true, Redacted: redacted, YAML: output.String()}
|
||||
}
|
||||
|
||||
func safeSnapshotDocument(node *document.Node, secrets map[string]bool, path Path) (*document.Node, bool) {
|
||||
if node == nil {
|
||||
return nil, false
|
||||
}
|
||||
if pathIsSecret(secrets, path) {
|
||||
return &document.Node{Kind: document.String, Text: "[REDACTED]"}, true
|
||||
}
|
||||
result := *node
|
||||
redacted := false
|
||||
switch node.Kind {
|
||||
case document.Mapping:
|
||||
result.Mapping = make([]document.Entry, len(node.Mapping))
|
||||
copy(result.Mapping, node.Mapping)
|
||||
sortDocumentMapping(result.Mapping)
|
||||
for index := range result.Mapping {
|
||||
entry := &result.Mapping[index]
|
||||
entry.Value, redacted = safeSnapshotChild(entry.Value, secrets, path.Key(entry.Key), redacted)
|
||||
}
|
||||
case document.Sequence:
|
||||
result.Sequence = make([]*document.Node, len(node.Sequence))
|
||||
for index, child := range node.Sequence {
|
||||
result.Sequence[index], redacted = safeSnapshotChild(child, secrets, path.Index(index), redacted)
|
||||
}
|
||||
}
|
||||
return &result, redacted
|
||||
}
|
||||
|
||||
func safeSnapshotChild(node *document.Node, secrets map[string]bool, path Path, prior bool) (*document.Node, bool) {
|
||||
safe, redacted := safeSnapshotDocument(node, secrets, path)
|
||||
return safe, prior || redacted
|
||||
}
|
||||
|
||||
func sortDocumentMapping(entries []document.Entry) {
|
||||
slices.SortFunc(entries, func(left, right document.Entry) int {
|
||||
return strings.Compare(left.Key, right.Key)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user