Can specify indent in encode ops

This commit is contained in:
Mike Farah 2021-10-24 11:35:40 +11:00
parent 587af7f722
commit 91717b3c5d
5 changed files with 115 additions and 20 deletions

5
go.mod
View File

@ -13,13 +13,8 @@ require (
)
require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/timtadh/data-structures v0.5.3 // indirect
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
go 1.17

View File

@ -2,10 +2,13 @@ Encode operators will take the piped in object structure and encode it as a stri
These operators are useful to process yaml documents that have stringified embeded yaml/json/props in them.
## Encode value as yaml string
Indent defaults to 2
Given a sample.yml file of:
```yaml
a:
cool: thing
cool:
bob: dylan
```
then
```bash
@ -14,9 +17,34 @@ yq eval '.b = (.a | to_yaml)' sample.yml
will output
```yaml
a:
cool: thing
cool:
bob: dylan
b: |
cool: thing
cool:
bob: dylan
```
## Encode value as yaml string, with custom indentation
You can specify the indentation level as the first parameter.
Given a sample.yml file of:
```yaml
a:
cool:
bob: dylan
```
then
```bash
yq eval '.b = (.a | to_yaml(8))' sample.yml
```
will output
```yaml
a:
cool:
bob: dylan
b: |
cool:
bob: dylan
```
## Encode value as yaml string, using toyaml
@ -59,6 +87,25 @@ b: |
}
```
## Encode value as json string, on one line
Pass in a 0 indent to print json on a single line.
Given a sample.yml file of:
```yaml
a:
cool: thing
```
then
```bash
yq eval '.b = (.a | to_json(0))' sample.yml
```
will output
```yaml
a:
cool: thing
b: '{"cool":"thing"}'
```
## Encode value as props string
Given a sample.yml file of:
```yaml

View File

@ -2,6 +2,7 @@ package yqlib
import (
"fmt"
"regexp"
"strconv"
"strings"
@ -130,6 +131,24 @@ func opTokenWithPrefs(op *operationType, assignOpType *operationType, preference
}
}
func encodeWithIndent(outputFormat PrinterOutputFormat) lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
parameterParser := regexp.MustCompile(`.*\(([0-9]+)\)`)
value := string(m.Bytes)
matches := parameterParser.FindStringSubmatch(value)
var indent, errParsingInt = strconv.ParseInt(matches[1], 10, 32) // nolint
if errParsingInt != nil {
return nil, errParsingInt
}
prefs := encoderPreferences{format: outputFormat, indent: int(indent)}
op := &Operation{OperationType: encodeOpType, Value: encodeOpType.Type, StringValue: value + "i " + matches[1], Preferences: prefs}
return &token{TokenType: operationToken, Operation: op}, nil
}
}
func assignAllCommentsOp(updateAssign bool) lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
log.Debug("assignAllCommentsOp %v", string(m.Bytes))
@ -273,13 +292,19 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
lexer.Add([]byte(`length`), opToken(lengthOpType))
lexer.Add([]byte(`toyaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat}))
lexer.Add([]byte(`tojson`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat}))
lexer.Add([]byte(`toprops`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat}))
lexer.Add([]byte(`toyaml\([0-9]+\)`), encodeWithIndent(YamlOutputFormat))
lexer.Add([]byte(`to_yaml\([0-9]+\)`), encodeWithIndent(YamlOutputFormat))
lexer.Add([]byte(`to_yaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat}))
lexer.Add([]byte(`to_json`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat}))
lexer.Add([]byte(`to_props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat}))
lexer.Add([]byte(`tojson\([0-9]+\)`), encodeWithIndent(JsonOutputFormat))
lexer.Add([]byte(`to_json\([0-9]+\)`), encodeWithIndent(JsonOutputFormat))
lexer.Add([]byte(`toyaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2}))
lexer.Add([]byte(`tojson`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat, indent: 2}))
lexer.Add([]byte(`toprops`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2}))
lexer.Add([]byte(`to_yaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2}))
lexer.Add([]byte(`to_json`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat, indent: 2}))
lexer.Add([]byte(`to_props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2}))
lexer.Add([]byte(`fromyaml`), opToken(decodeOpType))
lexer.Add([]byte(`fromjson`), opToken(decodeOpType))

View File

@ -12,13 +12,15 @@ import (
func yamlToString(candidate *CandidateNode, prefs encoderPreferences) (string, error) {
var output bytes.Buffer
printer := NewPrinter(bufio.NewWriter(&output), prefs.format, true, false, 2, true)
log.Debug("printing with indent: %v", prefs.indent)
printer := NewPrinter(bufio.NewWriter(&output), prefs.format, true, false, prefs.indent, true)
err := printer.PrintResults(candidate.AsList())
return output.String(), err
}
type encoderPreferences struct {
format PrinterOutputFormat
indent int
}
/* encodes object as yaml string */
@ -52,6 +54,11 @@ func encodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
}
}
// dont print a new line when printing json on a single line.
if preferences.format == JsonOutputFormat && preferences.indent == 0 {
stringValue = chomper.ReplaceAllString(stringValue, "")
}
stringContentNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: stringValue}
results.PushBack(candidate.CreateChild(nil, stringContentNode))
}

View File

@ -4,14 +4,25 @@ import (
"testing"
)
var prefix = "D0, P[], (doc)::a:\n cool:\n bob: dylan\n"
var encoderDecoderOperatorScenarios = []expressionScenario{
{
description: "Encode value as yaml string",
document: `{a: {cool: "thing"}}`,
expression: `.b = (.a | to_yaml)`,
description: "Encode value as yaml string",
subdescription: "Indent defaults to 2",
document: "a:\n cool:\n bob: dylan",
expression: `.b = (.a | to_yaml)`,
expected: []string{
`D0, P[], (doc)::{a: {cool: "thing"}, b: "{cool: \"thing\"}\n"}
`,
prefix + "b: |\n cool:\n bob: dylan\n",
},
},
{
description: "Encode value as yaml string, with custom indentation",
subdescription: "You can specify the indentation level as the first parameter.",
document: "a:\n cool:\n bob: dylan",
expression: `.b = (.a | to_yaml(8))`,
expected: []string{
prefix + "b: |\n cool:\n bob: dylan\n",
},
},
{
@ -30,6 +41,16 @@ var encoderDecoderOperatorScenarios = []expressionScenario{
expression: `.b = (.a | to_json)`,
expected: []string{
`D0, P[], (doc)::{a: {cool: "thing"}, b: "{\n \"cool\": \"thing\"\n}\n"}
`,
},
},
{
description: "Encode value as json string, on one line",
subdescription: "Pass in a 0 indent to print json on a single line.",
document: `{a: {cool: "thing"}}`,
expression: `.b = (.a | to_json(0))`,
expected: []string{
`D0, P[], (doc)::{a: {cool: "thing"}, b: '{"cool":"thing"}'}
`,
},
},