diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index dd34a811..89c40645 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -21,7 +21,23 @@ func (n *CandidateNode) GetKey() string { return fmt.Sprintf("%v - %v", n.Document, n.Path) } -func (n *CandidateNode) CreateChildPath(path interface{}) []interface{} { +func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *CandidateNode { + return &CandidateNode{ + Node: node, + Path: n.createChildPath(path), + Document: n.Document, + Filename: n.Filename, + FileIndex: n.FileIndex, + } +} + +func (n *CandidateNode) createChildPath(path interface{}) []interface{} { + if path == nil { + newPath := make([]interface{}, len(n.Path)) + copy(newPath, n.Path) + return newPath + } + //don't use append as they may actually modify the path of the orignal node! newPath := make([]interface{}, len(n.Path)+1) copy(newPath, n.Path) diff --git a/pkg/yqlib/operator_add.go b/pkg/yqlib/operator_add.go index 227c837a..7e77a99e 100644 --- a/pkg/yqlib/operator_add.go +++ b/pkg/yqlib/operator_add.go @@ -47,12 +47,7 @@ func add(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*Candida lhs.Node = UnwrapDoc(lhs.Node) rhs.Node = UnwrapDoc(rhs.Node) - target := &CandidateNode{ - Path: lhs.Path, - Document: lhs.Document, - Filename: lhs.Filename, - Node: &yaml.Node{}, - } + target := lhs.CreateChild(nil, &yaml.Node{}) lhsNode := lhs.Node switch lhsNode.Kind { diff --git a/pkg/yqlib/operator_anchors_aliases.go b/pkg/yqlib/operator_anchors_aliases.go index 689371d3..9f15a161 100644 --- a/pkg/yqlib/operator_anchors_aliases.go +++ b/pkg/yqlib/operator_anchors_aliases.go @@ -54,8 +54,8 @@ func getAliasOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode * for el := matchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Node.Value, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil } @@ -110,8 +110,8 @@ func getAnchorOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode candidate := el.Value.(*CandidateNode) anchor := candidate.Node.Anchor node := &yaml.Node{Kind: yaml.ScalarNode, Value: anchor, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil } diff --git a/pkg/yqlib/operator_collect.go b/pkg/yqlib/operator_collect.go index fb9cf955..46ea70b5 100644 --- a/pkg/yqlib/operator_collect.go +++ b/pkg/yqlib/operator_collect.go @@ -18,21 +18,22 @@ func collectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTr var results = list.New() node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} - - var document uint = 0 - var path []interface{} + var collectC *CandidateNode + if matchMap.Front() != nil { + collectC = matchMap.Front().Value.(*CandidateNode).CreateChild(nil, node) + if len(collectC.Path) > 0 { + collectC.Path = collectC.Path[:len(collectC.Path)-1] + } + } else { + collectC = &CandidateNode{Node: node} + } for el := matchMap.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Collecting %v", NodeToString(candidate)) - if path == nil && candidate.Path != nil && len(candidate.Path) > 1 { - path = candidate.Path[:len(candidate.Path)-1] - document = candidate.Document - } node.Content = append(node.Content, UnwrapDoc(candidate.Node)) } - collectC := &CandidateNode{Node: node, Document: document, Path: path} results.PushBack(collectC) return results, nil diff --git a/pkg/yqlib/operator_collect_object.go b/pkg/yqlib/operator_collect_object.go index eee0f7e0..03fcd46b 100644 --- a/pkg/yqlib/operator_collect_object.go +++ b/pkg/yqlib/operator_collect_object.go @@ -35,7 +35,7 @@ func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode * for el := matchMap.Front(); el != nil; el = el.Next() { candidateNode := el.Value.(*CandidateNode) for i := 0; i < len(first.Node.Content); i++ { - rotated[i].PushBack(createChildCandidate(candidateNode, i)) + rotated[i].PushBack(candidateNode.CreateChild(i, candidateNode.Node.Content[i])) } } @@ -52,15 +52,6 @@ func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode * } -func createChildCandidate(candidate *CandidateNode, index int) *CandidateNode { - return &CandidateNode{ - Document: candidate.Document, - Path: candidate.CreateChildPath(index), - Filename: candidate.Filename, - Node: candidate.Node.Content[index], - } -} - func collect(d *dataTreeNavigator, aggregate *list.List, remainingMatches *list.List) (*list.List, error) { if remainingMatches.Len() == 0 { return aggregate, nil diff --git a/pkg/yqlib/operator_comments.go b/pkg/yqlib/operator_comments.go index 3e5e9e29..226248f3 100644 --- a/pkg/yqlib/operator_comments.go +++ b/pkg/yqlib/operator_comments.go @@ -84,8 +84,8 @@ func getCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNod comment = strings.Replace(comment, "# ", "", 1) node := &yaml.Node{Kind: yaml.ScalarNode, Value: comment, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil } diff --git a/pkg/yqlib/operator_delete.go b/pkg/yqlib/operator_delete.go index 7634a377..dd1613aa 100644 --- a/pkg/yqlib/operator_delete.go +++ b/pkg/yqlib/operator_delete.go @@ -72,11 +72,7 @@ func deleteFromMap(candidate *CandidateNode, childPath interface{}) { key := contents[index] value := contents[index+1] - childCandidate := &CandidateNode{ - Node: value, - Document: candidate.Document, - Path: candidate.CreateChildPath(key.Value), - } + childCandidate := candidate.CreateChild(key.Value, value) shouldDelete := key.Value == childPath diff --git a/pkg/yqlib/operator_document_index.go b/pkg/yqlib/operator_document_index.go index 72334a96..0ef444bc 100644 --- a/pkg/yqlib/operator_document_index.go +++ b/pkg/yqlib/operator_document_index.go @@ -13,7 +13,7 @@ func getDocumentIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, pa for el := matchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"} - scalar := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + scalar := candidate.CreateChild(nil, node) results.PushBack(scalar) } return results, nil diff --git a/pkg/yqlib/operator_env.go b/pkg/yqlib/operator_env.go index 8c77a4ab..6aaef263 100644 --- a/pkg/yqlib/operator_env.go +++ b/pkg/yqlib/operator_env.go @@ -44,12 +44,7 @@ func envOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNo log.Debug("ENV value", node.Value) log.Debug("ENV Kind", node.Kind) - target := &CandidateNode{ - Path: make([]interface{}, 0), - Document: 0, - Filename: "", - Node: node, - } + target := &CandidateNode{Node: node} return nodeToMap(target), nil } diff --git a/pkg/yqlib/operator_file.go b/pkg/yqlib/operator_file.go index 292145df..c7c5de14 100644 --- a/pkg/yqlib/operator_file.go +++ b/pkg/yqlib/operator_file.go @@ -15,8 +15,8 @@ func getFilenameOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNod for el := matchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Filename, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil @@ -30,8 +30,8 @@ func getFileIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNo for el := matchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil diff --git a/pkg/yqlib/operator_file_test.go b/pkg/yqlib/operator_file_test.go index 72b3ff4e..5e756d38 100644 --- a/pkg/yqlib/operator_file_test.go +++ b/pkg/yqlib/operator_file_test.go @@ -29,6 +29,14 @@ var fileOperatorScenarios = []expressionScenario{ "D0, P[], (!!int)::0\n", }, }, + { + skipDoc: true, + document: "a: cat\nb: dog", + expression: `.. lineComment |= filename`, + expected: []string{ + "D0, P[], (!!map)::a: cat # sample.yml\nb: dog # sample.yml\n", + }, + }, } func TestFileOperatorsScenarios(t *testing.T) { diff --git a/pkg/yqlib/operator_length.go b/pkg/yqlib/operator_length.go index c82c00fd..90cbff4b 100644 --- a/pkg/yqlib/operator_length.go +++ b/pkg/yqlib/operator_length.go @@ -27,8 +27,8 @@ func lengthOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTre } node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", length), Tag: "!!int"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go index 7a8006f0..0925a58c 100644 --- a/pkg/yqlib/operator_multiply.go +++ b/pkg/yqlib/operator_multiply.go @@ -64,12 +64,7 @@ func multiply(preferences *multiplyPreferences) func(d *dataTreeNavigator, lhs * if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode || (lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) { - var newBlank = &CandidateNode{ - Path: lhs.Path, - Document: lhs.Document, - Filename: lhs.Filename, - Node: &yaml.Node{}, - } + var newBlank = lhs.CreateChild(nil, &yaml.Node{}) var newThing, err = mergeObjects(d, newBlank, lhs, false) if err != nil { return nil, err diff --git a/pkg/yqlib/operator_path.go b/pkg/yqlib/operator_path.go index 3dc60ac5..26756225 100644 --- a/pkg/yqlib/operator_path.go +++ b/pkg/yqlib/operator_path.go @@ -31,8 +31,8 @@ func getPathOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *P content[pathIndex] = createPathNodeFor(path) } node.Content = content - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil diff --git a/pkg/yqlib/operator_recursive_descent_test.go b/pkg/yqlib/operator_recursive_descent_test.go index 6f5b2900..00416cd1 100644 --- a/pkg/yqlib/operator_recursive_descent_test.go +++ b/pkg/yqlib/operator_recursive_descent_test.go @@ -68,7 +68,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{ document: `{a: {name: frog, b: {name: blog, age: 12}}}`, expression: `[.. | select(has("name"))]`, expected: []string{ - "D0, P[a], (!!seq)::- {name: frog, b: {name: blog, age: 12}}\n- {name: blog, age: 12}\n", + "D0, P[], (!!seq)::- {name: frog, b: {name: blog, age: 12}}\n- {name: blog, age: 12}\n", }, }, { @@ -165,7 +165,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{ document: `{a: &cat {c: frog}, b: *cat}`, expression: `[..]`, expected: []string{ - "D0, P[a], (!!seq)::- {a: &cat {c: frog}, b: *cat}\n- &cat {c: frog}\n- frog\n- *cat\n", + "D0, P[], (!!seq)::- {a: &cat {c: frog}, b: *cat}\n- &cat {c: frog}\n- frog\n- *cat\n", }, }, { @@ -187,7 +187,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{ document: mergeDocSample, expression: `.foobar | [..]`, expected: []string{ - "D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n", + "D0, P[], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n", }, }, { @@ -195,7 +195,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{ document: mergeDocSample, expression: `.foobar | [...]`, expected: []string{ - "D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- c\n- foobar_c\n- !!merge <<\n- *foo\n- thing\n- foobar_thing\n", + "D0, P[], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- c\n- foobar_c\n- !!merge <<\n- *foo\n- thing\n- foobar_thing\n", }, }, { diff --git a/pkg/yqlib/operator_style.go b/pkg/yqlib/operator_style.go index d9326c17..698c1a17 100644 --- a/pkg/yqlib/operator_style.go +++ b/pkg/yqlib/operator_style.go @@ -100,8 +100,8 @@ func getStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode * style = "" } node := &yaml.Node{Kind: yaml.ScalarNode, Value: style, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil diff --git a/pkg/yqlib/operator_tag.go b/pkg/yqlib/operator_tag.go index f8a37063..c7449bcb 100644 --- a/pkg/yqlib/operator_tag.go +++ b/pkg/yqlib/operator_tag.go @@ -55,8 +55,8 @@ func getTagOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *Pa for el := matchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: UnwrapDoc(candidate.Node).Tag, Tag: "!!str"} - lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} - results.PushBack(lengthCand) + result := candidate.CreateChild(nil, node) + results.PushBack(result) } return results, nil diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go index 3d22b59d..bd1dea0d 100644 --- a/pkg/yqlib/operator_traverse_path.go +++ b/pkg/yqlib/operator_traverse_path.go @@ -67,11 +67,8 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Oper return traverse(d, matchingNode, operation) case yaml.DocumentNode: log.Debug("digging into doc node") - return traverse(d, &CandidateNode{ - Node: matchingNode.Node.Content[0], - Filename: matchingNode.Filename, - FileIndex: matchingNode.FileIndex, - Document: matchingNode.Document}, operation) + + return traverse(d, matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), operation) default: return list.New(), nil } @@ -121,11 +118,7 @@ func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml } else if node.Kind == yaml.MappingNode { return traverseMapWithIndices(matchingNode, indicesToTraverse, prefs) } else if node.Kind == yaml.DocumentNode { - return traverseArrayIndices(&CandidateNode{ - Node: matchingNode.Node.Content[0], - Filename: matchingNode.Filename, - FileIndex: matchingNode.FileIndex, - Document: matchingNode.Document}, indicesToTraverse, prefs) + return traverseArrayIndices(matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), indicesToTraverse, prefs) } log.Debugf("OperatorArrayTraverse skipping %v as its a %v", matchingNode, node.Tag) return list.New(), nil @@ -159,11 +152,7 @@ func traverseArrayWithIndices(candidate *CandidateNode, indices []*yaml.Node) (* var index int64 for index = 0; index < int64(len(node.Content)); index = index + 1 { - newMatches.PushBack(&CandidateNode{ - Document: candidate.Document, - Path: candidate.CreateChildPath(index), - Node: node.Content[index], - }) + newMatches.PushBack(candidate.CreateChild(index, node.Content[index])) } return newMatches, nil @@ -190,11 +179,7 @@ func traverseArrayWithIndices(candidate *CandidateNode, indices []*yaml.Node) (* return nil, fmt.Errorf("Index [%v] out of range, array size is %v", index, contentLength) } - newMatches.PushBack(&CandidateNode{ - Node: node.Content[indexToUse], - Document: candidate.Document, - Path: candidate.CreateChildPath(index), - }) + newMatches.PushBack(candidate.CreateChild(index, node.Content[indexToUse])) } return newMatches, nil } @@ -216,13 +201,8 @@ func traverseMap(matchingNode *CandidateNode, key string, prefs *traversePrefere valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"} node := matchingNode.Node node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: key}, valueNode) - candidateNode := &CandidateNode{ - Node: valueNode, - Path: append(matchingNode.Path, key), - Document: matchingNode.Document, - } + candidateNode := matchingNode.CreateChild(key, valueNode) newMatches.Set(candidateNode.GetKey(), candidateNode) - } results := list.New() @@ -258,18 +238,10 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode, } else if splat || keyMatches(key, wantedKey) { log.Debug("MATCHED") if prefs.IncludeMapKeys { - candidateNode := &CandidateNode{ - Node: key, - Path: candidate.CreateChildPath(key.Value), - Document: candidate.Document, - } + candidateNode := candidate.CreateChild(key.Value, key) newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode) } - candidateNode := &CandidateNode{ - Node: value, - Path: candidate.CreateChildPath(key.Value), - Document: candidate.Document, - } + candidateNode := candidate.CreateChild(key.Value, value) newMatches.Set(candidateNode.GetKey(), candidateNode) } } @@ -280,11 +252,7 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode, func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *CandidateNode, value *yaml.Node, wantedKey string, prefs *traversePreferences, splat bool) error { switch value.Kind { case yaml.AliasNode: - candidateNode := &CandidateNode{ - Node: value.Alias, - Path: originalCandidate.Path, - Document: originalCandidate.Document, - } + candidateNode := originalCandidate.CreateChild(nil, value.Alias) return doTraverseMap(newMatches, candidateNode, wantedKey, prefs, splat) case yaml.SequenceNode: for _, childValue := range value.Content { diff --git a/pkg/yqlib/operators.go b/pkg/yqlib/operators.go index b557dcfc..c8eb790b 100644 --- a/pkg/yqlib/operators.go +++ b/pkg/yqlib/operators.go @@ -26,7 +26,7 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { valString = "false" } node := &yaml.Node{Kind: yaml.ScalarNode, Value: valString, Tag: "!!bool"} - return &CandidateNode{Node: node, Document: owner.Document, Path: owner.Path} + return owner.CreateChild(nil, node) } func nodeToMap(candidate *CandidateNode) *list.List {