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

Compare commits

...

12 Commits

Author SHA1 Message Date
Mike Farah
fae2b2643c Added gofmt to format command 2021-06-01 10:52:14 +10:00
Mike Farah
dd86b5e7f2 Fixing doc 2021-05-28 17:00:25 +10:00
Mike Farah
f1f75683c1 Fixed nil RHS bug in alternative operator #838 2021-05-28 16:59:02 +10:00
Mike Farah
38b9856f50 Increment version 2021-05-22 08:24:08 +10:00
Mike Farah
48eeb2a9df Fixes update-assign with collect object issue #830 2021-05-22 08:22:45 +10:00
Mike Farah
af283315f2 Increment version 2021-05-21 14:27:24 +10:00
Mike Farah
d18a6963f6 Fixes nested array indexing #824 2021-05-21 14:18:24 +10:00
Mike Farah
77edbb9f5c Fixing readonly ops not to modify context when paths dont exist 2021-05-16 15:02:31 +10:00
Mike Farah
179c44aacc Fixing readonly ops not to modify context when paths dont exist 2021-05-16 14:36:13 +10:00
Mike Farah
bc70c1fb16 Added blank alias example 2021-05-16 14:18:18 +10:00
Mike Farah
0b71a40797 Fixing readonly ops not to modify context when paths dont exist 2021-05-16 14:17:13 +10:00
Mike Farah
3f51a44596 Fixing readonly ops not to modify context when paths dont exist 2021-05-16 14:00:30 +10:00
47 changed files with 435 additions and 57 deletions

View File

@ -11,7 +11,7 @@ var (
GitDescribe string GitDescribe string
// Version is main version number that is being run at the moment. // Version is main version number that is being run at the moment.
Version = "4.9.1" Version = "4.9.3"
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string) // VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release // then it means that it is a final release. Otherwise, this is a pre-release

View File

@ -1,4 +1,4 @@
FROM mikefarah/yq:4.9.1 FROM mikefarah/yq:4.9.3
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh

View File

@ -12,6 +12,14 @@ type Context struct {
DontAutoCreate bool DontAutoCreate bool
} }
func (n *Context) SingleReadonlyChildContext(candidate *CandidateNode) Context {
list := list.New()
list.PushBack(candidate)
newContext := n.ChildContext(list)
newContext.DontAutoCreate = true
return newContext
}
func (n *Context) SingleChildContext(candidate *CandidateNode) Context { func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
list := list.New() list := list.New()
list.PushBack(candidate) list.PushBack(candidate)
@ -52,3 +60,9 @@ func (n *Context) Clone() Context {
} }
return clone return clone
} }
func (n *Context) ReadOnlyClone() Context {
clone := n.Clone()
clone.DontAutoCreate = true
return clone
}

View File

@ -169,6 +169,22 @@ b: &meow purr
a: *meow a: *meow
``` ```
## Set alias to blank does nothing
Given a sample.yml file of:
```yaml
b: &meow purr
a: cat
```
then
```bash
yq eval '.a alias = ""' sample.yml
```
will output
```yaml
b: &meow purr
a: cat
```
## Set alias relatively using assign-update ## Set alias relatively using assign-update
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml

View File

@ -71,7 +71,7 @@ dog
Both sides have now been evaluated, so now the operator copies across the value from the RHS to the value on the LHS, and it returns the now updated context: Both sides have now been evaluated, so now the operator copies across the value from the RHS to the value on the LHS, and it returns the now updated context:
```yaml ```yaml
a: cat a: dog
b: dog b: dog
``` ```

View File

