package configkit import ( "errors" "io" "slices" "strings" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml" ) var errEffectiveUnavailable = errors.New("configkit: effective configuration is unavailable") // WriteEffective writes deterministic redacted YAML from the frozen resolved // snapshot. The output is intended for inspection and is not promised reloadable. // Writer errors, including short writes, are returned and may leave partial output. func (r *Result[T]) WriteEffective(output io.Writer) error { if r == nil || r.final == nil || output == nil { return errEffectiveUnavailable } safe := redactedDocument(r.final, r.secrets, Path{}) return yamladapter.Write(output, safe) } func redactedDocument(node *document.Node, secret map[string]bool, path Path) *document.Node { if node == nil { return nil } if pathIsSecret(secret, path) { return &document.Node{Kind: document.String, Text: "[REDACTED]"} } result := *node switch node.Kind { case document.Mapping: result.Mapping = make([]document.Entry, len(node.Mapping)) copy(result.Mapping, node.Mapping) slices.SortFunc(result.Mapping, func(left, right document.Entry) int { return strings.Compare(left.Key, right.Key) }) for index := range result.Mapping { entry := &result.Mapping[index] entry.Value = redactedDocument(entry.Value, secret, path.Key(entry.Key)) } case document.Sequence: result.Sequence = make([]*document.Node, len(node.Sequence)) for index, child := range node.Sequence { result.Sequence[index] = redactedDocument(child, secret, path.Index(index)) } } return &result } func pathIsSecret(secret map[string]bool, path Path) bool { if secret[""] { return true } pointer := path.String() for pointer != "" { if secret[pointer] { return true } position := strings.LastIndexByte(pointer, '/') if position <= 0 { break } pointer = pointer[:position] } return false }