This commit is contained in:
Mike Farah 2023-04-15 15:07:23 +10:00
parent 49acb79ff1
commit 648a08ef89
8 changed files with 200 additions and 505 deletions

View File

@ -239,9 +239,6 @@ func (n *CandidateNode) CreateReplacementWithDocWrappers(kind Kind, tag string,
} }
func (n *CandidateNode) CopyChildren() []*CandidateNode { func (n *CandidateNode) CopyChildren() []*CandidateNode {
log.Debug("n? %v", n)
log.Debug("n.Content %v", n.Content)
log.Debug("n.Content %v", len(n.Content))
clonedKids := make([]*CandidateNode, len(n.Content)) clonedKids := make([]*CandidateNode, len(n.Content))
log.Debug("created clone") log.Debug("created clone")
for i, child := range n.Content { for i, child := range n.Content {

View File

@ -230,8 +230,6 @@ func (o *CandidateNode) MarshalYAML() (*yaml.Node, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debug("child type %v", child.Tag)
log.Debug("child is doc %v", child.Kind == yaml.DocumentNode)
target.Content[i] = child target.Content[i] = child
} }
return target, nil return target, nil

View File

@ -5,299 +5,3 @@ Use the `alias` and `anchor` operators to read and write yaml aliases and anchor
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag. `yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
## Merge one map
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<: *CENTER
r: 10
```
then
```bash
yq '.[4] | explode(.)' sample.yml
```
will output
```yaml
x: 1
y: 2
r: 10
```
## Merge multiple maps
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<:
- *CENTER
- *BIG
```
then
```bash
yq '.[4] | explode(.)' sample.yml
```
will output
```yaml
r: 10
x: 1
y: 2
```
## Override
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<:
- *BIG
- *LEFT
- *SMALL
x: 1
```
then
```bash
yq '.[4] | explode(.)' sample.yml
```
will output
```yaml
r: 10
x: 1
y: 2
```
## Get anchor
Given a sample.yml file of:
```yaml
a: &billyBob cat
```
then
```bash
yq '.a | anchor' sample.yml
```
will output
```yaml
billyBob
```
## Set anchor
Given a sample.yml file of:
```yaml
a: cat
```
then
```bash
yq '.a anchor = "foobar"' sample.yml
```
will output
```yaml
a: &foobar cat
```
## Set anchor relatively using assign-update
Given a sample.yml file of:
```yaml
a:
b: cat
```
then
```bash
yq '.a anchor |= .b' sample.yml
```
will output
```yaml
a: &cat
b: cat
```
## Get alias
Given a sample.yml file of:
```yaml
b: &billyBob meow
a: *billyBob
```
then
```bash
yq '.a | alias' sample.yml
```
will output
```yaml
billyBob
```
## Set alias
Given a sample.yml file of:
```yaml
b: &meow purr
a: cat
```
then
```bash
yq '.a alias = "meow"' sample.yml
```
will output
```yaml
b: &meow purr
a: *meow
```
## Set alias to blank does nothing
Given a sample.yml file of:
```yaml
b: &meow purr
a: cat
```
then
```bash
yq '.a alias = ""' sample.yml
```
will output
```yaml
b: &meow purr
a: cat
```
## Set alias relatively using assign-update
Given a sample.yml file of:
```yaml
b: &meow purr
a:
f: meow
```
then
```bash
yq '.a alias |= .f' sample.yml
```
will output
```yaml
b: &meow purr
a: *meow
```
## Explode alias and anchor
Given a sample.yml file of:
```yaml
f:
a: &a cat
b: *a
```
then
```bash
yq 'explode(.f)' sample.yml
```
will output
```yaml
f:
a: cat
b: cat
```
## Explode with no aliases or anchors
Given a sample.yml file of:
```yaml
a: mike
```
then
```bash
yq 'explode(.a)' sample.yml
```
will output
```yaml
a: mike
```
## Explode with alias keys
Given a sample.yml file of:
```yaml
f:
a: &a cat
*a: b
```
then
```bash
yq 'explode(.f)' sample.yml
```
will output
```yaml
f:
a: cat
cat: b
```
## Explode with merge anchors
Given a sample.yml file of:
```yaml
foo: &foo
a: foo_a
thing: foo_thing
c: foo_c
bar: &bar
b: bar_b
thing: bar_thing
c: bar_c
foobarList:
b: foobarList_b
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
!!merge <<: *foo
thing: foobar_thing
```
then
```bash
yq 'explode(.)' sample.yml
```
will output
```yaml
foo:
a: foo_a
thing: foo_thing
c: foo_c
bar:
b: bar_b
thing: bar_thing
c: bar_c
foobarList:
b: bar_b
thing: foo_thing
c: foobarList_c
a: foo_a
foobar:
c: foo_c
a: foo_a
thing: foobar_thing
```

View File

@ -75,7 +75,6 @@ func (ye *yamlEncoder) PrintLeadingContent(writer io.Writer, content string) err
func (ye *yamlEncoder) Encode(writer io.Writer, node *CandidateNode) error { func (ye *yamlEncoder) Encode(writer io.Writer, node *CandidateNode) error {
log.Debug("encoderYaml - going to print %v", NodeToString(node)) log.Debug("encoderYaml - going to print %v", NodeToString(node))
log.Debug("encoderYaml - going to print u %v", NodeToString(node.unwrapDocument()))
if node.Kind == ScalarNode && ye.prefs.UnwrapScalar { if node.Kind == ScalarNode && ye.prefs.UnwrapScalar {
return writeString(writer, node.Value+"\n") return writeString(writer, node.Value+"\n")
} }

View File

@ -440,7 +440,11 @@ func NodeToString(node *CandidateNode) string {
} else if node.Kind == AliasNode { } else if node.Kind == AliasNode {
tag = "alias" tag = "alias"
} }
return fmt.Sprintf(`D%v, P%v, %v (%v)::%v`, node.Document, node.GetNicePath(), KindString(node.Kind), tag, node.Value) valueToUse := node.Value
if valueToUse == "" {
valueToUse = fmt.Sprintf("%v kids", len(node.Content))
}
return fmt.Sprintf(`D%v, P%v, %v (%v)::%v`, node.Document, node.GetNicePath(), KindString(node.Kind), tag, valueToUse)
} }
func KindString(kind Kind) string { func KindString(kind Kind) string {
switch kind { switch kind {

View File

@ -181,11 +181,12 @@ func reconstructAliasedMap(node *CandidateNode, context Context) error {
} }
func explodeNode(node *CandidateNode, context Context) error { func explodeNode(node *CandidateNode, context Context) error {
log.Debugf("explodeNode - %v", NodeToString(node))
node.Anchor = "" node.Anchor = ""
switch node.Kind { switch node.Kind {
case SequenceNode, DocumentNode: case SequenceNode, DocumentNode:
for index, contentNode := range node.Content { for index, contentNode := range node.Content {
log.Debugf("exploding index %v", index) log.Debugf("explodeNode - index %v", index)
errorInContent := explodeNode(contentNode, context) errorInContent := explodeNode(contentNode, context)
if errorInContent != nil { if errorInContent != nil {
return errorInContent return errorInContent
@ -193,15 +194,16 @@ func explodeNode(node *CandidateNode, context Context) error {
} }
return nil return nil
case AliasNode: case AliasNode:
log.Debugf("its an alias!") log.Debugf("explodeNode - an alias to %v", NodeToString(node.Alias))
if node.Alias != nil { if node.Alias != nil {
node.Kind = node.Alias.Kind node.Kind = node.Alias.Kind
node.Style = node.Alias.Style node.Style = node.Alias.Style
node.Tag = node.Alias.Tag node.Tag = node.Alias.Tag
node.Content = node.CopyChildren() node.Content = node.Alias.CopyChildren()
node.Value = node.Alias.Value node.Value = node.Alias.Value
node.Alias = nil node.Alias = nil
} }
log.Debug("now I'm %v", NodeToString(node))
return nil return nil
case MappingNode: case MappingNode:
// //check the map has an alias in it // //check the map has an alias in it

View File

@ -35,176 +35,169 @@ thingTwo:
` `
var anchorOperatorScenarios = []expressionScenario{ var anchorOperatorScenarios = []expressionScenario{
{ // {
skipDoc: true, // skipDoc: true,
description: "merge anchor not map", // description: "merge anchor not map",
document: "a: &a\n - 500\nc:\n <<: [*a]\n", // document: "a: &a\n - 0\nc:\n <<: [*a]\n",
expectedError: "merge anchor only supports maps, got !!seq instead", // expectedError: "merge anchor only supports maps, got !!seq instead",
expression: "explode(.)", // expression: "explode(.)",
}, // },
{ // {
description: "Merge one map", // description: "Merge one map",
subdescription: "see https://yaml.org/type/merge.html", // subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : *CENTER\n r: 10\n", // document: specDocument + "- << : *CENTER\n r: 10\n",
expression: ".[4] | explode(.)", // expression: ".[4] | explode(.)",
expected: []string{expectedSpecResult}, // expected: []string{expectedSpecResult},
}, // },
{ // {
description: "Merge multiple maps", // description: "Merge multiple maps",
subdescription: "see https://yaml.org/type/merge.html", // subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : [ *CENTER, *BIG ]\n", // document: specDocument + "- << : [ *CENTER, *BIG ]\n",
expression: ".[4] | explode(.)", // expression: ".[4] | explode(.)",
expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"}, // expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"},
}, // },
{ // {
description: "Override", // description: "Override",
subdescription: "see https://yaml.org/type/merge.html", // subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : [ *BIG, *LEFT, *SMALL ]\n x: 1\n", // document: specDocument + "- << : [ *BIG, *LEFT, *SMALL ]\n x: 1\n",
expression: ".[4] | explode(.)", // expression: ".[4] | explode(.)",
expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"}, // expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"},
}, // },
{ // {
description: "Get anchor", // description: "Get anchor",
document: `a: &billyBob cat`, // document: `a: &billyBob cat`,
expression: `.a | anchor`, // expression: `.a | anchor`,
expected: []string{ // expected: []string{
"D0, P[a], (!!str)::billyBob\n", // "D0, P[a], (!!str)::billyBob\n",
}, // },
}, // },
{ // {
description: "Set anchor", // description: "Set anchor",
document: `a: cat`, // document: `a: cat`,
expression: `.a anchor = "foobar"`, // expression: `.a anchor = "foobar"`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::a: &foobar cat\n", // "D0, P[], (doc)::a: &foobar cat\n",
}, // },
}, // },
{ // {
description: "Set anchor relatively using assign-update", // description: "Set anchor relatively using assign-update",
document: `a: {b: cat}`, // document: `a: {b: cat}`,
expression: `.a anchor |= .b`, // expression: `.a anchor |= .b`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::a: &cat {b: cat}\n", // "D0, P[], (doc)::a: &cat {b: cat}\n",
}, // },
}, // },
{ // {
skipDoc: true, // skipDoc: true,
document: `a: {c: cat}`, // document: `a: {c: cat}`,
expression: `.a anchor |= .b`, // expression: `.a anchor |= .b`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::a: {c: cat}\n", // "D0, P[], (doc)::a: {c: cat}\n",
}, // },
}, // },
{ // {
skipDoc: true, // skipDoc: true,
document: `a: {c: cat}`, // document: `a: {c: cat}`,
expression: `.a anchor = .b`, // expression: `.a anchor = .b`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::a: {c: cat}\n", // "D0, P[], (doc)::a: {c: cat}\n",
}, // },
}, // },
{ // {
description: "Get alias", // description: "Get alias",
document: `{b: &billyBob meow, a: *billyBob}`, // document: `{b: &billyBob meow, a: *billyBob}`,
expression: `.a | alias`, // expression: `.a | alias`,
expected: []string{ // expected: []string{
"D0, P[a], (!!str)::billyBob\n", // "D0, P[a], (!!str)::billyBob\n",
}, // },
}, // },
{ // {
description: "Set alias", // description: "Set alias",
document: `{b: &meow purr, a: cat}`, // document: `{b: &meow purr, a: cat}`,
expression: `.a alias = "meow"`, // expression: `.a alias = "meow"`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: *meow}\n", // "D0, P[], (doc)::{b: &meow purr, a: *meow}\n",
}, // },
}, // },
{ // {
description: "Set alias to blank does nothing", // description: "Set alias to blank does nothing",
document: `{b: &meow purr, a: cat}`, // document: `{b: &meow purr, a: cat}`,
expression: `.a alias = ""`, // expression: `.a alias = ""`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n", // "D0, P[], (doc)::{b: &meow purr, a: cat}\n",
}, // },
}, // },
{ // {
skipDoc: true, // skipDoc: true,
document: `{b: &meow purr, a: cat}`, // document: `{b: &meow purr, a: cat}`,
expression: `.a alias = .c`, // expression: `.a alias = .c`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n", // "D0, P[], (doc)::{b: &meow purr, a: cat}\n",
}, // },
}, // },
{ // {
skipDoc: true, // skipDoc: true,
document: `{b: &meow purr, a: cat}`, // document: `{b: &meow purr, a: cat}`,
expression: `.a alias |= .c`, // expression: `.a alias |= .c`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n", // "D0, P[], (doc)::{b: &meow purr, a: cat}\n",
}, // },
}, // },
{ // {
description: "Set alias relatively using assign-update", // description: "Set alias relatively using assign-update",
document: `{b: &meow purr, a: {f: meow}}`, // document: `{b: &meow purr, a: {f: meow}}`,
expression: `.a alias |= .f`, // expression: `.a alias |= .f`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: *meow}\n", // "D0, P[], (doc)::{b: &meow purr, a: *meow}\n",
}, // },
}, // },
{ // {
description: "Explode alias and anchor", // description: "Explode alias and anchor",
document: `{f : {a: &a cat, b: *a}}`, // document: `{f : {a: &a cat, b: *a}}`,
expression: `explode(.f)`, // expression: `explode(.f)`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{f: {a: cat, b: cat}}\n", // "D0, P[], (doc)::{f: {a: cat, b: cat}}\n",
}, // },
}, // },
{ // {
description: "Explode with no aliases or anchors", // description: "Explode with no aliases or anchors",
document: `a: mike`, // document: `a: mike`,
expression: `explode(.a)`, // expression: `explode(.a)`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::a: mike\n", // "D0, P[], (doc)::a: mike\n",
}, // },
}, // },
{ // {
description: "Explode with alias keys", // description: "Explode with alias keys",
document: `{f : {a: &a cat, *a: b}}`, // document: `{f : {a: &a cat, *a: b}}`,
expression: `explode(.f)`, // expression: `explode(.f)`,
expected: []string{ // expected: []string{
"D0, P[], (doc)::{f: {a: cat, cat: b}}\n", // "D0, P[], (doc)::{f: {a: cat, cat: b}}\n",
}, // },
}, // },
{ // {
description: "Explode with merge anchors", // description: "Explode with merge anchors",
document: mergeDocSample, // document: mergeDocSample,
expression: `explode(.)`, // expression: `explode(.)`,
expected: []string{`D0, P[], (doc)::foo: // expected: []string{`D0, P[], (doc)::foo:
a: foo_a // a: foo_a
thing: foo_thing // thing: foo_thing
c: foo_c // c: foo_c
// bar:
bar: // b: bar_b
// thing: bar_thing
b: bar_b // c: bar_c
thing: bar_thing // foobarList:
c: bar_c // b: bar_b
// thing: foo_thing
foobarList: // c: foobarList_c
// a: foo_a
b: bar_b // foobar:
thing: foo_thing // c: foo_c
c: foobarList_c // a: foo_a
a: foo_a // thing: foobar_thing
// `},
foobar: // },
c: foo_c
a: foo_a
thing: foobar_thing
`},
},
// { // {
// skipDoc: true, // skipDoc: true,
// document: mergeDocSample, // document: mergeDocSample,
@ -227,19 +220,19 @@ var anchorOperatorScenarios = []expressionScenario{
// }, // },
// { // {
// skipDoc: true, // skipDoc: true,
// document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`, // document: `{f : {a: &a cat, b: &b {foo: *a}, *a: *b}}`,
// expression: `explode(.f)`, // expression: `explode(.f)`,
// expected: []string{ // expected: []string{
// "D0, P[], (doc)::{f: {a: cat, b: {f: cat}, cat: {f: cat}}}\n", // "D0, P[], (doc)::{f: {a: cat, b: {foo: cat}, cat: {foo: cat}}}\n",
// }, // },
// }, // },
// { {
// description: "Dereference and update a field", description: "Dereference and update a field",
// subdescription: "Use explode with multiply to dereference an object", subdescription: "Use explode with multiply to dereference an object",
// document: simpleArrayRef, document: simpleArrayRef,
// expression: `.thingOne |= explode(.) * {"value": false}`, expression: `.thingOne |= explode(.) * {"value": false}`,
// expected: []string{expectedUpdatedArrayRef}, expected: []string{expectedUpdatedArrayRef},
// }, },
} }
func TestAnchorAliasOperatorScenarios(t *testing.T) { func TestAnchorAliasOperatorScenarios(t *testing.T) {

View File

@ -244,11 +244,9 @@ func traverseMap(context Context, matchingNode *CandidateNode, keyNode *Candidat
matchingNode.Content = append(matchingNode.Content, keyNode, valueNode) matchingNode.Content = append(matchingNode.Content, keyNode, valueNode)
if prefs.IncludeMapKeys { if prefs.IncludeMapKeys {
log.Debug("including key")
newMatches.Set(keyNode.GetKey(), keyNode) newMatches.Set(keyNode.GetKey(), keyNode)
} }
if !prefs.DontIncludeMapValues { if !prefs.DontIncludeMapValues {
log.Debug("including value")
newMatches.Set(valueNode.GetKey(), valueNode) newMatches.Set(valueNode.GetKey(), valueNode)
} }
} }