mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-04 19:35:38 +00:00
Fix TOML roundtrip: use TomlInline flag instead of FlowStyle to preserve inline tables
FlowStyle affected YAML decode output (causing inline tables to appear as YAML flow mappings). Replace it with a new TOML-specific TomlInline bool on CandidateNode that: - Is set by the TOML decoder for inline tables (not FlowStyle) - Is copied by UpdateAttributesFrom so it survives DeeplyAssign merges - Is checked by the TOML encoder alongside FlowStyle (for YAML flow maps) - Has no effect on the YAML encoder, preserving existing TOML→YAML output TOML roundtrip tests are restored to their original expected values (inline tables stay inline, table sections stay as sections). Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/f59bdf62-6d16-4664-991b-38eb87c9d81c Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
This commit is contained in:
parent
b99f4174ec
commit
d1ffec12e6
@ -100,6 +100,9 @@ type CandidateNode struct {
|
|||||||
// For formats like HCL and TOML: indicates that child entries should be emitted as separate blocks/tables
|
// For formats like HCL and TOML: indicates that child entries should be emitted as separate blocks/tables
|
||||||
// rather than consolidated into nested mappings (default behaviour)
|
// rather than consolidated into nested mappings (default behaviour)
|
||||||
EncodeSeparate bool
|
EncodeSeparate bool
|
||||||
|
// For TOML: indicates that this mapping was originally a TOML inline table and should be
|
||||||
|
// re-encoded as an inline table rather than a separate table section.
|
||||||
|
TomlInline bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) CreateChild() *CandidateNode {
|
func (n *CandidateNode) CreateChild() *CandidateNode {
|
||||||
@ -412,6 +415,7 @@ func (n *CandidateNode) doCopy(cloneContent bool) *CandidateNode {
|
|||||||
IsMapKey: n.IsMapKey,
|
IsMapKey: n.IsMapKey,
|
||||||
|
|
||||||
EncodeSeparate: n.EncodeSeparate,
|
EncodeSeparate: n.EncodeSeparate,
|
||||||
|
TomlInline: n.TomlInline,
|
||||||
}
|
}
|
||||||
|
|
||||||
if cloneContent {
|
if cloneContent {
|
||||||
@ -467,6 +471,8 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignP
|
|||||||
|
|
||||||
// Preserve EncodeSeparate flag for format-specific encoding hints
|
// Preserve EncodeSeparate flag for format-specific encoding hints
|
||||||
n.EncodeSeparate = other.EncodeSeparate
|
n.EncodeSeparate = other.EncodeSeparate
|
||||||
|
// Preserve TomlInline flag for TOML inline table round-trips
|
||||||
|
n.TomlInline = other.TomlInline
|
||||||
|
|
||||||
// merge will pickup the style of the new thing
|
// merge will pickup the style of the new thing
|
||||||
// when autocreating nodes
|
// when autocreating nodes
|
||||||
|
|||||||
@ -150,10 +150,10 @@ func (dec *tomlDecoder) createInlineTableMap(tomlNode *toml.Node) (*CandidateNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &CandidateNode{
|
return &CandidateNode{
|
||||||
Kind: MappingNode,
|
Kind: MappingNode,
|
||||||
Tag: "!!map",
|
Tag: "!!map",
|
||||||
Style: FlowStyle,
|
TomlInline: true,
|
||||||
Content: content,
|
Content: content,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -153,9 +153,7 @@ yq '.' sample.toml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
[name]
|
name = { first = "Tom", last = "Preston-Werner" }
|
||||||
first = "Tom"
|
|
||||||
last = "Preston-Werner"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Roundtrip: table section
|
## Roundtrip: table section
|
||||||
@ -374,10 +372,7 @@ dob = 1979-05-27T07:32:00-08:00
|
|||||||
enabled = true
|
enabled = true
|
||||||
ports = [8000, 8001, 8002]
|
ports = [8000, 8001, 8002]
|
||||||
data = [["delta", "phi"], [3.14]]
|
data = [["delta", "phi"], [3.14]]
|
||||||
|
temp_targets = { cpu = 79.5, case = 72.0 }
|
||||||
[database.temp_targets]
|
|
||||||
cpu = 79.5
|
|
||||||
case = 72.0
|
|
||||||
|
|
||||||
# [servers] yq can't do this one yet
|
# [servers] yq can't do this one yet
|
||||||
[servers.alpha]
|
[servers.alpha]
|
||||||
|
|||||||
@ -162,10 +162,9 @@ func (te *tomlEncoder) encodeTopLevelEntry(w io.Writer, path []string, node *Can
|
|||||||
// Regular array attribute
|
// Regular array attribute
|
||||||
return te.writeArrayAttribute(w, path[len(path)-1], node)
|
return te.writeArrayAttribute(w, path[len(path)-1], node)
|
||||||
case MappingNode:
|
case MappingNode:
|
||||||
// Use inline table syntax only for nodes explicitly marked with flow/inline style
|
// Use inline table syntax for nodes explicitly marked as TOML inline tables
|
||||||
// (e.g. TOML inline tables or YAML flow mappings). All other mappings become
|
// or YAML flow mappings. All other mappings become readable TOML table sections.
|
||||||
// readable TOML table sections.
|
if node.TomlInline || node.Style&FlowStyle != 0 {
|
||||||
if node.Style&FlowStyle != 0 {
|
|
||||||
return te.writeInlineTableAttribute(w, path[len(path)-1], node)
|
return te.writeInlineTableAttribute(w, path[len(path)-1], node)
|
||||||
}
|
}
|
||||||
return te.encodeSeparateMapping(w, path, node)
|
return te.encodeSeparateMapping(w, path, node)
|
||||||
@ -428,7 +427,7 @@ func (te *tomlEncoder) writeTableHeader(w io.Writer, path []string, m *Candidate
|
|||||||
// It emits the table header for this mapping if it has any content, then processes children.
|
// It emits the table header for this mapping if it has any content, then processes children.
|
||||||
func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *CandidateNode) error {
|
func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *CandidateNode) error {
|
||||||
// Check if this mapping has any non-mapping, non-array-of-tables children (i.e., attributes).
|
// Check if this mapping has any non-mapping, non-array-of-tables children (i.e., attributes).
|
||||||
// Flow-style (inline) mapping children also count as attributes since they render as key = { ... }.
|
// TomlInline mapping children also count as attributes since they render as key = { ... }.
|
||||||
hasAttrs := false
|
hasAttrs := false
|
||||||
for i := 0; i < len(m.Content); i += 2 {
|
for i := 0; i < len(m.Content); i += 2 {
|
||||||
v := m.Content[i+1]
|
v := m.Content[i+1]
|
||||||
@ -436,7 +435,7 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
|
|||||||
hasAttrs = true
|
hasAttrs = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if v.Kind == MappingNode && v.Style&FlowStyle != 0 {
|
if v.Kind == MappingNode && (v.TomlInline || v.Style&FlowStyle != 0) {
|
||||||
hasAttrs = true
|
hasAttrs = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -598,13 +597,13 @@ func (te *tomlEncoder) encodeMappingBodyWithPath(w io.Writer, path []string, m *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, child mappings: flow-style (inline) ones become inline table attributes,
|
// Finally, child mappings: TomlInline or flow-style ones become inline table attributes,
|
||||||
// while all others are emitted as separate sub-table sections.
|
// while all others are emitted as separate sub-table sections.
|
||||||
for i := 0; i < len(m.Content); i += 2 {
|
for i := 0; i < len(m.Content); i += 2 {
|
||||||
k := m.Content[i].Value
|
k := m.Content[i].Value
|
||||||
v := m.Content[i+1]
|
v := m.Content[i+1]
|
||||||
if v.Kind == MappingNode {
|
if v.Kind == MappingNode {
|
||||||
if v.Style&FlowStyle != 0 {
|
if v.TomlInline || v.Style&FlowStyle != 0 {
|
||||||
if err := te.writeInlineTableAttribute(w, k, v); err != nil {
|
if err := te.writeInlineTableAttribute(w, k, v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,12 +182,6 @@ var expectedSampleWithHeader = `servers:
|
|||||||
var rtInlineTableAttr = `name = { first = "Tom", last = "Preston-Werner" }
|
var rtInlineTableAttr = `name = { first = "Tom", last = "Preston-Werner" }
|
||||||
`
|
`
|
||||||
|
|
||||||
// Inline tables are converted to readable table sections on encode
|
|
||||||
var rtInlineTableAttrEncoded = `[name]
|
|
||||||
first = "Tom"
|
|
||||||
last = "Preston-Werner"
|
|
||||||
`
|
|
||||||
|
|
||||||
var rtTableSection = `[owner.contact]
|
var rtTableSection = `[owner.contact]
|
||||||
name = "Tom"
|
name = "Tom"
|
||||||
age = 36
|
age = 36
|
||||||
@ -269,33 +263,6 @@ ip = "10.0.0.2"
|
|||||||
role = "backend"
|
role = "backend"
|
||||||
`
|
`
|
||||||
|
|
||||||
// Inline table temp_targets is expanded to a readable sub-table when re-encoding
|
|
||||||
var sampleFromWebEncoded = `# This is a TOML document
|
|
||||||
title = "TOML Example"
|
|
||||||
|
|
||||||
[owner]
|
|
||||||
name = "Tom Preston-Werner"
|
|
||||||
dob = 1979-05-27T07:32:00-08:00
|
|
||||||
|
|
||||||
[database]
|
|
||||||
enabled = true
|
|
||||||
ports = [8000, 8001, 8002]
|
|
||||||
data = [["delta", "phi"], [3.14]]
|
|
||||||
|
|
||||||
[database.temp_targets]
|
|
||||||
cpu = 79.5
|
|
||||||
case = 72.0
|
|
||||||
|
|
||||||
# [servers] yq can't do this one yet
|
|
||||||
[servers.alpha]
|
|
||||||
ip = "10.0.0.1"
|
|
||||||
role = "frontend"
|
|
||||||
|
|
||||||
[servers.beta]
|
|
||||||
ip = "10.0.0.2"
|
|
||||||
role = "backend"
|
|
||||||
`
|
|
||||||
|
|
||||||
var subArrays = `
|
var subArrays = `
|
||||||
[[array]]
|
[[array]]
|
||||||
|
|
||||||
@ -539,7 +506,7 @@ var tomlScenarios = []formatScenario{
|
|||||||
description: "Roundtrip: inline table attribute",
|
description: "Roundtrip: inline table attribute",
|
||||||
input: rtInlineTableAttr,
|
input: rtInlineTableAttr,
|
||||||
expression: ".",
|
expression: ".",
|
||||||
expected: rtInlineTableAttrEncoded,
|
expected: rtInlineTableAttr,
|
||||||
scenarioType: "roundtrip",
|
scenarioType: "roundtrip",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -638,7 +605,7 @@ var tomlScenarios = []formatScenario{
|
|||||||
description: "Roundtrip: sample from web",
|
description: "Roundtrip: sample from web",
|
||||||
input: sampleFromWeb,
|
input: sampleFromWeb,
|
||||||
expression: ".",
|
expression: ".",
|
||||||
expected: sampleFromWebEncoded,
|
expected: sampleFromWeb,
|
||||||
scenarioType: "roundtrip",
|
scenarioType: "roundtrip",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user