Added string case operators

This commit is contained in:
Mike Farah 2022-02-22 16:17:23 +11:00
parent 71706af3d4
commit d7b158f855
5 changed files with 106 additions and 0 deletions

View File

@ -49,6 +49,38 @@ Note that versions prior to 4.18 require the 'eval/e' command to be specified.&#
`yq e <exp> <file>` `yq e <exp> <file>`
{% endhint %} {% endhint %}
## To up (upper) case
Works with unicode characters
Given a sample.yml file of:
```yaml
água
```
then
```bash
yq 'upcase' sample.yml
```
will output
```yaml
ÁGUA
```
## To down (lower) case
Works with unicode characters
Given a sample.yml file of:
```yaml
ÁgUA
```
then
```bash
yq 'downcase' sample.yml
```
will output
```yaml
água
```
## Join strings ## Join strings
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml

View File

@ -414,6 +414,12 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`capture`), opToken(captureOpType)) lexer.Add([]byte(`capture`), opToken(captureOpType))
lexer.Add([]byte(`test`), opToken(testOpType)) lexer.Add([]byte(`test`), opToken(testOpType))
lexer.Add([]byte(`upcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: true}))
lexer.Add([]byte(`ascii_upcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: true}))
lexer.Add([]byte(`downcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: false}))
lexer.Add([]byte(`ascii_downcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: false}))
lexer.Add([]byte(`sort`), opToken(sortOpType)) lexer.Add([]byte(`sort`), opToken(sortOpType))
lexer.Add([]byte(`sort_by`), opToken(sortByOpType)) lexer.Add([]byte(`sort_by`), opToken(sortByOpType))
lexer.Add([]byte(`reverse`), opToken(reverseOpType)) lexer.Add([]byte(`reverse`), opToken(reverseOpType))

View File

@ -125,13 +125,16 @@ var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50,
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator} var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator} var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator} var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator} var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator} var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}
var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator} var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator}
var matchOpType = &operationType{Type: "MATCH", NumArgs: 1, Precedence: 50, Handler: matchOperator} var matchOpType = &operationType{Type: "MATCH", NumArgs: 1, Precedence: 50, Handler: matchOperator}
var captureOpType = &operationType{Type: "CAPTURE", NumArgs: 1, Precedence: 50, Handler: captureOperator} var captureOpType = &operationType{Type: "CAPTURE", NumArgs: 1, Precedence: 50, Handler: captureOperator}
var testOpType = &operationType{Type: "TEST", NumArgs: 1, Precedence: 50, Handler: testOperator} var testOpType = &operationType{Type: "TEST", NumArgs: 1, Precedence: 50, Handler: testOperator}
var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator} var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator}
var changeCaseOpType = &operationType{Type: "CHANGE_CASE", NumArgs: 0, Precedence: 50, Handler: changeCaseOperator}
var loadOpType = &operationType{Type: "LOAD", NumArgs: 1, Precedence: 50, Handler: loadYamlOperator} var loadOpType = &operationType{Type: "LOAD", NumArgs: 1, Precedence: 50, Handler: loadYamlOperator}

View File

@ -9,6 +9,37 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
type changeCasePrefs struct {
ToUpperCase bool
}
func changeCaseOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
results := list.New()
prefs := expressionNode.Operation.Preferences.(changeCasePrefs)
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
node := unwrapDoc(candidate.Node)
if guessTagFromCustomType(node) != "!!str" {
return Context{}, fmt.Errorf("cannot change case with %v, can only operate on strings. ", node.Tag)
}
newStringNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: node.Tag, Style: node.Style}
if prefs.ToUpperCase {
newStringNode.Value = strings.ToUpper(node.Value)
} else {
newStringNode.Value = strings.ToLower(node.Value)
}
results.PushBack(candidate.CreateReplacement(newStringNode))
}
return context.ChildContext(results), nil
}
func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, context Context) (string, string, error) { func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, context Context) (string, string, error) {
regEx := "" regEx := ""
replacementText := "" replacementText := ""

View File

@ -5,6 +5,40 @@ import (
) )
var stringsOperatorScenarios = []expressionScenario{ var stringsOperatorScenarios = []expressionScenario{
{
description: "To up (upper) case",
subdescription: "Works with unicode characters",
document: `água`,
expression: "upcase",
expected: []string{
"D0, P[], (!!str)::ÁGUA\n",
},
},
{
skipDoc: true,
document: `!camel água`,
expression: "upcase",
expected: []string{
"D0, P[], (!camel)::ÁGUA\n",
},
},
{
description: "To down (lower) case",
subdescription: "Works with unicode characters",
document: `ÁgUA`,
expression: "downcase",
expected: []string{
"D0, P[], (!!str)::água\n",
},
},
{
skipDoc: true,
document: `!camel ÁgUA`,
expression: "downcase",
expected: []string{
"D0, P[], (!camel)::água\n",
},
},
{ {
description: "Join strings", description: "Join strings",
document: `[cat, meow, 1, null, true]`, document: `[cat, meow, 1, null, true]`,