Added with operator

This commit is contained in:
Mike Farah 2021-09-12 21:52:02 +10:00
parent 0c3334d838
commit 6002604251
11 changed files with 91 additions and 13 deletions

View File

@ -1,3 +1,5 @@
# a1
a: 1
# a2
Resources:
S3Bucket:
Type: AWS::CloudFormation::Stack
Properties:
BucketName: !Ref MyBucketNameA

View File

@ -18,8 +18,6 @@ a:
```
## Update and set style of a particular node using path variables
You can use a variable reference to re-use a path
Given a sample.yml file of:
```yaml
a:
@ -28,7 +26,7 @@ a:
```
then
```bash
yq eval '.a.b ref $x | $x = "new" | $x style="double"' sample.yml
yq eval 'with(.a.b ; . = "new" | . style="double")' sample.yml
```
will output
```yaml

View File

@ -75,6 +75,8 @@ b: a_value
```
## Use ref to reference a path repeatedly
Note: You may find the `with` operator more useful.
Given a sample.yml file of:
```yaml
a:

20
pkg/yqlib/doc/With.md Normal file
View File

@ -0,0 +1,20 @@
Use the `with` operator to conveniently make multiple updates to a deeply nested path.
## Update and style
Given a sample.yml file of:
```yaml
a:
deeply:
nested: value
```
then
```bash
yq eval 'with(.a.deeply.nested ; . = "newValue" | . style="single")' sample.yml
```
will output
```yaml
a:
deeply:
nested: 'newValue'
```

View File

@ -0,0 +1 @@
Use the `with` operator to conveniently make multiple updates to a deeply nested path.

View File

@ -315,6 +315,8 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`from_entries`), opToken(fromEntriesOpType))
lexer.Add([]byte(`with_entries`), opToken(withEntriesOpType))
lexer.Add([]byte(`with`), opToken(withOpType))
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: true}))
lexer.Add([]byte(`headComment`), opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{HeadComment: true}))

View File

@ -69,6 +69,8 @@ var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence:
var fromEntriesOpType = &operationType{Type: "FROM_ENTRIES", NumArgs: 0, Precedence: 50, Handler: fromEntriesOperator}
var withEntriesOpType = &operationType{Type: "WITH_ENTRIES", NumArgs: 1, Precedence: 50, Handler: withEntriesOperator}
var withOpType = &operationType{Type: "WITH", NumArgs: 1, Precedence: 50, Handler: withOperator}
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}

View File

@ -15,9 +15,8 @@ var styleOperatorScenarios = []expressionScenario{
},
{
description: "Update and set style of a particular node using path variables",
subdescription: "You can use a variable reference to re-use a path",
document: `a: {b: thing, c: something}`,
expression: `.a.b ref $x | $x = "new" | $x style="double"`,
expression: `with(.a.b ; . = "new" | . style="double")`,
expected: []string{
"D0, P[], (doc)::a: {b: \"new\", c: something}\n",
},

View File

@ -53,6 +53,7 @@ var variableOperatorScenarios = []expressionScenario{
},
{
description: "Use ref to reference a path repeatedly",
subdescription: "Note: You may find the `with` operator more useful.",
document: `a: {b: thing, c: something}`,
expression: `.a.b ref $x | $x = "new" | $x style="double"`,
expected: []string{

View File

@ -0,0 +1,30 @@
package yqlib
import "fmt"
func withOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- withOperator")
// with(path, exp)
if expressionNode.Rhs.Operation.OperationType != blockOpType {
return Context{}, fmt.Errorf("with must be given a block, got %v instead", expressionNode.Rhs.Operation.OperationType.Type)
}
pathExp := expressionNode.Rhs.Lhs
updateContext, err := d.GetMatchingNodes(context, pathExp)
if err != nil {
return Context{}, err
}
updateExp := expressionNode.Rhs.Rhs
_, err = d.GetMatchingNodes(updateContext, updateExp)
if err != nil {
return Context{}, err
}
return context, nil
}

View File

@ -0,0 +1,21 @@
package yqlib
import "testing"
var withOperatorScenarios = []expressionScenario{
{
description: "Update and style",
document: `a: {deeply: {nested: value}}`,
expression: `with(.a.deeply.nested ; . = "newValue" | . style="single")`,
expected: []string{
"D0, P[], (doc)::a: {deeply: {nested: 'newValue'}}\n",
},
},
}
func TestWithOperatorScenarios(t *testing.T) {
for _, tt := range withOperatorScenarios {
testScenario(t, &tt)
}
documentScenarios(t, "With", withOperatorScenarios)
}