Add redacted effective rendering

This commit is contained in:
2026-09-06 21:21:11 +00:00
parent 26cece6d53
commit 828fdd38eb
14 changed files with 662 additions and 62 deletions

103
internal/yaml/write.go Normal file
View File

@@ -0,0 +1,103 @@
package yaml
import (
"errors"
"io"
"strings"
"go.yaml.in/yaml/v4"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
var errWrite = errors.New("configkit: effective YAML rendering failed")
// Write serializes one owned document tree with fully specified stable formatting.
func Write(output io.Writer, root *document.Node) error {
if output == nil || root == nil {
return errWrite
}
dumper, err := yaml.NewDumper(strictWriter{output},
yaml.WithIndent(2),
yaml.WithCompactSeqIndent(false),
yaml.WithLineWidth(-1),
yaml.WithUnicode(true),
yaml.WithUniqueKeys(true),
yaml.WithCanonical(false),
yaml.WithLineBreak(yaml.LineBreakLN),
yaml.WithExplicitStart(false),
yaml.WithExplicitEnd(false),
yaml.WithFlowSimpleCollections(false),
yaml.WithQuotePreference(yaml.QuoteSingle),
yaml.WithAllDocuments(false),
)
if err != nil {
return errWrite
}
if err := dumper.Dump(adapterNode(root)); err != nil {
return safeWriteError(err)
}
if err := dumper.Close(); err != nil {
return safeWriteError(err)
}
return nil
}
func adapterNode(node *document.Node) *yaml.Node {
result := &yaml.Node{}
switch node.Kind {
case document.Mapping:
result.Kind, result.Tag = yaml.MappingNode, "!!map"
result.Content = make([]*yaml.Node, 0, len(node.Mapping)*2)
for _, entry := range node.Mapping {
result.Content = append(result.Content,
&yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: entry.Key},
adapterNode(entry.Value),
)
}
case document.Sequence:
result.Kind, result.Tag = yaml.SequenceNode, "!!seq"
result.Content = make([]*yaml.Node, len(node.Sequence))
for index, child := range node.Sequence {
result.Content[index] = adapterNode(child)
}
case document.String:
result.Kind, result.Tag, result.Value = yaml.ScalarNode, "!!str", node.Text
if node.Text == "[REDACTED]" {
result.Style = yaml.SingleQuotedStyle
}
case document.Boolean:
result.Kind, result.Tag, result.Value = yaml.ScalarNode, "!!bool", node.Text
case document.Number:
result.Kind, result.Value = yaml.ScalarNode, node.Text
result.Tag = "!!int"
if strings.ContainsAny(node.Text, ".eE") {
result.Tag = "!!float"
}
case document.Null:
result.Kind, result.Tag, result.Value = yaml.ScalarNode, "!!null", "null"
}
return result
}
func safeWriteError(err error) error {
var dumpError *yaml.DumpError
if errors.As(err, &dumpError) && dumpError.Stage == yaml.WriterStage {
if cause := errors.Unwrap(dumpError); cause != nil {
return cause
}
}
return errWrite
}
type strictWriter struct {
output io.Writer
}
func (writer strictWriter) Write(data []byte) (int, error) {
written, err := writer.output.Write(data)
if err == nil && written != len(data) {
err = io.ErrShortWrite
}
return written, err
}

View File

@@ -0,0 +1,59 @@
package yaml
import (
"bytes"
"errors"
"io"
"testing"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
func TestWriteUsesStableExplicitFormatting(t *testing.T) {
t.Parallel()
root := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{
{Key: "empty_map", Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{}}},
{Key: "empty_sequence", Value: &document.Node{Kind: document.Sequence, Sequence: []*document.Node{}}},
{Key: "number", Value: &document.Node{Kind: document.Number, Text: "1.25e+2"}},
{Key: "redacted", Value: &document.Node{Kind: document.String, Text: "[REDACTED]"}},
{Key: "sequence", Value: &document.Node{Kind: document.Sequence, Sequence: []*document.Node{
{Kind: document.String, Text: "first"},
{Kind: document.String, Text: "true"},
}}},
{Key: "nil_value", Value: &document.Node{Kind: document.Null}},
}}
var output bytes.Buffer
if err := Write(&output, root); err != nil {
t.Fatal(err)
}
want := "empty_map: {}\nempty_sequence: []\nnumber: 1.25e+2\nredacted: '[REDACTED]'\nsequence:\n - first\n - 'true'\nnil_value: null\n"
if output.String() != want {
t.Fatalf("output:\n%s\nwant:\n%s", output.String(), want)
}
}
func TestWritePropagatesWriterErrorsAndShortWrites(t *testing.T) {
t.Parallel()
root := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{Key: "value", Value: &document.Node{Kind: document.String, Text: "safe"}}}}
want := errors.New("write failed")
if err := Write(failingWriter{err: want}, root); !errors.Is(err, want) {
t.Fatalf("writer error = %v", err)
}
if err := Write(shortOutput{}, root); !errors.Is(err, io.ErrShortWrite) {
t.Fatalf("short writer error = %v", err)
}
}
type failingWriter struct {
err error
}
func (writer failingWriter) Write(data []byte) (int, error) {
return min(2, len(data)), writer.err
}
type shortOutput struct{}
func (shortOutput) Write(data []byte) (int, error) {
return max(0, len(data)-1), nil
}