mirror of
https://github.com/mikefarah/yq.git
synced 2025-02-25 17:15:48 +00:00
Updated var to work like jq #934
This commit is contained in:
parent
1cfbbde796
commit
b2ee131a4c
@ -61,9 +61,31 @@ func (n *Context) ToString() string {
|
|||||||
return result + NodesToString(n.MatchingNodes)
|
return result + NodesToString(n.MatchingNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Context) DeepClone() Context {
|
||||||
|
clone := Context{}
|
||||||
|
err := copier.Copy(&clone, n)
|
||||||
|
// copier doesn't do lists properly for some reason
|
||||||
|
clone.MatchingNodes = list.New()
|
||||||
|
for el := n.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
clonedNode, err := el.Value.(*CandidateNode).Copy()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error cloning context :(")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
clone.MatchingNodes.PushBack(clonedNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error cloning context :(")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Context) Clone() Context {
|
func (n *Context) Clone() Context {
|
||||||
clone := Context{}
|
clone := Context{}
|
||||||
err := copier.Copy(&clone, n)
|
err := copier.Copy(&clone, n)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error cloning context :(")
|
log.Error("Error cloning context :(")
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -18,7 +18,7 @@ a:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Update and set style of a particular node using path variables
|
## Update and set style of a particular node using path variables
|
||||||
You can use a variable to re-use a path
|
You can use a variable reference to re-use a path
|
||||||
|
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@ -28,7 +28,7 @@ a:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yq eval '.a.b as $x | $x = "new" | $x style="double"' sample.yml
|
yq eval '.a.b ref $x | $x = "new" | $x style="double"' sample.yml
|
||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
Like the `jq` equivalents, variables are sometimes required for the more complex expressions (or swapping values between fields).
|
||||||
|
|
||||||
|
Note that there is also an additional `ref` operator that holds a reference (instead of a copy) of the path, allowing you to make multiple changes to the same path.
|
||||||
|
|
||||||
## Single value variable
|
## Single value variable
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
@ -56,3 +58,37 @@ title: A well-written article
|
|||||||
author: Person McPherson
|
author: Person McPherson
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using variables to swap values
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: a_value
|
||||||
|
b: b_value
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a as $x | .b as $y | .b = $x | .a = $y' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: b_value
|
||||||
|
b: a_value
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use ref to reference a path repeatedly
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b: thing
|
||||||
|
c: something
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a.b ref $x | $x = "new" | $x style="double"' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b: "new"
|
||||||
|
c: something
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
Like the `jq` equivalents, variables are sometimes required for the more complex expressions (or swapping values between fields).
|
||||||
|
|
||||||
|
Note that there is also an additional `ref` operator that holds a reference (instead of a copy) of the path, allowing you to make multiple changes to the same path.
|
||||||
|
@ -367,7 +367,8 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`\-`), opToken(subtractOpType))
|
lexer.Add([]byte(`\-`), opToken(subtractOpType))
|
||||||
lexer.Add([]byte(`\-=`), opToken(subtractAssignOpType))
|
lexer.Add([]byte(`\-=`), opToken(subtractAssignOpType))
|
||||||
lexer.Add([]byte(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
lexer.Add([]byte(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
||||||
lexer.Add([]byte(`as`), opToken(assignVariableOpType))
|
lexer.Add([]byte(`as`), opTokenWithPrefs(assignVariableOpType, nil, assignVarPreferences{}))
|
||||||
|
lexer.Add([]byte(`ref`), opTokenWithPrefs(assignVariableOpType, nil, assignVarPreferences{IsReference: true}))
|
||||||
|
|
||||||
err := lexer.CompileNFA()
|
err := lexer.CompileNFA()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -20,8 +20,7 @@ import (
|
|||||||
func collectObjectOperator(d *dataTreeNavigator, originalContext 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 := originalContext.WritableClone()
|
||||||
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: "{}"}
|
||||||
|
@ -15,9 +15,9 @@ var styleOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Update and set style of a particular node using path variables",
|
description: "Update and set style of a particular node using path variables",
|
||||||
subdescription: "You can use a variable to re-use a path",
|
subdescription: "You can use a variable reference to re-use a path",
|
||||||
document: `a: {b: thing, c: something}`,
|
document: `a: {b: thing, c: something}`,
|
||||||
expression: `.a.b as $x | $x = "new" | $x style="double"`,
|
expression: `.a.b ref $x | $x = "new" | $x style="double"`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (doc)::a: {b: \"new\", c: something}\n",
|
"D0, P[], (doc)::a: {b: \"new\", c: something}\n",
|
||||||
},
|
},
|
||||||
|
@ -15,6 +15,10 @@ func getVariableOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
return context.ChildContext(result), nil
|
return context.ChildContext(result), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type assignVarPreferences struct {
|
||||||
|
IsReference bool
|
||||||
|
}
|
||||||
|
|
||||||
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.ReadOnlyClone(), expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -24,6 +28,15 @@ func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNod
|
|||||||
return Context{}, fmt.Errorf("RHS of 'as' operator must be a variable name e.g. $foo")
|
return Context{}, fmt.Errorf("RHS of 'as' operator must be a variable name e.g. $foo")
|
||||||
}
|
}
|
||||||
variableName := expressionNode.Rhs.Operation.StringValue
|
variableName := expressionNode.Rhs.Operation.StringValue
|
||||||
context.SetVariable(variableName, lhs.MatchingNodes)
|
|
||||||
|
prefs := expressionNode.Operation.Preferences.(assignVarPreferences)
|
||||||
|
|
||||||
|
var variableValue *list.List
|
||||||
|
if prefs.IsReference {
|
||||||
|
variableValue = lhs.MatchingNodes
|
||||||
|
} else {
|
||||||
|
variableValue = lhs.DeepClone().MatchingNodes
|
||||||
|
}
|
||||||
|
context.SetVariable(variableName, variableValue)
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
@ -34,15 +34,31 @@ var variableOperatorScenarios = []expressionScenario{
|
|||||||
description: "Using variables as a lookup",
|
description: "Using variables as a lookup",
|
||||||
subdescription: "Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)",
|
subdescription: "Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)",
|
||||||
document: `{"posts": [{"title": "Frist psot", "author": "anon"},
|
document: `{"posts": [{"title": "Frist psot", "author": "anon"},
|
||||||
{"title": "A well-written article", "author": "person1"}],
|
{"title": "A well-written article", "author": "person1"}],
|
||||||
"realnames": {"anon": "Anonymous Coward",
|
"realnames": {"anon": "Anonymous Coward",
|
||||||
"person1": "Person McPherson"}}`,
|
"person1": "Person McPherson"}}`,
|
||||||
expression: `.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}`,
|
expression: `.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!map)::title: \"Frist psot\"\nauthor: \"Anonymous Coward\"\n",
|
"D0, P[], (!!map)::title: \"Frist psot\"\nauthor: \"Anonymous Coward\"\n",
|
||||||
"D0, P[], (!!map)::title: \"A well-written article\"\nauthor: \"Person McPherson\"\n",
|
"D0, P[], (!!map)::title: \"A well-written article\"\nauthor: \"Person McPherson\"\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Using variables to swap values",
|
||||||
|
document: "a: a_value\nb: b_value",
|
||||||
|
expression: `.a as $x | .b as $y | .b = $x | .a = $y`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: b_value\nb: a_value\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Use ref to reference a path repeatedly",
|
||||||
|
document: `a: {b: thing, c: something}`,
|
||||||
|
expression: `.a.b ref $x | $x = "new" | $x style="double"`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: {b: \"new\", c: something}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVariableOperatorScenarios(t *testing.T) {
|
func TestVariableOperatorScenarios(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user