@ -54,7 +54,7 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
opStack = opStack[0 : len(opStack)-1] opStack = opStack[0 : len(opStack)-1]
log.Debugf("deleteing open bracket from opstack") log.Debugf("deleteing open bracket from opstack")
//and append a collect to the opStack //and append a collect to the result
// hack - see if there's the optional traverse flag // hack - see if there's the optional traverse flag
// on the close op - move it to the collect op. // on the close op - move it to the collect op.
// allows for .["cat"]? // allows for .["cat"]?
@ -68,6 +68,12 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
result = append(result, &Operation{OperationType: shortPipeOpType}) result = append(result, &Operation{OperationType: shortPipeOpType})
log.Debugf("put shortpipe onto the result") log.Debugf("put shortpipe onto the result")
//traverseArrayCollect is a sneaky op that needs to be included too
//when closing a []
if len(opStack) > 0 && opStack[len(opStack)-1].Operation != nil && opStack[len(opStack)-1].Operation.OperationType == traverseArrayOpType {
opStack, result = popOpToResult(opStack, result)
}
case closeBracket: case closeBracket:
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket { for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
opStack, result = popOpToResult(opStack, result) opStack, result = popOpToResult(opStack, result)

View File

@ -12,6 +12,16 @@ var pathTests = []struct {
expectedTokens []interface{} expectedTokens []interface{}
expectedPostFix []interface{} expectedPostFix []interface{}
}{ }{
{
`.[0]`,
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
append(make([]interface{}, 0), "SELF", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
},
{
`.[0][1]`,
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "0 (int64)", "]", "TRAVERSE_ARRAY", "[", "1 (int64)", "]"),
append(make([]interface{}, 0), "SELF", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "1 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
},
{ {
`"\""`, `"\""`,
append(make([]interface{}, 0), "\" (string)"), append(make([]interface{}, 0), "\" (string)"),

View File

@ -411,16 +411,19 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
skipNextToken = false skipNextToken = false
currentToken := tokens[index] currentToken := tokens[index]
log.Debug("processing %v", currentToken.toString(true))
if currentToken.TokenType == traverseArrayCollect { if currentToken.TokenType == traverseArrayCollect {
//need to put a traverse array then a collect currentToken //need to put a traverse array then a collect currentToken
// do this by adding traverse then converting currentToken to collect // do this by adding traverse then converting currentToken to collect
if index == 0 || tokens[index-1].TokenType != operationToken || if index == 0 || tokens[index-1].TokenType != operationToken ||
tokens[index-1].Operation.OperationType != traversePathOpType { tokens[index-1].Operation.OperationType != traversePathOpType {
log.Debug(" adding self")
op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"} op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op}) postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
} }
log.Debug(" adding traverse array")
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"} op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op}) postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
@ -431,17 +434,19 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
if index != len(tokens)-1 && currentToken.AssignOperation != nil && if index != len(tokens)-1 && currentToken.AssignOperation != nil &&
tokens[index+1].TokenType == operationToken && tokens[index+1].TokenType == operationToken &&
tokens[index+1].Operation.OperationType == assignOpType { tokens[index+1].Operation.OperationType == assignOpType {
log.Debug(" its an update assign")
currentToken.Operation = currentToken.AssignOperation currentToken.Operation = currentToken.AssignOperation
currentToken.Operation.UpdateAssign = tokens[index+1].Operation.UpdateAssign currentToken.Operation.UpdateAssign = tokens[index+1].Operation.UpdateAssign
skipNextToken = true skipNextToken = true
} }
log.Debug(" adding token to the fixed list")
postProcessedTokens = append(postProcessedTokens, currentToken) postProcessedTokens = append(postProcessedTokens, currentToken)
if index != len(tokens)-1 && if index != len(tokens)-1 &&
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) || ((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) { (currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
log.Debug(" adding empty")
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"} op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op}) postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
} }
@ -449,12 +454,19 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
if index != len(tokens)-1 && currentToken.CheckForPostTraverse && if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
tokens[index+1].TokenType == operationToken && tokens[index+1].TokenType == operationToken &&
tokens[index+1].Operation.OperationType == traversePathOpType { tokens[index+1].Operation.OperationType == traversePathOpType {
log.Debug(" adding pipe because the next thing is traverse")
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"} op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op}) postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
} }
if index != len(tokens)-1 && currentToken.CheckForPostTraverse && if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
tokens[index+1].TokenType == openCollect { tokens[index+1].TokenType == openCollect {
// if tokens[index].TokenType == closeCollect {
// log.Debug(" adding pipe because next is opencollect")
// op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
// postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
// }
log.Debug(" adding traverArray because next is opencollect")
op := &Operation{OperationType: traverseArrayOpType} op := &Operation{OperationType: traverseArrayOpType}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op}) postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
} }

View File

@ -20,9 +20,6 @@ type operationType struct {
Handler operatorHandler Handler operatorHandler
} }
// operators TODO:
// - mergeEmpty (sets only if the document is empty, do I do that now?)
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator} var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator} var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator} var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}

