yq/pkg/yqlib/operator_explode.go
2020-11-13 14:07:11 +11:00

152 lines
3.8 KiB
Go

package yqlib
import (
"container/list"
"gopkg.in/yaml.v3"
)
func ExplodeOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- ExplodeOperation")
for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil {
return nil, err
}
for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() {
err = explodeNode(childEl.Value.(*CandidateNode).Node)
if err != nil {
return nil, err
}
}
}
return matchMap, nil
}
func explodeNode(node *yaml.Node) error {
node.Anchor = ""
switch node.Kind {
case yaml.SequenceNode, yaml.DocumentNode:
for index, contentNode := range node.Content {
log.Debugf("exploding index %v", index)
errorInContent := explodeNode(contentNode)
if errorInContent != nil {
return errorInContent
}
}
return nil
case yaml.AliasNode:
log.Debugf("its an alias!")
if node.Alias != nil {
node.Kind = node.Alias.Kind
node.Style = node.Alias.Style
node.Tag = node.Alias.Tag
node.Content = node.Alias.Content
node.Value = node.Alias.Value
node.Alias = nil
}
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 != "<<" {
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 := 0; index < len(valueNode.Content); index = index + 1 {
aliasNode := valueNode.Content[index]
err := applyAlias(node, aliasNode.Alias, index, newContent)
if err != nil {
return err
}
}
} else {
log.Debugf("an alias merge!")
err := applyAlias(node, valueNode.Alias, index, newContent)
if err != nil {
return err
}
}
}
}
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:
return nil
}
}
func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *list.List) error {
if alias == nil {
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]
err := overrideEntry(node, keyNode, valueNode, aliasIndex, newContent)
if err != nil {
return err
}
}
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
}