mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-01 01:41:39 +00:00
241 lines
5.9 KiB
Go
241 lines
5.9 KiB
Go
package yqlib
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
type hclEncoder struct {
|
|
indentString string
|
|
}
|
|
|
|
// NewHclEncoder creates a new HCL encoder
|
|
func NewHclEncoder() Encoder {
|
|
return &hclEncoder{
|
|
indentString: " ", // 2 spaces for HCL indentation
|
|
}
|
|
}
|
|
|
|
func (he *hclEncoder) CanHandleAliases() bool {
|
|
return false
|
|
}
|
|
|
|
func (he *hclEncoder) PrintDocumentSeparator(_ io.Writer) error {
|
|
return nil
|
|
}
|
|
|
|
func (he *hclEncoder) PrintLeadingContent(_ io.Writer, _ string) error {
|
|
return nil
|
|
}
|
|
|
|
func (he *hclEncoder) Encode(writer io.Writer, node *CandidateNode) error {
|
|
log.Debugf("I need to encode %v", NodeToString(node))
|
|
|
|
return he.encodeNodeInContext(writer, node, "", false)
|
|
}
|
|
|
|
func (he *hclEncoder) encodeNodeInContext(writer io.Writer, node *CandidateNode, indent string, isInAttribute bool) error {
|
|
switch node.Kind {
|
|
case ScalarNode:
|
|
return writeString(writer, he.formatScalarValue(node.Value))
|
|
case MappingNode:
|
|
return he.encodeMappingInContext(writer, node, indent, isInAttribute)
|
|
case SequenceNode:
|
|
return he.encodeSequence(writer, node, indent)
|
|
case AliasNode:
|
|
return fmt.Errorf("HCL encoder does not support aliases")
|
|
default:
|
|
return fmt.Errorf("unsupported node kind: %v", node.Kind)
|
|
}
|
|
}
|
|
|
|
func (he *hclEncoder) encodeMappingInContext(writer io.Writer, node *CandidateNode, indent string, isInAttribute bool) error {
|
|
if len(node.Content) == 0 {
|
|
return writeString(writer, "{}")
|
|
}
|
|
|
|
// If this mapping is an attribute value or flow-styled, render as inline object: { a = 1, b = "two" }
|
|
if isInAttribute || node.Style == FlowStyle {
|
|
return he.encodeInlineMapping(writer, node, indent)
|
|
} // If we're at the top level (indent == "") AND all values are scalars OR mappings,
|
|
// render as attributes (key = value) or blocks (key { ... })
|
|
if indent == "" {
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
keyNode := node.Content[i]
|
|
valueNode := node.Content[i+1]
|
|
key := keyNode.Value
|
|
|
|
// Block-style for nested mappings (unless they're inline objects), attribute-style for scalars/sequences
|
|
if valueNode.Kind == MappingNode && valueNode.Style != FlowStyle {
|
|
// Block: key { ... }
|
|
if err := writeString(writer, key); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, " {\n"); err != nil {
|
|
return err
|
|
}
|
|
|
|
nextIndent := he.indentString
|
|
for j := 0; j < len(valueNode.Content); j += 2 {
|
|
nestedKeyNode := valueNode.Content[j]
|
|
nestedValueNode := valueNode.Content[j+1]
|
|
nestedKey := nestedKeyNode.Value
|
|
|
|
if err := writeString(writer, nextIndent); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, nestedKey); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, " = "); err != nil {
|
|
return err
|
|
}
|
|
if err := he.encodeNodeInContext(writer, nestedValueNode, nextIndent, true); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, "\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := writeString(writer, "}\n"); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Attribute: key = value
|
|
if err := writeString(writer, key); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, " = "); err != nil {
|
|
return err
|
|
}
|
|
if err := he.encodeNodeInContext(writer, valueNode, "", true); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, "\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Otherwise, this shouldn't happen at nested levels in top-level syntax
|
|
return writeString(writer, "{}")
|
|
}
|
|
|
|
func (he *hclEncoder) encodeInlineMapping(writer io.Writer, node *CandidateNode, indent string) error {
|
|
if len(node.Content) == 0 {
|
|
return writeString(writer, "{}")
|
|
}
|
|
|
|
if err := writeString(writer, "{\n"); err != nil {
|
|
return err
|
|
}
|
|
|
|
nextIndent := indent + he.indentString
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
keyNode := node.Content[i]
|
|
valueNode := node.Content[i+1]
|
|
key := keyNode.Value
|
|
|
|
if err := writeString(writer, nextIndent); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, key); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, " = "); err != nil {
|
|
return err
|
|
}
|
|
if err := he.encodeNodeInContext(writer, valueNode, nextIndent, true); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, "\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := writeString(writer, indent+"}"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (he *hclEncoder) encodeSequence(writer io.Writer, node *CandidateNode, indent string) error {
|
|
if len(node.Content) == 0 {
|
|
return writeString(writer, "[]")
|
|
}
|
|
|
|
// Check if we should use inline format (simple values only)
|
|
useInline := true
|
|
for _, item := range node.Content {
|
|
if item.Kind != ScalarNode {
|
|
useInline = false
|
|
break
|
|
}
|
|
}
|
|
|
|
if useInline {
|
|
// Inline format: ["a", "b", "c"]
|
|
if err := writeString(writer, "["); err != nil {
|
|
return err
|
|
}
|
|
for i, item := range node.Content {
|
|
if i > 0 {
|
|
if err := writeString(writer, ", "); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := he.encodeNodeInContext(writer, item, indent, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := writeString(writer, "]"); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Multi-line format for complex items
|
|
if err := writeString(writer, "[\n"); err != nil {
|
|
return err
|
|
}
|
|
|
|
nextIndent := indent + he.indentString
|
|
for _, item := range node.Content {
|
|
if err := writeString(writer, nextIndent); err != nil {
|
|
return err
|
|
}
|
|
if err := he.encodeNodeInContext(writer, item, nextIndent, true); err != nil {
|
|
return err
|
|
}
|
|
if err := writeString(writer, ",\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := writeString(writer, indent+"]"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (he *hclEncoder) formatScalarValue(value string) string {
|
|
// Check if value is a boolean
|
|
if value == "true" || value == "false" {
|
|
return value
|
|
}
|
|
|
|
// Check if value is a number
|
|
if _, err := strconv.ParseFloat(value, 64); err == nil {
|
|
return value
|
|
}
|
|
|
|
// Treat as string, quote it
|
|
return strconv.Quote(value)
|
|
}
|