View File

@ -39,7 +39,7 @@ func toNodes(candidate *CandidateNode) []*yaml.Node {
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("Add operator") log.Debugf("Add operator")
return crossFunction(d, context, expressionNode, add, false) return crossFunction(d, context.ReadOnlyClone(), expressionNode, add, false)
} }
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {

View File

@ -14,6 +14,22 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[1 a], (!!int)::3\n", "D0, P[1 a], (!!int)::3\n",
}, },
}, },
{
skipDoc: true,
document: `{}`,
expression: "(.a + .b) as $x",
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{
skipDoc: true,
document: `a: 0`,
expression: ".a += .b.c",
expected: []string{
"D0, P[], (doc)::a: 0\n",
},
},
{ {
description: "Concatenate and assign arrays", description: "Concatenate and assign arrays",
document: `{a: {val: thing, b: [cat,dog]}}`, document: `{a: {val: thing, b: [cat,dog]}}`,

View File

@ -2,7 +2,7 @@ package yqlib
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- alternative") log.Debugf("-- alternative")
return crossFunction(d, context, expressionNode, alternativeFunc, true) return crossFunction(d, context.ReadOnlyClone(), expressionNode, alternativeFunc, true)
} }
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {

View File

@ -5,6 +5,20 @@ import (
) )
var alternativeOperatorScenarios = []expressionScenario{ var alternativeOperatorScenarios = []expressionScenario{
{
skipDoc: true,
expression: `.b // .c`,
document: `a: bridge`,
expected: []string{},
},
{
skipDoc: true,
expression: `(.b // "hello") as $x`,
document: `a: bridge`,
expected: []string{
"D0, P[], (doc)::a: bridge\n",
},
},
{ {
description: "LHS is defined", description: "LHS is defined",
expression: `.a // "hello"`, expression: `.a // "hello"`,

View File

@ -12,7 +12,7 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
aliasName := "" aliasName := ""
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -32,7 +32,7 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
log.Debugf("Setting aliasName : %v", candidate.GetKey()) log.Debugf("Setting aliasName : %v", candidate.GetKey())
if expressionNode.Operation.UpdateAssign { if expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -41,8 +41,10 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
} }
} }
candidate.Node.Kind = yaml.AliasNode if aliasName != "" {
candidate.Node.Value = aliasName candidate.Node.Kind = yaml.AliasNode
candidate.Node.Value = aliasName
}
} }
return context, nil return context, nil
} }
@ -66,7 +68,7 @@ func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode
anchorName := "" anchorName := ""
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -87,7 +89,7 @@ func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode
log.Debugf("Setting anchorName of : %v", candidate.GetKey()) log.Debugf("Setting anchorName of : %v", candidate.GetKey())
if expressionNode.Operation.UpdateAssign { if expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -58,6 +58,22 @@ var anchorOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: &cat {b: cat}\n", "D0, P[], (doc)::a: &cat {b: cat}\n",
}, },
}, },
{
skipDoc: true,
document: `a: {c: cat}`,
expression: `.a anchor |= .b`,
expected: []string{
"D0, P[], (doc)::a: {c: cat}\n",
},
},
{
skipDoc: true,
document: `a: {c: cat}`,
expression: `.a anchor = .b`,
expected: []string{
"D0, P[], (doc)::a: {c: cat}\n",
},
},
{ {
description: "Get alias", description: "Get alias",
document: `{b: &billyBob meow, a: *billyBob}`, document: `{b: &billyBob meow, a: *billyBob}`,
@ -74,6 +90,30 @@ var anchorOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::{b: &meow purr, a: *meow}\n", "D0, P[], (doc)::{b: &meow purr, a: *meow}\n",
}, },
}, },
{
description: "Set alias to blank does nothing",
document: `{b: &meow purr, a: cat}`,
expression: `.a alias = ""`,
expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
},
},
{
skipDoc: true,
document: `{b: &meow purr, a: cat}`,
expression: `.a alias = .c`,
expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
},
},
{
skipDoc: true,
document: `{b: &meow purr, a: cat}`,
expression: `.a alias |= .c`,
expected: []string{
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
},
},
{ {
description: "Set alias relatively using assign-update", description: "Set alias relatively using assign-update",
document: `{b: &meow purr, a: {f: meow}}`, document: `{b: &meow purr, a: {f: meow}}`,
@ -159,7 +199,7 @@ foobar:
}, },
} }
func TestAnchorAliaseOperatorScenarios(t *testing.T) { func TestAnchorAliasOperatorScenarios(t *testing.T) {
for _, tt := range anchorOperatorScenarios { for _, tt := range anchorOperatorScenarios {
testScenario(t, &tt) testScenario(t, &tt)
} }

View File

@ -7,7 +7,7 @@ func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode
} }
var rhs Context var rhs Context
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err = d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err = d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
} }
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
@ -44,7 +44,7 @@ func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionN
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err

