From 8170eec6d14f03dab640c9cf33238830f99cb7f4 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Sat, 10 Oct 2020 15:00:39 +1100 Subject: [PATCH] extracted out operators --- pkg/yqlib/treeops/data_tree_navigator.go | 116 +++--------------- .../{traverse.go => leaf_traverser.go} | 4 +- pkg/yqlib/treeops/lib.go | 16 ++- pkg/yqlib/treeops/operators.go | 96 +++++++++++++++ 4 files changed, 132 insertions(+), 100 deletions(-) rename pkg/yqlib/treeops/{traverse.go => leaf_traverser.go} (97%) create mode 100644 pkg/yqlib/treeops/operators.go diff --git a/pkg/yqlib/treeops/data_tree_navigator.go b/pkg/yqlib/treeops/data_tree_navigator.go index 61ccad25..f2e72df0 100644 --- a/pkg/yqlib/treeops/data_tree_navigator.go +++ b/pkg/yqlib/treeops/data_tree_navigator.go @@ -1,11 +1,14 @@ package treeops import ( + "fmt" + "github.com/elliotchance/orderedmap" ) type dataTreeNavigator struct { - traverser Traverser + leafTraverser LeafTraverser + operatorHandlers map[OperationType]OperatorHandler } type NavigationPrefs struct { @@ -17,8 +20,15 @@ type DataTreeNavigator interface { } func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator { - traverse := NewTraverser(navigationPrefs) - return &dataTreeNavigator{traverse} + leafTraverser := NewLeafTraverser(navigationPrefs) + operatorHandlers := make(map[OperationType]OperatorHandler) + + operatorHandlers[Traverse] = TraverseOperator + operatorHandlers[Equals] = EqualsOperator + operatorHandlers[Or] = UnionOperator + operatorHandlers[And] = IntersectionOperator + + return &dataTreeNavigator{leafTraverser, operatorHandlers} } func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode *PathElement) (*orderedmap.OrderedMap, error) { @@ -28,7 +38,7 @@ func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode * var err error for el := matchMap.Front(); el != nil; el = el.Next() { - newNodes, err = d.traverser.Traverse(el.Value.(*CandidateNode), pathNode) + newNodes, err = d.leafTraverser.Traverse(el.Value.(*CandidateNode), pathNode) if err != nil { return nil, err } @@ -40,73 +50,6 @@ func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode * return matchingNodeMap, nil } -func (d *dataTreeNavigator) equalsOperation(matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { - log.Debugf("-- equalsOperation") - var results = orderedmap.NewOrderedMap() - - for el := matchMap.Front(); el != nil; el = el.Next() { - elMap := orderedmap.NewOrderedMap() - elMap.Set(el.Key, el.Value) - //need to splat matching nodes, then search through them - splatter := &PathTreeNode{PathElement: &PathElement{ - PathElementType: PathKey, - Value: "*", - StringValue: "*", - }} - children, err := d.getMatchingNodes(elMap, splatter) - log.Debugf("-- splatted matches, ") - if err != nil { - return nil, err - } - for childEl := children.Front(); childEl != nil; childEl = childEl.Next() { - childMap := orderedmap.NewOrderedMap() - childMap.Set(childEl.Key, childEl.Value) - childMatches, errChild := d.getMatchingNodes(childMap, pathNode.Lhs) - if errChild != nil { - return nil, errChild - } - - if d.containsMatchingValue(childMatches, pathNode.Rhs.PathElement.StringValue) { - results.Set(childEl.Key, childEl.Value) - } - } - } - - return results, nil -} - -func (d *dataTreeNavigator) containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { - log.Debugf("-- findMatchingValues") - - for el := matchMap.Front(); el != nil; el = el.Next() { - node := el.Value.(*CandidateNode) - if Match(node.Node.Value, valuePattern) { - return true - } - } - - return false -} - -func (d *dataTreeNavigator) setFunction(op OperationType, lhs *orderedmap.OrderedMap, rhs *orderedmap.OrderedMap) *orderedmap.OrderedMap { - - if op == Or { - for el := rhs.Front(); el != nil; el = el.Next() { - node := el.Value.(*CandidateNode) - lhs.Set(node.getKey(), node) - } - return lhs - } - var matchingNodeMap = orderedmap.NewOrderedMap() - for el := lhs.Front(); el != nil; el = el.Next() { - _, exists := rhs.Get(el.Key) - if exists { - matchingNodeMap.Set(el.Key, el.Value) - } - } - return matchingNodeMap -} - func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) { var matchingNodeMap = orderedmap.NewOrderedMap() @@ -132,33 +75,12 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex { return d.traverse(matchingNodes, pathNode.PathElement) } else { - var lhs, rhs *orderedmap.OrderedMap - var err error - switch pathNode.PathElement.OperationType { - case Traverse: - lhs, err = d.getMatchingNodes(matchingNodes, pathNode.Lhs) - if err != nil { - return nil, err - } - return d.getMatchingNodes(lhs, pathNode.Rhs) - case Or, And: - lhs, err = d.getMatchingNodes(matchingNodes, pathNode.Lhs) - if err != nil { - return nil, err - } - rhs, err = d.getMatchingNodes(matchingNodes, pathNode.Rhs) - if err != nil { - return nil, err - } - return d.setFunction(pathNode.PathElement.OperationType, lhs, rhs), nil - case Equals: - return d.equalsOperation(matchingNodes, pathNode) - // case EqualsSelf: - // return d.findMatchingValues(matchingNodes, pathNode.Rhs) - default: - return nil, nil + handler := d.operatorHandlers[pathNode.PathElement.OperationType] + if handler != nil { + return handler(d, matchingNodes, pathNode) + } else { + return nil, fmt.Errorf("Unknown operator %v", pathNode.PathElement.OperationType) } - } } diff --git a/pkg/yqlib/treeops/traverse.go b/pkg/yqlib/treeops/leaf_traverser.go similarity index 97% rename from pkg/yqlib/treeops/traverse.go rename to pkg/yqlib/treeops/leaf_traverser.go index 7b3790d5..bb855b2a 100644 --- a/pkg/yqlib/treeops/traverse.go +++ b/pkg/yqlib/treeops/leaf_traverser.go @@ -8,11 +8,11 @@ type traverser struct { prefs NavigationPrefs } -type Traverser interface { +type LeafTraverser interface { Traverse(matchingNode *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) } -func NewTraverser(navigationPrefs NavigationPrefs) Traverser { +func NewLeafTraverser(navigationPrefs NavigationPrefs) LeafTraverser { return &traverser{navigationPrefs} } diff --git a/pkg/yqlib/treeops/lib.go b/pkg/yqlib/treeops/lib.go index 0fbc7db8..81eb9e12 100644 --- a/pkg/yqlib/treeops/lib.go +++ b/pkg/yqlib/treeops/lib.go @@ -8,6 +8,8 @@ import ( "gopkg.in/yaml.v3" ) +var log = logging.MustGetLogger("yq-treeops") + type CandidateNode struct { Node *yaml.Node // the actual node Path []interface{} /// the path we took to get to this node @@ -22,7 +24,19 @@ func (n *CandidateNode) getKey() string { return fmt.Sprintf("%v - %v", n.Document, n.Path) } -var log = logging.MustGetLogger("yq-treeops") +type YqTreeLib interface { + Get(rootNode *yaml.Node, path string) ([]*CandidateNode, error) + // GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, error) + // Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error + // New(path string) yaml.Node + + // PathStackToString(pathStack []interface{}) string + // MergePathStackToString(pathStack []interface{}, arrayMergeStrategy ArrayMergeStrategy) string +} + +type lib struct { + treeCreator PathTreeCreator +} func NodeToString(node *CandidateNode) string { if !log.IsEnabledFor(logging.DEBUG) { diff --git a/pkg/yqlib/treeops/operators.go b/pkg/yqlib/treeops/operators.go new file mode 100644 index 00000000..f3879c12 --- /dev/null +++ b/pkg/yqlib/treeops/operators.go @@ -0,0 +1,96 @@ +package treeops + +import "github.com/elliotchance/orderedmap" + +type OperatorHandler func(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) + +func TraverseOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { + lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + return d.getMatchingNodes(lhs, pathNode.Rhs) +} + +func UnionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { + lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + for el := rhs.Front(); el != nil; el = el.Next() { + node := el.Value.(*CandidateNode) + lhs.Set(node.getKey(), node) + } + return lhs, nil +} + +func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { + lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + var matchingNodeMap = orderedmap.NewOrderedMap() + for el := lhs.Front(); el != nil; el = el.Next() { + _, exists := rhs.Get(el.Key) + if exists { + matchingNodeMap.Set(el.Key, el.Value) + } + } + return matchingNodeMap, nil +} + +func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { + log.Debugf("-- equalsOperation") + var results = orderedmap.NewOrderedMap() + + for el := matchMap.Front(); el != nil; el = el.Next() { + elMap := orderedmap.NewOrderedMap() + elMap.Set(el.Key, el.Value) + //need to splat matching nodes, then search through them + splatter := &PathTreeNode{PathElement: &PathElement{ + PathElementType: PathKey, + Value: "*", + StringValue: "*", + }} + children, err := d.getMatchingNodes(elMap, splatter) + log.Debugf("-- splatted matches, ") + if err != nil { + return nil, err + } + for childEl := children.Front(); childEl != nil; childEl = childEl.Next() { + childMap := orderedmap.NewOrderedMap() + childMap.Set(childEl.Key, childEl.Value) + childMatches, errChild := d.getMatchingNodes(childMap, pathNode.Lhs) + if errChild != nil { + return nil, errChild + } + + if containsMatchingValue(childMatches, pathNode.Rhs.PathElement.StringValue) { + results.Set(childEl.Key, childEl.Value) + } + } + } + + return results, nil +} + +func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { + log.Debugf("-- findMatchingValues") + + for el := matchMap.Front(); el != nil; el = el.Next() { + node := el.Value.(*CandidateNode) + if Match(node.Node.Value, valuePattern) { + return true + } + } + + return false +}