yaml aliases!

This commit is contained in:
Mike Farah 2023-04-15 14:24:39 +10:00
parent 4eca302efc
commit 4ef84278a3
6 changed files with 232 additions and 538 deletions

View File

@ -42,15 +42,26 @@ func MapToYamlStyle(original Style) yaml.Style {
return 0 return 0
} }
func (o *CandidateNode) copyFromYamlNode(node *yaml.Node) { func (o *CandidateNode) copyFromYamlNode(node *yaml.Node, anchorMap map[string]*CandidateNode) {
o.Style = MapYamlStyle(node.Style) o.Style = MapYamlStyle(node.Style)
o.Tag = node.Tag o.Tag = node.Tag
o.Value = node.Value o.Value = node.Value
o.Anchor = node.Anchor o.Anchor = node.Anchor
if o.Anchor != "" {
anchorMap[o.Anchor] = o
log.Debug("set anchor %v to %v", o.Anchor, NodeToString(o))
}
// o.Alias = TODO - find Alias in our own structure // o.Alias = TODO - find Alias in our own structure
// might need to be a post process thing // might need to be a post process thing
if node.Alias != nil && node.Alias.Value != "" {
o.Alias = anchorMap[node.Alias.Anchor]
log.Debug("set alias to %v", NodeToString(anchorMap[node.Alias.Anchor]))
}
o.HeadComment = node.HeadComment o.HeadComment = node.HeadComment
o.LineComment = node.LineComment o.LineComment = node.LineComment
o.FootComment = node.FootComment o.FootComment = node.FootComment
@ -78,33 +89,34 @@ func (o *CandidateNode) copyToYamlNode(node *yaml.Node) {
node.Column = o.Column node.Column = o.Column
} }
func (o *CandidateNode) decodeIntoChild(childNode *yaml.Node) (*CandidateNode, error) { func (o *CandidateNode) decodeIntoChild(childNode *yaml.Node, anchorMap map[string]*CandidateNode) (*CandidateNode, error) {
newChild := o.CreateChild() newChild := o.CreateChild()
// null yaml.Nodes to not end up calling UnmarshalYAML // null yaml.Nodes to not end up calling UnmarshalYAML
// so we call it explicitly // so we call it explicitly
if childNode.Tag == "!!null" { if childNode.Tag == "!!null" {
newChild.Kind = ScalarNode newChild.Kind = ScalarNode
newChild.copyFromYamlNode(childNode) newChild.copyFromYamlNode(childNode, anchorMap)
return newChild, nil return newChild, nil
} }
err := newChild.UnmarshalYAML(childNode) err := newChild.UnmarshalYAML(childNode, anchorMap)
return newChild, err return newChild, err
} }
func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error { func (o *CandidateNode) UnmarshalYAML(node *yaml.Node, anchorMap map[string]*CandidateNode) error {
log.Debugf("UnmarshalYAML %v", node.Tag) log.Debugf("UnmarshalYAML %v", node.Tag)
switch node.Kind { switch node.Kind {
case yaml.DocumentNode: case yaml.DocumentNode:
log.Debugf("UnmarshalYAML - a document") log.Debugf("UnmarshalYAML - a document")
o.Kind = DocumentNode o.Kind = DocumentNode
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
if len(node.Content) == 0 { if len(node.Content) == 0 {
return nil return nil
} }
singleChild, err := o.decodeIntoChild(node.Content[0]) singleChild, err := o.decodeIntoChild(node.Content[0], anchorMap)
if err != nil { if err != nil {
return err return err
@ -115,28 +127,28 @@ func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error {
case yaml.AliasNode: case yaml.AliasNode:
log.Debug("UnmarshalYAML - alias from yaml: %v", o.Tag) log.Debug("UnmarshalYAML - alias from yaml: %v", o.Tag)
o.Kind = AliasNode o.Kind = AliasNode
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
return nil return nil
case yaml.ScalarNode: case yaml.ScalarNode:
log.Debugf("UnmarshalYAML - a scalar") log.Debugf("UnmarshalYAML - a scalar")
o.Kind = ScalarNode o.Kind = ScalarNode
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
return nil return nil
case yaml.MappingNode: case yaml.MappingNode:
log.Debugf("UnmarshalYAML - a mapping node") log.Debugf("UnmarshalYAML - a mapping node")
o.Kind = MappingNode o.Kind = MappingNode
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
o.Content = make([]*CandidateNode, len(node.Content)) o.Content = make([]*CandidateNode, len(node.Content))
for i := 0; i < len(node.Content); i += 2 { for i := 0; i < len(node.Content); i += 2 {
keyNode, err := o.decodeIntoChild(node.Content[i]) keyNode, err := o.decodeIntoChild(node.Content[i], anchorMap)
if err != nil { if err != nil {
return err return err
} }
keyNode.IsMapKey = true keyNode.IsMapKey = true
valueNode, err := o.decodeIntoChild(node.Content[i+1]) valueNode, err := o.decodeIntoChild(node.Content[i+1], anchorMap)
if err != nil { if err != nil {
return err return err
} }
@ -151,7 +163,7 @@ func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error {
case yaml.SequenceNode: case yaml.SequenceNode:
log.Debugf("UnmarshalYAML - a sequence: %v", len(node.Content)) log.Debugf("UnmarshalYAML - a sequence: %v", len(node.Content))
o.Kind = SequenceNode o.Kind = SequenceNode
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
o.Content = make([]*CandidateNode, len(node.Content)) o.Content = make([]*CandidateNode, len(node.Content))
for i := 0; i < len(node.Content); i += 1 { for i := 0; i < len(node.Content); i += 1 {
keyNode := o.CreateChild() keyNode := o.CreateChild()
@ -160,7 +172,7 @@ func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error {
keyNode.Kind = ScalarNode keyNode.Kind = ScalarNode
keyNode.Value = fmt.Sprintf("%v", i) keyNode.Value = fmt.Sprintf("%v", i)
valueNode, err := o.decodeIntoChild(node.Content[i]) valueNode, err := o.decodeIntoChild(node.Content[i], anchorMap)
if err != nil { if err != nil {
return err return err
} }
@ -171,7 +183,7 @@ func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error {
return nil return nil
case 0: case 0:
// not sure when this happens // not sure when this happens
o.copyFromYamlNode(node) o.copyFromYamlNode(node, anchorMap)
log.Debugf("UnmarshalYAML - errr.. %v", NodeToString(o)) log.Debugf("UnmarshalYAML - errr.. %v", NodeToString(o))
return nil return nil
default: default:

View File

@ -118,7 +118,7 @@ func (dec *yamlDecoder) Decode() (*CandidateNode, error) {
} }
candidateNode := CandidateNode{} candidateNode := CandidateNode{}
candidateNode.UnmarshalYAML(&yamlNode) candidateNode.UnmarshalYAML(&yamlNode, make(map[string]*CandidateNode))
if dec.leadingContent != "" { if dec.leadingContent != "" {
candidateNode.LeadingContent = dec.leadingContent candidateNode.LeadingContent = dec.leadingContent

View File

@ -5,329 +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
```
## Dereference and update a field
Use explode with multiply to dereference an object
Given a sample.yml file of:
```yaml
item_value: &item_value
value: true
thingOne:
name: item_1
!!merge <<: *item_value
thingTwo:
name: item_2
!!merge <<: *item_value
```
then
```bash
yq '.thingOne |= explode(.) * {"value": false}' sample.yml
```
will output
```yaml
item_value: &item_value
value: true
thingOne:
name: item_1
value: false
thingTwo:
name: item_2
!!merge <<: *item_value
```

View File

@ -3,8 +3,6 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -240,9 +238,11 @@ func explodeNode(node *CandidateNode, context Context) error {
} }
func applyAlias(node *CandidateNode, alias *CandidateNode, aliasIndex int, newContent Context) error { func applyAlias(node *CandidateNode, alias *CandidateNode, aliasIndex int, newContent Context) error {
log.Debug("alias is nil ?")
if alias == nil { if alias == nil {
return nil return nil
} }
log.Debug("alias: %v", NodeToString(alias))
if alias.Kind != MappingNode { if alias.Kind != MappingNode {
return fmt.Errorf("merge anchor only supports maps, got %v instead", alias.Tag) return fmt.Errorf("merge anchor only supports maps, got %v instead", alias.Tag)
} }
@ -268,8 +268,8 @@ func overrideEntry(node *CandidateNode, key *CandidateNode, value *CandidateNode
for newEl := newContent.MatchingNodes.Front(); newEl != nil; newEl = newEl.Next() { for newEl := newContent.MatchingNodes.Front(); newEl != nil; newEl = newEl.Next() {
valueEl := newEl.Next() // move forward twice valueEl := newEl.Next() // move forward twice
keyNode := newEl.Value.(*yaml.Node) keyNode := newEl.Value.(*CandidateNode)
log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value) log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*CandidateNode).Value)
if keyNode.Value == key.Value && keyNode.Alias == nil && key.Alias == nil { if keyNode.Value == key.Value && keyNode.Alias == nil && key.Alias == nil {
log.Debugf("overridign new content") log.Debugf("overridign new content")
valueEl.Value = value valueEl.Value = value

View File

@ -42,197 +42,205 @@ var anchorOperatorScenarios = []expressionScenario{
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: //
b: bar_b // bar:
thing: bar_thing //
c: bar_c // b: bar_b
foobarList: // thing: bar_thing
b: bar_b // c: bar_c
thing: foo_thing //
c: foobarList_c // foobarList:
a: foo_a //
foobar: // b: bar_b
c: foo_c // thing: foo_thing
a: foo_a // c: foobarList_c
thing: foobar_thing // a: foo_a
`}, //
}, // foobar:
{ //
skipDoc: true, // c: foo_c
document: mergeDocSample, // a: foo_a
expression: `.foo* | explode(.) | (. style="flow")`, // thing: foobar_thing
expected: []string{ //
"D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", // `},
"D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n", //
"D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", // },
}, // {
}, // skipDoc: true,
{ // document: mergeDocSample,
skipDoc: true, // expression: `.foo* | explode(.) | (. style="flow")`,
document: mergeDocSample, // expected: []string{
expression: `.foo* | explode(explode(.)) | (. style="flow")`, // "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n",
expected: []string{ // "D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n",
"D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", // "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n",
"D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n", // },
"D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", // },
}, // {
}, // skipDoc: true,
{ // document: mergeDocSample,
skipDoc: true, // expression: `.foo* | explode(explode(.)) | (. style="flow")`,
document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`, // expected: []string{
expression: `explode(.f)`, // "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n",
expected: []string{ // "D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n",
"D0, P[], (doc)::{f: {a: cat, b: {f: cat}, cat: {f: cat}}}\n", // "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n",
}, // },
}, // },
{ // {
description: "Dereference and update a field", // skipDoc: true,
subdescription: "Use explode with multiply to dereference an object", // document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`,
document: simpleArrayRef, // expression: `explode(.f)`,
expression: `.thingOne |= explode(.) * {"value": false}`, // expected: []string{
expected: []string{expectedUpdatedArrayRef}, // "D0, P[], (doc)::{f: {a: cat, b: {f: cat}, cat: {f: cat}}}\n",
}, // },
// },
// {
// description: "Dereference and update a field",
// subdescription: "Use explode with multiply to dereference an object",
// document: simpleArrayRef,
// expression: `.thingOne |= explode(.) * {"value": false}`,
// expected: []string{expectedUpdatedArrayRef},
// },
} }
func TestAnchorAliasOperatorScenarios(t *testing.T) { func TestAnchorAliasOperatorScenarios(t *testing.T) {

View File

@ -66,7 +66,7 @@ var yamlParseScenarios = []expressionScenario{
}, },
{ {
document: "a: &a apple\nb: *a", document: "a: &a apple\nb: *a",
expression: ".b", expression: ".b | explode(.)",
expected: []string{ expected: []string{
"D0, P[b], (!!str)::apple\n", "D0, P[b], (!!str)::apple\n",
}, },