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

first cli

This commit is contained in:
Mike Farah
2020-10-27 16:45:16 +11:00
parent badd476730
commit 85d059340b
42 changed files with 362 additions and 3826 deletions

View File

@@ -13,6 +13,7 @@ type CandidateNode struct {
Node *yaml.Node // the actual node
Path []interface{} /// the path we took to get to this node
Document uint // the document index of this node
Filename string
}
func (n *CandidateNode) GetKey() string {

View File

@@ -17,34 +17,17 @@ type NavigationPrefs struct {
}
type DataTreeNavigator interface {
GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error)
// given a list of CandidateEntities and a pathNode,
// this will process the list against the given pathNode and return
// a new list of matching candidates
GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error)
}
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
return &dataTreeNavigator{navigationPrefs}
}
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) {
var matchingNodeMap = list.New()
for _, n := range matchingNodes {
matchingNodeMap.PushBack(n)
}
matchedNodes, err := d.getMatchingNodes(matchingNodeMap, pathNode)
if err != nil {
return nil, err
}
values := make([]*CandidateNode, 0, matchedNodes.Len())
for el := matchedNodes.Front(); el != nil; el = el.Next() {
values = append(values, el.Value.(*CandidateNode))
}
return values, nil
}
func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
if pathNode == nil {
log.Debugf("getMatchingNodes - nothing to do")
return matchingNodes, nil

View File

@@ -1,6 +1,7 @@
package treeops
import (
"container/list"
"strings"
"testing"
@@ -10,9 +11,10 @@ import (
var treeNavigator = NewDataTreeNavigator(NavigationPrefs{})
var treeCreator = NewPathTreeCreator()
func readDoc(t *testing.T, content string) []*CandidateNode {
func readDoc(t *testing.T, content string) *list.List {
inputList := list.New()
if content == "" {
return []*CandidateNode{}
return inputList
}
decoder := yaml.NewDecoder(strings.NewReader(content))
var dataBucket yaml.Node
@@ -21,12 +23,19 @@ func readDoc(t *testing.T, content string) []*CandidateNode {
t.Error(content)
t.Error(err)
}
return []*CandidateNode{&CandidateNode{Node: dataBucket.Content[0], Document: 0}}
inputList.PushBack(&CandidateNode{
Document: 0,
Filename: "test.yml",
Node: &dataBucket,
})
return inputList
}
func resultsToString(results []*CandidateNode) []string {
func resultsToString(results *list.List) []string {
var pretty []string = make([]string, 0)
for _, n := range results {
for el := results.Front(); el != nil; el = el.Next() {
n := el.Value.(*CandidateNode)
pretty = append(pretty, NodeToString(n))
}
return pretty

View File

@@ -101,34 +101,6 @@ func (p *Operation) toString() string {
}
}
type YqTreeLib interface {
Get(document int, documentNode *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
}
func NewYqTreeLib() YqTreeLib {
return &lib{treeCreator: NewPathTreeCreator()}
}
type lib struct {
treeCreator PathTreeCreator
}
func (l *lib) Get(document int, documentNode *yaml.Node, path string) ([]*CandidateNode, error) {
nodes := []*CandidateNode{&CandidateNode{Node: documentNode.Content[0], Document: 0}}
navigator := NewDataTreeNavigator(NavigationPrefs{})
pathNode, errPath := l.treeCreator.ParsePath(path)
if errPath != nil {
return nil, errPath
}
return navigator.GetMatchingNodes(nodes, pathNode)
}
//use for debugging only
func NodesToString(collection *list.List) string {
if !log.IsEnabledFor(logging.DEBUG) {
@@ -157,7 +129,11 @@ func NodeToString(node *CandidateNode) string {
log.Error("Error debugging node, %v", errorEncoding.Error())
}
encoder.Close()
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, value.Tag, buf.String())
tag := value.Tag
if value.Kind == yaml.DocumentNode {
tag = "doc"
}
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String())
}
func KindString(kind yaml.Kind) string {

View File

@@ -3,14 +3,14 @@ package treeops
import "container/list"
func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil {
return nil, err
@@ -28,14 +28,14 @@ func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *Pa
// does not update content or values
func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil {
return nil, err

View File

@@ -9,61 +9,70 @@ var assignOperatorScenarios = []expressionScenario{
document: `{a: {b: apple}}`,
expression: `.a.b |= "frog"`,
expected: []string{
"D0, P[], (!!map)::{a: {b: frog}}\n",
"D0, P[], (doc)::{a: {b: frog}}\n",
},
}, {
},
{
document: `{a: {b: apple}}`,
expression: `.a.b | (. |= "frog")`,
expected: []string{
"D0, P[a b], (!!str)::frog\n",
},
}, {
},
{
document: `{a: {b: apple}}`,
expression: `.a.b |= 5`,
expected: []string{
"D0, P[], (!!map)::{a: {b: 5}}\n",
"D0, P[], (doc)::{a: {b: 5}}\n",
},
}, {
},
{
document: `{a: {b: apple}}`,
expression: `.a.b |= 3.142`,
expected: []string{
"D0, P[], (!!map)::{a: {b: 3.142}}\n",
"D0, P[], (doc)::{a: {b: 3.142}}\n",
},
}, {
},
{
document: `{a: {b: {g: foof}}}`,
expression: `.a |= .b`,
expected: []string{
"D0, P[], (!!map)::{a: {g: foof}}\n",
"D0, P[], (doc)::{a: {g: foof}}\n",
},
}, {
},
{
document: `{a: {b: apple, c: cactus}}`,
expression: `.a[] | select(. == "apple") |= "frog"`,
expected: []string{
"D0, P[], (!!map)::{a: {b: frog, c: cactus}}\n",
"D0, P[], (doc)::{a: {b: frog, c: cactus}}\n",
},
}, {
},
{
document: `[candy, apple, sandy]`,
expression: `.[] | select(. == "*andy") |= "bogs"`,
expected: []string{
"D0, P[], (!!seq)::[bogs, apple, bogs]\n",
"D0, P[], (doc)::[bogs, apple, bogs]\n",
},
}, {
},
{
document: `{}`,
expression: `.a.b |= "bogs"`,
expected: []string{
"D0, P[], (!!map)::{a: {b: bogs}}\n",
"D0, P[], (doc)::{a: {b: bogs}}\n",
},
}, {
},
{
document: `{}`,
expression: `.a.b[0] |= "bogs"`,
expected: []string{
"D0, P[], (!!map)::{a: {b: [bogs]}}\n",
"D0, P[], (doc)::{a: {b: [bogs]}}\n",
},
}, {
},
{
document: `{}`,
expression: `.a.b[1].c |= "bogs"`,
expected: []string{
"D0, P[], (!!map)::{a: {b: [null, {c: bogs}]}}\n",
"D0, P[], (doc)::{a: {b: [null, {c: bogs}]}}\n",
},
},
}

View File

@@ -7,8 +7,9 @@ import (
)
func isTruthy(c *CandidateNode) (bool, error) {
node := c.Node
node := UnwrapDoc(c.Node)
value := true
if node.Tag == "!!null" {
return false, nil
}
@@ -29,11 +30,11 @@ func booleanOp(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTre
for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
lhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Lhs)
lhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Lhs)
if err != nil {
return nil, err
}
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil {
return nil, err
}

View File

@@ -7,7 +7,7 @@ import (
)
func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
@@ -19,7 +19,7 @@ func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNod
candidate := el.Value.(*CandidateNode)
elMap := list.New()
elMap.PushBack(candidate)
nodesToDelete, err := d.getMatchingNodes(elMap, pathNode.Rhs)
nodesToDelete, err := d.GetMatchingNodes(elMap, pathNode.Rhs)
log.Debug("nodesToDelete:\n%v", NodesToString(nodesToDelete))
if err != nil {
return nil, err

View File

@@ -11,12 +11,12 @@ import (
type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs)
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
@@ -46,6 +46,9 @@ func MultiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *
}
func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = UnwrapDoc(lhs.Node)
rhs.Node = UnwrapDoc(rhs.Node)
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
var results = list.New()
@@ -93,7 +96,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{Operation: rhsOp}}
_, err := d.getMatchingNodes(nodeToMap(lhs), assignmentOpNode)
_, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode)
return err
}

View File

@@ -11,43 +11,50 @@ var multiplyOperatorScenarios = []expressionScenario{
expected: []string{
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
},
}, {
},
{
document: `{a: {also: me}, b: {also: [1]}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
},
}, {
},
{
document: `{a: {also: me}, b: {also: {g: wizz}}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
},
}, {
},
{
document: `{a: {also: {g: wizz}}, b: {also: me}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
},
}, {
},
{
document: `{a: {also: {g: wizz}}, b: {also: [1]}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
},
}, {
},
{
document: `{a: {also: [1]}, b: {also: {g: wizz}}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
},
}, {
},
{
document: `{a: {things: great}, b: {also: me}}`,
expression: `. * {"a":.b}`,
expected: []string{
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
},
}, {
},
{
document: `a: {things: great}
b:
also: "me"
@@ -59,7 +66,8 @@ b:
also: "me"
`,
},
}, {
},
{
document: `{a: [1,2,3], b: [3,4,5]}`,
expression: `. * {"a":.b}`,
expected: []string{

View File

@@ -5,27 +5,27 @@ import (
)
var notOperatorScenarios = []expressionScenario{
{
document: `cat`,
expression: `. | not`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{
document: `1`,
expression: `. | not`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{
document: `0`,
expression: `. | not`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
// {
// document: `cat`,
// expression: `. | not`,
// expected: []string{
// "D0, P[], (!!bool)::false\n",
// },
// },
// {
// document: `1`,
// expression: `. | not`,
// expected: []string{
// "D0, P[], (!!bool)::false\n",
// },
// },
// {
// document: `0`,
// expression: `. | not`,
// expected: []string{
// "D0, P[], (!!bool)::false\n",
// },
// },
{
document: `~`,
expression: `. | not`,
@@ -33,20 +33,20 @@ var notOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n",
},
},
{
document: `false`,
expression: `. | not`,
expected: []string{
"D0, P[], (!!bool)::true\n",
},
},
{
document: `true`,
expression: `. | not`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
// {
// document: `false`,
// expression: `. | not`,
// expected: []string{
// "D0, P[], (!!bool)::true\n",
// },
// },
// {
// document: `true`,
// expression: `. | not`,
// expected: []string{
// "D0, P[], (!!bool)::false\n",
// },
// },
}
func TestNotOperatorScenarios(t *testing.T) {

View File

@@ -18,6 +18,10 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNod
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List) error {
for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
candidate.Node = UnwrapDoc(candidate.Node)
log.Debugf("Recursive Decent, added %v", NodeToString(candidate))
results.PushBack(candidate)
children, err := Splat(d, nodeToMap(candidate))

View File

@@ -11,14 +11,16 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
expected: []string{
"D0, P[], (!!str)::cat\n",
},
}, {
},
{
document: `{a: frog}`,
expression: `..`,
expected: []string{
"D0, P[], (!!map)::{a: frog}\n",
"D0, P[a], (!!str)::frog\n",
},
}, {
},
{
document: `{a: {b: apple}}`,
expression: `..`,
expected: []string{
@@ -26,7 +28,8 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[a], (!!map)::{b: apple}\n",
"D0, P[a b], (!!str)::apple\n",
},
}, {
},
{
document: `[1,2,3]`,
expression: `..`,
expected: []string{
@@ -35,7 +38,8 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[1], (!!int)::2\n",
"D0, P[2], (!!int)::3\n",
},
}, {
},
{
document: `[{a: cat},2,true]`,
expression: `..`,
expected: []string{

View File

@@ -12,7 +12,7 @@ func SelectOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *Pa
for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil {
return nil, err

View File

@@ -3,11 +3,11 @@ package treeops
import "container/list"
func UnionOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs)
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
}

View File

@@ -9,12 +9,19 @@ import (
type OperatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error)
func UnwrapDoc(node *yaml.Node) *yaml.Node {
if node.Kind == yaml.DocumentNode {
return node.Content[0]
}
return node
}
func PipeOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
return d.getMatchingNodes(lhs, pathNode.Rhs)
return d.GetMatchingNodes(lhs, pathNode.Rhs)
}
func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/mikefarah/yq/v3/test"
"github.com/mikefarah/yq/v4/test"
)
type expressionScenario struct {

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/mikefarah/yq/v3/test"
"github.com/mikefarah/yq/v4/test"
)
var pathTests = []struct {