mirror of
https://github.com/mikefarah/yq.git
synced 2026-03-10 15:54:26 +00:00
wip
This commit is contained in:
parent
5f3dcb1ccf
commit
8af768a015
@ -1,8 +1,4 @@
|
||||
io_mode = "async"
|
||||
|
||||
service "http" "web_proxy" {
|
||||
listen_addr = "127.0.0.1:8080"
|
||||
|
||||
service "cat" {
|
||||
process "main" {
|
||||
command = ["/usr/local/bin/awesome-app", "server"]
|
||||
}
|
||||
|
||||
@ -105,13 +105,40 @@ func hclBodyToNode(body *hclsyntax.Body, src []byte) *CandidateNode {
|
||||
// addBlockToMapping nests block type and labels into the parent mapping, merging children.
|
||||
func addBlockToMapping(parent *CandidateNode, block *hclsyntax.Block, src []byte) {
|
||||
bodyNode := hclBodyToNode(block.Body, src)
|
||||
chain := bodyNode
|
||||
for i := len(block.Labels) - 1; i >= 0; i-- {
|
||||
wrap := &CandidateNode{Kind: MappingNode}
|
||||
wrap.AddKeyValueChild(createStringScalarNode(block.Labels[i]), chain)
|
||||
chain = wrap
|
||||
current := parent
|
||||
|
||||
// ensure block type mapping exists
|
||||
var typeNode *CandidateNode
|
||||
for i := 0; i < len(current.Content); i += 2 {
|
||||
if current.Content[i].Value == block.Type {
|
||||
typeNode = current.Content[i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if typeNode == nil {
|
||||
_, typeNode = current.AddKeyValueChild(createStringScalarNode(block.Type), &CandidateNode{Kind: MappingNode})
|
||||
}
|
||||
current = typeNode
|
||||
|
||||
// walk labels, creating/merging mappings
|
||||
for _, label := range block.Labels {
|
||||
var next *CandidateNode
|
||||
for i := 0; i < len(current.Content); i += 2 {
|
||||
if current.Content[i].Value == label {
|
||||
next = current.Content[i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if next == nil {
|
||||
_, next = current.AddKeyValueChild(createStringScalarNode(label), &CandidateNode{Kind: MappingNode})
|
||||
}
|
||||
current = next
|
||||
}
|
||||
|
||||
// merge body attributes/blocks into the final mapping
|
||||
for i := 0; i < len(bodyNode.Content); i += 2 {
|
||||
current.AddKeyValueChild(bodyNode.Content[i], bodyNode.Content[i+1])
|
||||
}
|
||||
parent.AddKeyValueChild(createStringScalarNode(block.Type), chain)
|
||||
}
|
||||
|
||||
func convertHclExprToNode(expr hclsyntax.Expression, src []byte) *CandidateNode {
|
||||
|
||||
@ -153,17 +153,37 @@ func (he *hclEncoder) encodeNode(body *hclwrite.Body, node *CandidateNode) error
|
||||
if valueNode.Kind == MappingNode && valueNode.Style != FlowStyle {
|
||||
// Try to extract block labels from a single-entry mapping chain
|
||||
if labels, bodyNode, ok := extractBlockLabels(valueNode); ok {
|
||||
if len(labels) > 1 && mappingChildrenAllMappings(bodyNode) {
|
||||
primaryLabels := labels[:len(labels)-1]
|
||||
nestedType := labels[len(labels)-1]
|
||||
block := body.AppendNewBlock(key, primaryLabels)
|
||||
if handled, err := he.encodeMappingChildrenAsBlocks(block.Body(), nestedType, bodyNode); err != nil {
|
||||
return err
|
||||
} else if !handled {
|
||||
if err := he.encodeNodeAttributes(block.Body(), bodyNode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
block := body.AppendNewBlock(key, labels)
|
||||
if err := he.encodeNodeAttributes(block.Body(), bodyNode); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
// If all child values are mappings, treat each child key as a labeled instance of this block type
|
||||
if handled, err := he.encodeMappingChildrenAsBlocks(body, key, valueNode); err != nil {
|
||||
return err
|
||||
} else if handled {
|
||||
continue
|
||||
}
|
||||
// No labels detected, render as unlabeled block
|
||||
block := body.AppendNewBlock(key, nil)
|
||||
if err := he.encodeNodeAttributes(block.Body(), valueNode); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
// Render as attribute: key = value
|
||||
// Check the style to determine how to encode strings
|
||||
@ -252,6 +272,47 @@ func (he *hclEncoder) encodeNode(body *hclwrite.Body, node *CandidateNode) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// mappingChildrenAllMappings reports whether all values in a mapping node are non-flow mappings.
|
||||
func mappingChildrenAllMappings(node *CandidateNode) bool {
|
||||
if node == nil || node.Kind != MappingNode || node.Style == FlowStyle {
|
||||
return false
|
||||
}
|
||||
if len(node.Content) == 0 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(node.Content); i += 2 {
|
||||
childVal := node.Content[i+1]
|
||||
if childVal.Kind != MappingNode || childVal.Style == FlowStyle {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// encodeMappingChildrenAsBlocks emits a block for each mapping child, treating the child key as a label.
|
||||
// Returns handled=true when it emitted blocks.
|
||||
func (he *hclEncoder) encodeMappingChildrenAsBlocks(body *hclwrite.Body, blockType string, valueNode *CandidateNode) (bool, error) {
|
||||
if !mappingChildrenAllMappings(valueNode) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(valueNode.Content); i += 2 {
|
||||
childKey := valueNode.Content[i].Value
|
||||
childVal := valueNode.Content[i+1]
|
||||
labels := []string{childKey}
|
||||
if extraLabels, bodyNode, ok := extractBlockLabels(childVal); ok {
|
||||
labels = append(labels, extraLabels...)
|
||||
childVal = bodyNode
|
||||
}
|
||||
block := body.AppendNewBlock(blockType, labels)
|
||||
if err := he.encodeNodeAttributes(block.Body(), childVal); err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// encodeNodeAttributes encodes the attributes of a mapping node (used for blocks)
|
||||
func (he *hclEncoder) encodeNodeAttributes(body *hclwrite.Body, node *CandidateNode) error {
|
||||
if node.Kind != MappingNode {
|
||||
@ -263,6 +324,39 @@ func (he *hclEncoder) encodeNodeAttributes(body *hclwrite.Body, node *CandidateN
|
||||
valueNode := node.Content[i+1]
|
||||
key := keyNode.Value
|
||||
|
||||
if valueNode.Kind == MappingNode && valueNode.Style != FlowStyle {
|
||||
if labels, bodyNode, ok := extractBlockLabels(valueNode); ok {
|
||||
if len(labels) > 1 && mappingChildrenAllMappings(bodyNode) {
|
||||
primaryLabels := labels[:len(labels)-1]
|
||||
nestedType := labels[len(labels)-1]
|
||||
block := body.AppendNewBlock(key, primaryLabels)
|
||||
if handled, err := he.encodeMappingChildrenAsBlocks(block.Body(), nestedType, bodyNode); err != nil {
|
||||
return err
|
||||
} else if !handled {
|
||||
if err := he.encodeNodeAttributes(block.Body(), bodyNode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
block := body.AppendNewBlock(key, labels)
|
||||
if err := he.encodeNodeAttributes(block.Body(), bodyNode); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if handled, err := he.encodeMappingChildrenAsBlocks(body, key, valueNode); err != nil {
|
||||
return err
|
||||
} else if handled {
|
||||
continue
|
||||
}
|
||||
block := body.AppendNewBlock(key, nil)
|
||||
if err := he.encodeNodeAttributes(block.Body(), valueNode); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this is an unquoted identifier (no DoubleQuotedStyle)
|
||||
if valueNode.Kind == ScalarNode && valueNode.Tag == "!!str" && valueNode.Style&DoubleQuotedStyle == 0 {
|
||||
if valueNode.Style&LiteralStyle != 0 {
|
||||
|
||||
@ -12,6 +12,39 @@ var nestedExample = `service "http" "web_proxy" {
|
||||
|
||||
var nestedExampleYaml = "service:\n http:\n web_proxy:\n listen_addr: \"127.0.0.1:8080\"\n"
|
||||
|
||||
var multipleBlockLabelKeys = `service "cat" {
|
||||
process "main" {
|
||||
command = ["/usr/local/bin/awesome-app", "server"]
|
||||
}
|
||||
|
||||
process "management" {
|
||||
command = ["/usr/local/bin/awesome-app", "management"]
|
||||
}
|
||||
}
|
||||
`
|
||||
var multipleBlockLabelKeysExpected = `service "cat" {
|
||||
process "main" {
|
||||
command = ["/usr/local/bin/awesome-app", "server"]
|
||||
}
|
||||
process "management" {
|
||||
command = ["/usr/local/bin/awesome-app", "management"]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var multipleBlockLabelKeysExpectedYaml = `service:
|
||||
cat:
|
||||
process:
|
||||
main:
|
||||
command:
|
||||
- "/usr/local/bin/awesome-app"
|
||||
- "server"
|
||||
management:
|
||||
command:
|
||||
- "/usr/local/bin/awesome-app"
|
||||
- "management"
|
||||
`
|
||||
|
||||
var hclFormatScenarios = []formatScenario{
|
||||
{
|
||||
description: "Simple decode",
|
||||
@ -133,6 +166,18 @@ var hclFormatScenarios = []formatScenario{
|
||||
expected: "values:\n - 1\n - \"two\"\n - true\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
description: "multiple block label keys roundtrip",
|
||||
input: multipleBlockLabelKeys,
|
||||
expected: multipleBlockLabelKeysExpected,
|
||||
scenarioType: "roundtrip",
|
||||
},
|
||||
{
|
||||
description: "multiple block label keys decode",
|
||||
input: multipleBlockLabelKeys,
|
||||
expected: multipleBlockLabelKeysExpectedYaml,
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
description: "block with labels",
|
||||
input: `resource "aws_instance" "example" { ami = "ami-12345" }`,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user