diff --git a/pkg/yqlib/treeops/operator_explode.go b/pkg/yqlib/treeops/operator_explode.go index 77ad39d6..de3a73bd 100644 --- a/pkg/yqlib/treeops/operator_explode.go +++ b/pkg/yqlib/treeops/operator_explode.go @@ -50,35 +50,35 @@ func explodeNode(node *yaml.Node) error { } return nil case yaml.MappingNode: + var newContent = list.New() for index := 0; index < len(node.Content); index = index + 2 { keyNode := node.Content[index] valueNode := node.Content[index+1] log.Debugf("traversing %v", keyNode.Value) if keyNode.Value != "<<" { - errorInContent := explodeNode(valueNode) - if errorInContent != nil { - return errorInContent - } - errorInContent = explodeNode(keyNode) - if errorInContent != nil { - return errorInContent + err := overrideEntry(node, keyNode, valueNode, index, newContent) + if err != nil { + return err } } else { if valueNode.Kind == yaml.SequenceNode { log.Debugf("an alias merge list!") - for index := len(valueNode.Content) - 1; index >= 0; index = index - 1 { + for index := 0; index < len(valueNode.Content); index = index + 1 { aliasNode := valueNode.Content[index] - applyAlias(node, aliasNode.Alias) + applyAlias(node, aliasNode.Alias, index, newContent) } } else { log.Debugf("an alias merge!") - applyAlias(node, valueNode.Alias) + applyAlias(node, valueNode.Alias, index, newContent) } - node.Content = append(node.Content[:index], node.Content[index+2:]...) - //replay that index, since the array is shorter now. - index = index - 2 } } + node.Content = make([]*yaml.Node, newContent.Len()) + index := 0 + for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { + node.Content[index] = newEl.Value.(*yaml.Node) + index++ + } return nil default: @@ -86,27 +86,57 @@ func explodeNode(node *yaml.Node) error { } } -func applyAlias(node *yaml.Node, alias *yaml.Node) { +func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *list.List) error { if alias == nil { - return + return nil } for index := 0; index < len(alias.Content); index = index + 2 { keyNode := alias.Content[index] log.Debugf("applying alias key %v", keyNode.Value) valueNode := alias.Content[index+1] - setIfNotThere(node, keyNode.Value, valueNode) - } -} - -func setIfNotThere(node *yaml.Node, key string, value *yaml.Node) { - for index := 0; index < len(node.Content); index = index + 2 { - keyNode := node.Content[index] - if keyNode.Value == key { - return + err := overrideEntry(node, keyNode, valueNode, aliasIndex, newContent) + if err != nil { + return err } } - // need to add it to the map - mapEntryKey := yaml.Node{Value: key, Kind: yaml.ScalarNode} - node.Content = append(node.Content, &mapEntryKey) - node.Content = append(node.Content, value) + return nil +} + +func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent *list.List) error { + + err := explodeNode(value) + + if err != nil { + return err + } + + for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { + valueEl := newEl.Next() // move forward twice + keyNode := newEl.Value.(*yaml.Node) + log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value) + if keyNode.Value == key.Value && keyNode.Alias == nil && key.Alias == nil { + log.Debugf("overridign new content") + valueEl.Value = value + return nil + } + newEl = valueEl // move forward twice + } + + for index := startIndex + 2; index < len(node.Content); index = index + 2 { + keyNode := node.Content[index] + + if keyNode.Value == key.Value && keyNode.Alias == nil { + log.Debugf("content will be overridden at index %v", index) + return nil + } + } + + err = explodeNode(key) + if err != nil { + return err + } + log.Debugf("adding %v:%v", key.Value, value.Value) + newContent.PushBack(key) + newContent.PushBack(value) + return nil } diff --git a/pkg/yqlib/treeops/operator_explode_test.go b/pkg/yqlib/treeops/operator_explode_test.go index ed283636..b9f79cc5 100644 --- a/pkg/yqlib/treeops/operator_explode_test.go +++ b/pkg/yqlib/treeops/operator_explode_test.go @@ -4,6 +4,8 @@ import ( "testing" ) +//nested alias + var explodeTest = []expressionScenario{ { document: `{a: mike}`, @@ -20,10 +22,26 @@ var explodeTest = []expressionScenario{ }, }, { - document: mergeDocSample, - expression: `.foo* | explode(.)`, + document: `{f : {a: &a cat, *a: b}}`, + expression: `explode(.f)`, expected: []string{ - "D0, P[], (doc)::{f: {a: cat, b: cat}}\n", + "D0, P[], (doc)::{f: {a: cat, cat: b}}\n", + }, + }, + { + document: mergeDocSample, + expression: `.foo* | explode(.) | (. style="flow")`, + expected: []string{ + "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", + "D0, P[foobarList], (!!map)::{b: bar_b, a: foo_a, thing: bar_thing, c: foobarList_c}\n", + "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", + }, + }, + { + document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`, + expression: `explode(.f)`, + expected: []string{ + "D0, P[], (doc)::{f: {a: cat, b: {f: cat}, cat: {f: cat}}}\n", }, }, } diff --git a/pkg/yqlib/treeops/path_parse_test.go b/pkg/yqlib/treeops/path_parse_test.go index 3bd3ee68..1142f4bf 100644 --- a/pkg/yqlib/treeops/path_parse_test.go +++ b/pkg/yqlib/treeops/path_parse_test.go @@ -109,6 +109,11 @@ var pathTests = []struct { append(make([]interface{}, 0), " (string)"), append(make([]interface{}, 0), " (string)"), }, + { + `.foo* | (. style="flow")`, + append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"), + append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"), + }, // {".animals | .==cat", append(make([]interface{}, 0), "animals", "TRAVERSE", "SELF", "EQUALS", "cat")}, // {".animals | (. == cat)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "cat", ")")}, diff --git a/pkg/yqlib/treeops/path_tree_test.go b/pkg/yqlib/treeops/path_tree_test.go deleted file mode 100644 index 005dee7e..00000000 --- a/pkg/yqlib/treeops/path_tree_test.go +++ /dev/null @@ -1 +0,0 @@ -package treeops