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 }