it works! wip

This commit is contained in:
Mike Farah 2020-01-11 18:52:15 +11:00
parent 96955ffa9c
commit 74c7a4e027
7 changed files with 77 additions and 24 deletions

View File

@ -94,6 +94,15 @@ func TestReadCmd(t *testing.T) {
test.AssertResult(t, "2", result.Output) test.AssertResult(t, "2", result.Output)
} }
func TestReadWithAdvancedFilterCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "read examples/sample.yaml b.e(name == sam).value")
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "4", result.Output)
}
func TestReadWithKeyAndValueCmd(t *testing.T) { func TestReadWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.c") result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.c")

View File

@ -1,9 +1,4 @@
a: true - name: fred
b: value: 3
c: 2 - name: sam
d: [3, 4, 5] value: 4
e:
- name: fred
value: 3
- name: sam
value: 4

View File

@ -2,6 +2,7 @@ package yqlib
import ( import (
"strconv" "strconv"
"strings"
errors "github.com/pkg/errors" errors "github.com/pkg/errors"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
@ -69,7 +70,7 @@ 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!", len(value.Content)) log.Debug("its a sequence of %v things!", len(value.Content))
if head == "*" || head == "**" { if head == "*" || head == "**" || strings.Contains(head, "==") {
return n.splatArray(value, head, tail, pathStack) return n.splatArray(value, head, tail, pathStack)
} else if head == "+" { } else if head == "+" {
return n.appendArray(value, head, tail, pathStack) return n.appendArray(value, head, tail, pathStack)
@ -96,7 +97,7 @@ func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, pat
newPathStack := append(pathStack, contents[indexInMap].Value) newPathStack := append(pathStack, contents[indexInMap].Value)
log.Debug("appended %v", contents[indexInMap].Value) log.Debug("appended %v", contents[indexInMap].Value)
n.navigationStrategy.DebugVisitedNodes() n.navigationStrategy.DebugVisitedNodes()
log.Debug("should I traverse? %v, %v", head, pathStackToString(newPathStack)) log.Debug("should I traverse? head: %v, path: %v", head, pathStackToString(newPathStack))
DebugNode(value) DebugNode(value)
if n.navigationStrategy.ShouldTraverse(NewNodeContext(contents[indexInMap+1], head, tail, newPathStack), contents[indexInMap].Value) { if n.navigationStrategy.ShouldTraverse(NewNodeContext(contents[indexInMap+1], head, tail, newPathStack), contents[indexInMap].Value) {
log.Debug("recurseMap: Going to traverse") log.Debug("recurseMap: Going to traverse")
@ -214,9 +215,13 @@ func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pat
log.Debug("processing") log.Debug("processing")
DebugNode(childValue) DebugNode(childValue)
childValue = n.getOrReplace(childValue, guessKind(head, tail, childValue.Kind)) childValue = n.getOrReplace(childValue, guessKind(head, tail, childValue.Kind))
var err = n.doTraverse(childValue, head, tail, append(pathStack, index))
if err != nil { newPathStack := append(pathStack, index)
return err if n.navigationStrategy.ShouldTraverse(NewNodeContext(childValue, head, tail, newPathStack), childValue.Value) {
var err = n.doTraverse(childValue, head, tail, newPathStack)
if err != nil {
return err
}
} }
} }
return nil return nil

View File

@ -0,0 +1,20 @@
package yqlib
func FilterMatchingNodesNavigationStrategy(value string) NavigationStrategy {
return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{},
followAlias: func(nodeContext NodeContext) bool {
return true
},
autoCreateMap: func(nodeContext NodeContext) bool {
return false
},
visit: func(nodeContext NodeContext) error {
return nil
},
shouldVisitExtraFn: func(nodeContext NodeContext) bool {
log.Debug("does %v match %v ? %v", nodeContext.Node.Value, value, nodeContext.Node.Value == value)
return nodeContext.Node.Value == value
},
}
}

View File

@ -108,10 +108,10 @@ func NewYqLib() YqLib {
func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) { func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) {
var paths = l.parser.ParsePath(path) var paths = l.parser.ParsePath(path)
NavigationStrategy := ReadNavigationStrategy() navigationStrategy := ReadNavigationStrategy()
navigator := NewDataNavigator(NavigationStrategy) navigator := NewDataNavigator(navigationStrategy)
error := navigator.Traverse(rootNode, paths) error := navigator.Traverse(rootNode, paths)
return NavigationStrategy.GetVisitedNodes(), error return navigationStrategy.GetVisitedNodes(), error
} }

View File

@ -39,10 +39,11 @@ type NavigationStrategy interface {
} }
type NavigationStrategyImpl struct { type NavigationStrategyImpl struct {
followAlias func(nodeContext NodeContext) bool followAlias func(nodeContext NodeContext) bool
autoCreateMap func(nodeContext NodeContext) bool autoCreateMap func(nodeContext NodeContext) bool
visit func(nodeContext NodeContext) error visit func(nodeContext NodeContext) error
visitedNodes []*NodeContext shouldVisitExtraFn func(nodeContext NodeContext) bool
visitedNodes []*NodeContext
} }
func (ns *NavigationStrategyImpl) GetVisitedNodes() []*NodeContext { func (ns *NavigationStrategyImpl) GetVisitedNodes() []*NodeContext {
@ -90,8 +91,8 @@ func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool {
parser := NewPathParser() parser := NewPathParser()
// only visit aliases if its an exact match // only visit aliases if its an exact match
return (nodeKey == "<<" && nodeContext.Head == "<<") || (nodeKey != "<<" && return ((nodeKey == "<<" && nodeContext.Head == "<<") || (nodeKey != "<<" &&
parser.MatchesNextPathElement(nodeContext, nodeKey)) parser.MatchesNextPathElement(nodeContext, nodeKey))) && (ns.shouldVisitExtraFn == nil || ns.shouldVisitExtraFn(nodeContext))
} }
func (ns *NavigationStrategyImpl) Visit(nodeContext NodeContext) error { func (ns *NavigationStrategyImpl) Visit(nodeContext NodeContext) error {

View File

@ -28,6 +28,26 @@ func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey str
if head == "**" || head == "*" { if head == "**" || head == "*" {
return true return true
} }
if strings.Contains(head, "==") {
log.Debug("ooh deep recursion time")
result := strings.SplitN(head, "==", 2)
path := strings.TrimSpace(result[0])
value := strings.TrimSpace(result[1])
log.Debug("path %v", path)
log.Debug("value %v", value)
DebugNode(nodeContext.Node)
navigationStrategy := FilterMatchingNodesNavigationStrategy(value)
navigator := NewDataNavigator(navigationStrategy)
err := navigator.Traverse(nodeContext.Node, p.ParsePath(path))
if err != nil {
log.Error(err.Error())
}
//crap handle error
log.Debug("done deep recursing, found %v matches", len(navigationStrategy.GetVisitedNodes()))
return len(navigationStrategy.GetVisitedNodes()) > 0
}
if head == "+" { if head == "+" {
log.Debug("head is +, nodeKey is %v", nodeKey) log.Debug("head is +, nodeKey is %v", nodeKey)
var _, err = strconv.ParseInt(nodeKey, 10, 64) // nolint var _, err = strconv.ParseInt(nodeKey, 10, 64) // nolint
@ -66,9 +86,12 @@ func (p *pathParser) nextYamlPath(path string) (pathElement string, remaining st
case '"': case '"':
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat" // e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
return p.search(path[1:], []uint8{'"'}, true) return p.search(path[1:], []uint8{'"'}, true)
case '(':
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
return p.search(path[1:], []uint8{')'}, true)
default: default:
// e.g "a.blah.cat" -> return "a" and "blah.cat" // e.g "a.blah.cat" -> return "a" and "blah.cat"
return p.search(path[0:], []uint8{'.', '['}, false) return p.search(path[0:], []uint8{'.', '[', '"', '('}, false)
} }
} }