View File

@ -12,6 +12,22 @@ var assignOperatorScenarios = []expressionScenario{
"D0, P[], ()::a:\n b: cat\nx: frog\n", "D0, P[], ()::a:\n b: cat\nx: frog\n",
}, },
}, },
{
skipDoc: true,
document: "{}",
expression: `.a |= .b`,
expected: []string{
"D0, P[], (doc)::{a: null}\n",
},
},
{
skipDoc: true,
document: "{}",
expression: `.a = .b`,
expected: []string{
"D0, P[], (doc)::{a: null}\n",
},
},
{ {
description: "Update node to be the child value", description: "Update node to be the child value",
document: `{a: {b: {g: foof}}}`, document: `{a: {b: {g: foof}}}`,

View File

@ -71,7 +71,7 @@ func findBoolean(wantBool bool, d *dataTreeNavigator, context Context, expressio
if expressionNode != nil { if expressionNode != nil {
//need to evaluate the expression against the node //need to evaluate the expression against the node
candidate := &CandidateNode{Node: node} candidate := &CandidateNode{Node: node}
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -134,7 +134,7 @@ func anyOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- orOp") log.Debugf("-- orOp")
return crossFunction(d, context, expressionNode, performBoolOp( return crossFunction(d, context.ReadOnlyClone(), expressionNode, performBoolOp(
func(b1 bool, b2 bool) bool { func(b1 bool, b2 bool) bool {
log.Debugf("-- peformingOrOp with %v and %v", b1, b2) log.Debugf("-- peformingOrOp with %v and %v", b1, b2)
return b1 || b2 return b1 || b2
@ -143,7 +143,7 @@ func orOperator(d *dataTreeNavigator, context Context, expressionNode *Expressio
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- AndOp") log.Debugf("-- AndOp")
return crossFunction(d, context, expressionNode, performBoolOp( return crossFunction(d, context.ReadOnlyClone(), expressionNode, performBoolOp(
func(b1 bool, b2 bool) bool { func(b1 bool, b2 bool) bool {
return b1 && b2 return b1 && b2
}), true) }), true)

View File

@ -12,6 +12,14 @@ var booleanOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{
skipDoc: true,
document: "b: hi",
expression: `.a or .c`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: "b: hi", document: "b: hi",
@ -67,6 +75,22 @@ var booleanOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: true\nb: false\n", "D0, P[], (doc)::a: true\nb: false\n",
}, },
}, },
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `any_c(.name == "harry") as $c`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
},
},
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `all_c(.name == "harry") as $c`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: `[false, false]`, document: `[false, false]`,
@ -125,6 +149,22 @@ var booleanOperatorScenarios = []expressionScenario{
"D0, P[b], (!!bool)::true\n", "D0, P[b], (!!bool)::true\n",
}, },
}, },
{
skipDoc: true,
document: `{}`,
expression: `(.a.b or .c) as $x`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{
skipDoc: true,
document: `{}`,
expression: `(.a.b and .c) as $x`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{ {
description: "Not true is false", description: "Not true is false",
expression: `true | not`, expression: `true | not`,

View File

@ -17,9 +17,12 @@ import (
... ...
*/ */
func collectObjectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func collectObjectOperator(d *dataTreeNavigator, originalContext Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- collectObjectOperation") log.Debugf("-- collectObjectOperation")
context := originalContext.Clone()
context.DontAutoCreate = false
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"} node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"}
candidate := &CandidateNode{Node: node} candidate := &CandidateNode{Node: node}

View File

@ -5,6 +5,30 @@ import (
) )
var collectObjectOperatorScenarios = []expressionScenario{ var collectObjectOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: "a: []",
expression: `.a += [{"key": "att2", "value": "val2"}]`,
expected: []string{
"D0, P[], (doc)::a: [{key: att2, value: val2}]\n",
},
},
{
skipDoc: true,
document: "",
expression: `.a += {"key": "att2", "value": "val2"}`,
expected: []string{
"D0, P[], ()::a:\n key: att2\n value: val2\n",
},
},
{
skipDoc: true,
document: "",
expression: `.a += [0]`,
expected: []string{
"D0, P[], ()::a:\n - 0\n",
},
},
{ {
description: `Collect empty object`, description: `Collect empty object`,
document: ``, document: ``,

View File

@ -5,6 +5,14 @@ import (
) )
var collectOperatorScenarios = []expressionScenario{ var collectOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: ``,
expression: `.a += [0]`,
expected: []string{
"D0, P[], ()::a:\n - 0\n",
},
},
{ {
description: "Collect empty", description: "Collect empty",
document: ``, document: ``,

View File

@ -27,7 +27,7 @@ func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNod
comment := "" comment := ""
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -41,7 +41,7 @@ func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNod
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
if expressionNode.Operation.UpdateAssign { if expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -62,6 +62,22 @@ var commentOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: cat\n\n# cat\n", "D0, P[], (doc)::a: cat\n\n# cat\n",
}, },
}, },
{
skipDoc: true,
document: `a: cat`,
expression: `. footComment=.b.d`,
expected: []string{
"D0, P[], (doc)::a: cat\n",
},
},
{
skipDoc: true,
document: `a: cat`,
expression: `. footComment|=.b.d`,
expected: []string{
"D0, P[], (doc)::a: cat\n",
},
},
{ {
description: "Remove comment", description: "Remove comment",
document: "a: cat # comment\nb: dog # leave this", document: "a: cat # comment\nb: dog # leave this",

View File

@ -7,9 +7,7 @@ import (
) )
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
contextToUse := context.Clone() nodesToDelete, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
contextToUse.DontAutoCreate = true
nodesToDelete, err := d.GetMatchingNodes(contextToUse, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err

View File

@ -4,7 +4,7 @@ import "gopkg.in/yaml.v3"
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- equalsOperation") log.Debugf("-- equalsOperation")
return crossFunction(d, context, expressionNode, isEquals(false), true) return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(false), true)
} }
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
@ -50,5 +50,5 @@ func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *Candid
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- notEqualsOperator") log.Debugf("-- notEqualsOperator")
return crossFunction(d, context, expressionNode, isEquals(true), true) return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(true), true)
} }

View File

@ -5,6 +5,13 @@ import (
) )
var equalsOperatorScenarios = []expressionScenario{ var equalsOperatorScenarios = []expressionScenario{
{
skipDoc: true,
expression: ".a == .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: "cat", document: "cat",
@ -14,6 +21,38 @@ var equalsOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::false\n", "D0, P[], (!!bool)::false\n",
}, },
}, },
{
skipDoc: true,
document: "{}",
expression: "(.a == .b) as $x",
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{
skipDoc: true,
document: "{}",
expression: ".a == .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
},
},
{
skipDoc: true,
document: "{}",
expression: "(.a != .b) as $x",
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{
skipDoc: true,
document: "{}",
expression: ".a != .b",
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: "{a: {b: 10}}", document: "{a: {b: 10}}",

View File

@ -12,14 +12,19 @@ func hasOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
log.Debugf("-- hasOperation") log.Debugf("-- hasOperation")
var results = list.New() var results = list.New()
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
wanted := rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
wantedKey := wanted.Value
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
wantedKey := "null"
wanted := &yaml.Node{Tag: "!!null"}
if rhs.MatchingNodes.Len() != 0 {
wanted = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
wantedKey = wanted.Value
}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)

View File

@ -13,6 +13,22 @@ var hasOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{
skipDoc: true,
document: `a: hello`,
expression: `has(.b) as $c`,
expected: []string{
"D0, P[], (doc)::a: hello\n",
},
},
{
skipDoc: true,
document: `a: hello`,
expression: `has(.b)`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{ {
description: "Has map key", description: "Has map key",
document: `- a: "yes" document: `- a: "yes"

View File

@ -11,9 +11,7 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
childContext := context.SingleChildContext(candidate) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
childContext.DontAutoCreate = true
rhs, err := d.GetMatchingNodes(childContext, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err

View File

@ -10,7 +10,7 @@ func sortKeysOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -13,6 +13,14 @@ var sortKeysOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::{a: blah, b: bing, c: frog}\n", "D0, P[], (doc)::{a: blah, b: bing, c: frog}\n",
}, },
}, },
{
skipDoc: true,
document: `{c: frog}`,
expression: `sortKeys(.d)`,
expected: []string{
"D0, P[], (doc)::{c: frog}\n",
},
},
{ {
description: "Sort keys recursively", description: "Sort keys recursively",
subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted", subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted",

View File

@ -13,7 +13,7 @@ func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, contex
regEx := "" regEx := ""
replacementText := "" replacementText := ""
regExNodes, err := d.GetMatchingNodes(context, block.Lhs) regExNodes, err := d.GetMatchingNodes(context.ReadOnlyClone(), block.Lhs)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -78,7 +78,7 @@ func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *E
log.Debugf("-- joinStringOperator") log.Debugf("-- joinStringOperator")
joinStr := "" joinStr := ""
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -119,7 +119,7 @@ func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *
log.Debugf("-- splitStringOperator") log.Debugf("-- splitStringOperator")
splitStr := "" splitStr := ""
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -31,7 +31,7 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
log.Debugf("AssignStyleOperator: %v") log.Debugf("AssignStyleOperator: %v")
var style yaml.Style var style yaml.Style
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -54,7 +54,7 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
log.Debugf("Setting style of : %v", candidate.GetKey()) log.Debugf("Setting style of : %v", candidate.GetKey())
if expressionNode.Operation.UpdateAssign { if expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -25,7 +25,7 @@ func subtractAssignOperator(d *dataTreeNavigator, context Context, expressionNod
func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("Subtract operator") log.Debugf("Subtract operator")
return crossFunction(d, context, expressionNode, subtract, false) return crossFunction(d, context.ReadOnlyClone(), expressionNode, subtract, false)
} }
func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {

View File

@ -5,6 +5,14 @@ import (
) )
var subtractOperatorScenarios = []expressionScenario{ var subtractOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: "(.a - .b) as $x",
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{ {
description: "Number subtraction - float", description: "Number subtraction - float",
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.", subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",

View File

@ -12,7 +12,7 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
tag := "" tag := ""
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -32,7 +32,7 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
log.Debugf("Setting tag of : %v", candidate.GetKey()) log.Debugf("Setting tag of : %v", candidate.GetKey())
if expressionNode.Operation.UpdateAssign { if expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -89,7 +89,7 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
// rhs is a collect expression that will yield indexes to retreive of the arrays // rhs is a collect expression that will yield indexes to retreive of the arrays
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
@ -308,6 +308,6 @@ func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *C
func traverseArray(candidate *CandidateNode, operation *Operation, prefs traversePreferences) (*list.List, error) { func traverseArray(candidate *CandidateNode, operation *Operation, prefs traversePreferences) (*list.List, error) {
log.Debug("operation Value %v", operation.Value) log.Debug("operation Value %v", operation.Value)
indices := []*yaml.Node{&yaml.Node{Value: operation.StringValue}} indices := []*yaml.Node{{Value: operation.StringValue}}
return traverseArrayWithIndices(candidate, indices, prefs) return traverseArrayWithIndices(candidate, indices, prefs)
} }

View File

@ -27,6 +27,22 @@ foobar:
` `
var traversePathOperatorScenarios = []expressionScenario{ var traversePathOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `[[1]]`,
expression: `.[0][0]`,
expected: []string{
"D0, P[0 0], (!!int)::1\n",
},
},
{
skipDoc: true,
document: `[[[1]]]`,
expression: `.[0][0][0]`,
expected: []string{
"D0, P[0 0 0], (!!int)::1\n",
},
},
{ {
description: "Simple map navigation", description: "Simple map navigation",
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
@ -78,6 +94,14 @@ var traversePathOperatorScenarios = []expressionScenario{
"D0, P[flying fox], (!!str)::frog\n", "D0, P[flying fox], (!!str)::frog\n",
}, },
}, },
{
skipDoc: true,
document: `c: dog`,
expression: `.[.a.b] as $x`,
expected: []string{
"D0, P[], (doc)::c: dog\n",
},
},
{ {
description: "Dynamic keys", description: "Dynamic keys",
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`, subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,

View File

@ -5,6 +5,20 @@ import (
) )
var unionOperatorScenarios = []expressionScenario{ var unionOperatorScenarios = []expressionScenario{
// {
// skipDoc: true,
// document: "{}",
// expression: `(.a, .b.c) as $x`,
// expected: []string{
// "D0, P[], (doc)::{}\n",
// },
// },
// {
// skipDoc: true,
// document: "{}",
// expression: `(.a, .b.c)`,
// expected: []string{},
// },
{ {
description: "Combine scalars", description: "Combine scalars",
expression: `1, true, "cat"`, expression: `1, true, "cat"`,

View File

@ -31,15 +31,20 @@ func uniqueBy(d *dataTreeNavigator, context Context, expressionNode *ExpressionN
var newMatches = orderedmap.NewOrderedMap() var newMatches = orderedmap.NewOrderedMap()
for _, node := range candidateNode.Content { for _, node := range candidateNode.Content {
child := &CandidateNode{Node: node} child := &CandidateNode{Node: node}
rhs, err := d.GetMatchingNodes(context.SingleChildContext(child), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(child), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
first := rhs.MatchingNodes.Front() keyValue := "null"
keyCandidate := first.Value.(*CandidateNode)
keyValue := keyCandidate.Node.Value if rhs.MatchingNodes.Len() > 0 {
first := rhs.MatchingNodes.Front()
keyCandidate := first.Value.(*CandidateNode)
keyValue = keyCandidate.Node.Value
}
_, exists := newMatches.Get(keyValue) _, exists := newMatches.Get(keyValue)
if !exists { if !exists {

View File

@ -39,6 +39,22 @@ var uniqueOperatorScenarios = []expressionScenario{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {name: billy, pet: dog}\n", "D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {name: billy, pet: dog}\n",
}, },
}, },
{
skipDoc: true,
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
expression: `unique_by(.name)`,
expected: []string{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {pet: fish}\n",
},
},
{
skipDoc: true,
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
expression: `unique_by(.cat.dog)`,
expected: []string{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n",
},
},
} }
func TestUniqueOperatorScenarios(t *testing.T) { func TestUniqueOperatorScenarios(t *testing.T) {

View File

@ -16,7 +16,7 @@ func getVariableOperator(d *dataTreeNavigator, context Context, expressionNode *
} }
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) lhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Lhs)
if err != nil { if err != nil {
return Context{}, nil return Context{}, nil
} }

View File

@ -5,6 +5,14 @@ import (
) )
var variableOperatorScenarios = []expressionScenario{ var variableOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: `.a.b as $foo`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
},
{ {
description: "Single value variable", description: "Single value variable",
document: `a: cat`, document: `a: cat`,

View File

@ -31,7 +31,9 @@ func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *Candidat
if err != nil { if err != nil {
return err return err
} }
results.PushBack(resultCandidate) if resultCandidate != nil {
results.PushBack(resultCandidate)
}
return nil return nil
} }
@ -42,7 +44,9 @@ func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *Candidat
if err != nil { if err != nil {
return err return err
} }
results.PushBack(resultCandidate) if resultCandidate != nil {
results.PushBack(resultCandidate)
}
} }
return nil return nil
} }

View File

@ -1,5 +1,6 @@
#!/bin/bash #!/bin/bash
find . \( -path ./vendor \) -prune -o -name "*.go" -exec goimports -w {} \; find . \( -path ./vendor \) -prune -o -name "*.go" -exec goimports -w {} \;
gofmt -w -s .
go mod tidy go mod tidy
go mod vendor go mod vendor

View File

@ -1,5 +1,5 @@
name: yq name: yq
version: '4.9.1' version: '4.9.3'
summary: A lightweight and portable command-line YAML processor summary: A lightweight and portable command-line YAML processor
description: | description: |
The aim of the project is to be the jq or sed of yaml files. The aim of the project is to be the jq or sed of yaml files.