1
0
mirror of https://github.com/taigrr/yq synced 2025-01-18 04:53:17 -08:00

Refactored merge - will allow more sophisticated mergin

This commit is contained in:
Mike Farah
2020-06-18 09:20:26 +10:00
parent eac218980e
commit b11661a1be
10 changed files with 148 additions and 34 deletions

View File

@@ -73,6 +73,7 @@ func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface
nodeContext := NewNodeContext(value, head, tail, pathStack)
if head == "**" && !n.navigationStrategy.ShouldOnlyDeeplyVisitLeaves(nodeContext) {
nodeContext.IsMiddleNode = true
errorVisitingDeeply := n.navigationStrategy.Visit(nodeContext)
if errorVisitingDeeply != nil {
return errorVisitingDeeply

View File

@@ -13,11 +13,12 @@ import (
var log = logging.MustGetLogger("yq")
type UpdateCommand struct {
Command string
Path string
Value *yaml.Node
Overwrite bool
DontUpdateNodeValue bool
Command string
Path string
Value *yaml.Node
Overwrite bool
DontUpdateNodeValue bool
DontUpdateNodeContent bool
}
func KindString(kind yaml.Kind) string {
@@ -49,7 +50,10 @@ func DebugNode(value *yaml.Node) {
}
encoder.Close()
log.Debug("Tag: %v, Kind: %v, Anchor: %v", value.Tag, KindString(value.Kind), value.Anchor)
log.Debug("%v", buf.String())
log.Debug("Head Comment: %v", value.HeadComment)
log.Debug("Line Comment: %v", value.LineComment)
log.Debug("FootComment Comment: %v", value.FootComment)
log.Debug("\n%v", buf.String())
}
}
@@ -131,7 +135,8 @@ func guessKind(head interface{}, tail []interface{}, guess yaml.Kind) yaml.Kind
}
type YqLib interface {
Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error)
Get(rootNode *yaml.Node, path string) ([]*NodeContext, error)
GetForMerge(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error)
Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error
New(path string) yaml.Node
@@ -149,13 +154,20 @@ func NewYqLib() YqLib {
}
}
func (l *lib) Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error) {
func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) {
var paths = l.parser.ParsePath(path)
navigationStrategy := ReadNavigationStrategy(deeplyTraverseArrays)
navigationStrategy := ReadNavigationStrategy()
navigator := NewDataNavigator(navigationStrategy)
error := navigator.Traverse(rootNode, paths)
return navigationStrategy.GetVisitedNodes(), error
}
func (l *lib) GetForMerge(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error) {
var paths = l.parser.ParsePath(path)
navigationStrategy := ReadForMergeNavigationStrategy(deeplyTraverseArrays)
navigator := NewDataNavigator(navigationStrategy)
error := navigator.Traverse(rootNode, paths)
return navigationStrategy.GetVisitedNodes(), error
}
func (l *lib) PathStackToString(pathStack []interface{}) string {
@@ -179,6 +191,10 @@ func (l *lib) Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreat
var paths = l.parser.ParsePath(updateCommand.Path)
navigator := NewDataNavigator(UpdateNavigationStrategy(updateCommand, autoCreate))
return navigator.Traverse(rootNode, paths)
case "merge":
var paths = l.parser.ParsePath(updateCommand.Path)
navigator := NewDataNavigator(MergeNavigationStrategy(updateCommand, autoCreate))
return navigator.Traverse(rootNode, paths)
case "delete":
var paths = l.parser.ParsePath(updateCommand.Path)
lastBit, newTail := paths[len(paths)-1], paths[:len(paths)-1]

View File

@@ -0,0 +1,63 @@
package yqlib
import "gopkg.in/yaml.v3"
func MergeNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) NavigationStrategy {
return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{},
pathParser: NewPathParser(),
followAlias: func(nodeContext NodeContext) bool {
return false
},
autoCreateMap: func(nodeContext NodeContext) bool {
return autoCreate
},
visit: func(nodeContext NodeContext) error {
node := nodeContext.Node
changesToApply := updateCommand.Value
if node.Kind == yaml.DocumentNode && changesToApply.Kind != yaml.DocumentNode {
// when the path is empty, it matches both the top level pseudo document node
// and the actual top level node (e.g. map/sequence/whatever)
// so when we are updating with no path, make sure we update the right node.
node = node.Content[0]
}
if updateCommand.Overwrite || node.Value == "" {
log.Debug("going to update")
DebugNode(node)
log.Debug("with")
DebugNode(changesToApply)
node.Value = changesToApply.Value
node.Tag = changesToApply.Tag
node.Kind = changesToApply.Kind
node.Style = changesToApply.Style
node.Anchor = changesToApply.Anchor
node.Alias = changesToApply.Alias
node.HeadComment = changesToApply.HeadComment
node.LineComment = changesToApply.LineComment
node.FootComment = changesToApply.FootComment
if !updateCommand.DontUpdateNodeContent {
node.Content = changesToApply.Content
}
// // TODO: mergeComments flag
// if node.HeadComment != "" && changesToApply.HeadComment != "" {
// node.HeadComment = node.HeadComment + "\n" + changesToApply.HeadComment
// log.Debug("merged comments with a space, %v", node.HeadComment)
// } else {
// node.HeadComment = node.HeadComment + changesToApply.HeadComment
// if node.HeadComment != "" {
// log.Debug("merged comments with no space, %v", node.HeadComment)
// }
// }
// node.LineComment = node.LineComment + changesToApply.LineComment
// node.FootComment = node.FootComment + changesToApply.FootComment
} else {
log.Debug("skipping update as node already has value %v and overwriteFlag is ", node.Value, updateCommand.Overwrite)
}
return nil
},
}
}

View File

@@ -11,6 +11,9 @@ type NodeContext struct {
Head interface{}
Tail []interface{}
PathStack []interface{}
// middle nodes are nodes that match along the original path, but not a
// target match of the path. This is only relevant when ShouldOnlyDeeplyVisitLeaves is false.
IsMiddleNode bool
}
func NewNodeContext(node *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) NodeContext {

View File

@@ -0,0 +1,30 @@
package yqlib
func ReadForMergeNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{},
pathParser: NewPathParser(),
followAlias: func(nodeContext NodeContext) bool {
return false
},
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return false
},
visit: func(nodeContext NodeContext) error {
return nil
},
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
var isInArray = false
if len(nodeContext.PathStack) > 0 {
var lastElement = nodeContext.PathStack[len(nodeContext.PathStack)-1]
switch lastElement.(type) {
case int:
isInArray = true
default:
isInArray = false
}
}
return deeplyTraverseArrays || !isInArray
},
}
}

View File

@@ -1,24 +1,11 @@
package yqlib
func ReadNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
func ReadNavigationStrategy() NavigationStrategy {
return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{},
pathParser: NewPathParser(),
visit: func(nodeContext NodeContext) error {
return nil
},
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
var isInArray = false
if len(nodeContext.PathStack) > 0 {
var lastElement = nodeContext.PathStack[len(nodeContext.PathStack)-1]
switch lastElement.(type) {
case int:
isInArray = true
default:
isInArray = false
}
}
return deeplyTraverseArrays || !isInArray
},
}
}

View File

@@ -24,7 +24,9 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
node.Tag = changesToApply.Tag
node.Kind = changesToApply.Kind
node.Style = changesToApply.Style
node.Content = changesToApply.Content
if !updateCommand.DontUpdateNodeContent {
node.Content = changesToApply.Content
}
node.Anchor = changesToApply.Anchor
node.Alias = changesToApply.Alias
node.HeadComment = changesToApply.HeadComment