Deep splat!

This commit is contained in:
Mike Farah 2019-12-30 16:51:07 +13:00
parent 8a6af1720d
commit 4dbdd4a805
5 changed files with 40 additions and 14 deletions

View File

@ -112,6 +112,24 @@ func TestReadArrayCmd(t *testing.T) {
test.AssertResult(t, "b.e.1.name: sam\n", result.Output) test.AssertResult(t, "b.e.1.name: sam\n", result.Output)
} }
func TestReadDeepSplatCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "read -p kv examples/sample.yaml b.**")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b.c: 2
b.d.[0]: 3
b.d.[1]: 4
b.d.[2]: 5
b.e.[0].name: fred
b.e.[0].value: 3
b.e.[1].name: sam
b.e.[1].value: 4
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestReadWithKeyCmd(t *testing.T) { func TestReadWithKeyCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "read -p k examples/sample.yaml b.c") result := test.RunCmd(cmd, "read -p k examples/sample.yaml b.c")

View File

@ -31,13 +31,19 @@ func (n *navigator) Traverse(value *yaml.Node, path []string) error {
return n.doTraverse(value, "", path, emptyArray) return n.doTraverse(value, "", path, emptyArray)
} }
func (n *navigator) doTraverse(value *yaml.Node, head string, path []string, pathStack []interface{}) error { func (n *navigator) doTraverse(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
if len(path) > 0 { log.Debug("head %v", head)
log.Debugf("diving into %v", path[0])
DebugNode(value) DebugNode(value)
return n.recurse(value, path[0], path[1:], pathStack) if head == "**" && value.Kind != yaml.ScalarNode {
return n.recurse(value, head, tail, pathStack)
} }
return n.navigationStrategy.Visit(NewNodeContext(value, head, path, pathStack))
if len(tail) > 0 {
log.Debugf("diving into %v", tail[0])
DebugNode(value)
return n.recurse(value, tail[0], tail[1:], pathStack)
}
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
} }
func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node { func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node {
@ -56,8 +62,8 @@ func (n *navigator) recurse(value *yaml.Node, head string, tail []string, pathSt
return n.recurseMap(value, head, tail, pathStack) return n.recurseMap(value, head, tail, pathStack)
case yaml.SequenceNode: case yaml.SequenceNode:
log.Debug("its a sequence of %v things!, %v", len(value.Content)) log.Debug("its a sequence of %v things!, %v", len(value.Content))
if head == "*" { if head == "*" || head == "**" {
return n.splatArray(value, tail, pathStack) return n.splatArray(value, head, tail, pathStack)
} else if head == "+" { } else if head == "+" {
return n.appendArray(value, tail, pathStack) return n.appendArray(value, tail, pathStack)
} }
@ -196,11 +202,11 @@ func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head str
return nil return nil
} }
func (n *navigator) splatArray(value *yaml.Node, tail []string, pathStack []interface{}) error { func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
for index, childValue := range value.Content { for index, childValue := range value.Content {
log.Debug("processing") log.Debug("processing")
DebugNode(childValue) DebugNode(childValue)
head := fmt.Sprintf("%v", index) // head = fmt.Sprintf("%v", index)
childValue = n.getOrReplace(childValue, guessKind(tail, childValue.Kind)) childValue = n.getOrReplace(childValue, guessKind(tail, childValue.Kind))
var err = n.doTraverse(childValue, head, tail, append(pathStack, index)) var err = n.doTraverse(childValue, head, tail, append(pathStack, index))
if err != nil { if err != nil {

View File

@ -61,7 +61,7 @@ func guessKind(tail []string, guess yaml.Kind) yaml.Kind {
if tail[0] == "+" || errorParsingInt == nil { if tail[0] == "+" || errorParsingInt == nil {
return yaml.SequenceNode return yaml.SequenceNode
} }
if tail[0] == "*" && (guess == yaml.SequenceNode || guess == yaml.MappingNode) { if (tail[0] == "*" || tail[0] == "**") && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
return guess return guess
} }
if guess == yaml.AliasNode { if guess == yaml.AliasNode {

View File

@ -74,13 +74,12 @@ func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKe
} }
func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool { func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool {
// we should traverse aliases (if enabled), but not visit them :/
pathStack := nodeContext.PathStack pathStack := nodeContext.PathStack
if len(pathStack) == 0 { if len(pathStack) == 0 {
return true return true
} }
if ns.alreadyVisited(pathStack) { if ns.alreadyVisited(pathStack) || len(nodeContext.Tail) != 0 {
return false return false
} }
@ -108,7 +107,7 @@ func (ns *NavigationStrategyImpl) Visit(nodeContext NodeContext) error {
} }
func (ns *NavigationStrategyImpl) DebugVisitedNodes() { func (ns *NavigationStrategyImpl) DebugVisitedNodes() {
log.Debug("%v", ns.visitedNodes) log.Debug("Visited Nodes:")
for _, candidate := range ns.visitedNodes { for _, candidate := range ns.visitedNodes {
log.Debug(" - %v", PathStackToString(candidate.PathStack)) log.Debug(" - %v", PathStackToString(candidate.PathStack))
} }

View File

@ -24,6 +24,9 @@ func NewPathParser() PathParser {
*/ */
func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool { func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool {
head := nodeContext.Head head := nodeContext.Head
if head == "**" || head == "*" {
return true
}
var prefixMatch = strings.TrimSuffix(head, "*") var prefixMatch = strings.TrimSuffix(head, "*")
if prefixMatch != head { if prefixMatch != head {
log.Debug("prefix match, %v", strings.HasPrefix(nodeKey, prefixMatch)) log.Debug("prefix match, %v", strings.HasPrefix(nodeKey, prefixMatch))