Delete now works with deep splat

This commit is contained in:
Mike Farah 2020-06-11 09:40:59 +10:00
parent b9e304e7a4
commit 71f5f76213
6 changed files with 61 additions and 11 deletions

View File

@ -100,6 +100,29 @@ b:
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestDeleteDeepSplatArrayYaml(t *testing.T) {
content := `thing: 123
b:
hi:
- thing: item1
name: fred
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("delete %s **.thing", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
hi:
- name: fred
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestDeleteSplatPrefixYaml(t *testing.T) { func TestDeleteSplatPrefixYaml(t *testing.T) {
content := `a: 2 content := `a: 2
b: b:

View File

@ -24,6 +24,7 @@ func NewDataNavigator(NavigationStrategy NavigationStrategy) DataNavigator {
func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error { func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error {
realValue := value realValue := value
emptyArray := make([]interface{}, 0) emptyArray := make([]interface{}, 0)
log.Debugf("Traversing path %v", pathStackToString(path))
if realValue.Kind == yaml.DocumentNode { if realValue.Kind == yaml.DocumentNode {
log.Debugf("its a document! returning the first child") log.Debugf("its a document! returning the first child")
return n.doTraverse(value.Content[0], "", path, emptyArray) return n.doTraverse(value.Content[0], "", path, emptyArray)
@ -68,6 +69,16 @@ func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *y
func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error { func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
log.Debug("recursing, processing %v, pathStack %v", head, pathStackToString(pathStack)) log.Debug("recursing, processing %v, pathStack %v", head, pathStackToString(pathStack))
nodeContext := NewNodeContext(value, head, tail, pathStack)
if head == "**" && !n.navigationStrategy.ShouldOnlyDeeplyVisitLeaves(nodeContext) {
errorVisitingDeeply := n.navigationStrategy.Visit(nodeContext)
if errorVisitingDeeply != nil {
return errorVisitingDeeply
}
}
switch value.Kind { switch value.Kind {
case yaml.MappingNode: case yaml.MappingNode:
log.Debug("its a map with %v entries", len(value.Content)/2) log.Debug("its a map with %v entries", len(value.Content)/2)
@ -85,20 +96,20 @@ func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface
if head == "+" { if head == "+" {
return n.appendArray(value, head, tail, pathStack) return n.appendArray(value, head, tail, pathStack)
} else if len(value.Content) == 0 && head == "**" { } else if len(value.Content) == 0 && head == "**" {
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack)) return n.navigationStrategy.Visit(nodeContext)
} }
return n.splatArray(value, head, tail, pathStack) return n.splatArray(value, head, tail, pathStack)
} }
case yaml.AliasNode: case yaml.AliasNode:
log.Debug("its an alias!") log.Debug("its an alias!")
DebugNode(value.Alias) DebugNode(value.Alias)
if n.navigationStrategy.FollowAlias(NewNodeContext(value, head, tail, pathStack)) { if n.navigationStrategy.FollowAlias(nodeContext) {
log.Debug("following the alias") log.Debug("following the alias")
return n.recurse(value.Alias, head, tail, pathStack) return n.recurse(value.Alias, head, tail, pathStack)
} }
return nil return nil
default: default:
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack)) return n.navigationStrategy.Visit(nodeContext)
} }
} }

View File

@ -18,9 +18,12 @@ func DeleteNavigationStrategy(pathElementToDelete interface{}) NavigationStrateg
shouldDeeplyTraverse: func(nodeContext NodeContext) bool { shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true return true
}, },
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return false
},
visit: func(nodeContext NodeContext) error { visit: func(nodeContext NodeContext) error {
node := nodeContext.Node node := nodeContext.Node
log.Debug("need to find and delete %v in here", pathElementToDelete) log.Debug("need to find and delete %v in here (%v)", pathElementToDelete, pathStackToString(nodeContext.PathStack))
DebugNode(node) DebugNode(node)
if node.Kind == yaml.SequenceNode { if node.Kind == yaml.SequenceNode {
newContent := deleteFromArray(parser, node.Content, nodeContext.PathStack, pathElementToDelete) newContent := deleteFromArray(parser, node.Content, nodeContext.PathStack, pathElementToDelete)

View File

@ -35,6 +35,8 @@ type NavigationStrategy interface {
// we use it to match against the pathExpression in head. // we use it to match against the pathExpression in head.
ShouldTraverse(nodeContext NodeContext, nodeKey string) bool ShouldTraverse(nodeContext NodeContext, nodeKey string) bool
ShouldDeeplyTraverse(nodeContext NodeContext) bool ShouldDeeplyTraverse(nodeContext NodeContext) bool
// when deeply traversing, should we visit all matching nodes, or just leaves?
ShouldOnlyDeeplyVisitLeaves(NodeContext) bool
GetVisitedNodes() []*NodeContext GetVisitedNodes() []*NodeContext
DebugVisitedNodes() DebugVisitedNodes()
GetPathParser() PathParser GetPathParser() PathParser
@ -46,6 +48,7 @@ type NavigationStrategyImpl struct {
visit func(nodeContext NodeContext) error visit func(nodeContext NodeContext) error
shouldVisitExtraFn func(nodeContext NodeContext) bool shouldVisitExtraFn func(nodeContext NodeContext) bool
shouldDeeplyTraverse func(nodeContext NodeContext) bool shouldDeeplyTraverse func(nodeContext NodeContext) bool
shouldOnlyDeeplyVisitLeaves func(nodeContext NodeContext) bool
visitedNodes []*NodeContext visitedNodes []*NodeContext
pathParser PathParser pathParser PathParser
} }
@ -70,6 +73,10 @@ func (ns *NavigationStrategyImpl) ShouldDeeplyTraverse(nodeContext NodeContext)
return ns.shouldDeeplyTraverse(nodeContext) return ns.shouldDeeplyTraverse(nodeContext)
} }
func (ns *NavigationStrategyImpl) ShouldOnlyDeeplyVisitLeaves(nodeContext NodeContext) bool {
return ns.shouldOnlyDeeplyVisitLeaves(nodeContext)
}
func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool { func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool {
// we should traverse aliases (if enabled), but not visit them :/ // we should traverse aliases (if enabled), but not visit them :/
if len(nodeContext.PathStack) == 0 { if len(nodeContext.PathStack) == 0 {

View File

@ -26,5 +26,8 @@ func ReadNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
} }
return deeplyTraverseArrays || !isInArray return deeplyTraverseArrays || !isInArray
}, },
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return true
},
} }
} }

View File

@ -13,6 +13,9 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
shouldDeeplyTraverse: func(nodeContext NodeContext) bool { shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true return true
}, },
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return true
},
visit: func(nodeContext NodeContext) error { visit: func(nodeContext NodeContext) error {
node := nodeContext.Node node := nodeContext.Node
changesToApply := updateCommand.Value changesToApply := updateCommand.Value