Skip to content

Commit

Permalink
Merge pull request #1033 from wader/toml-yaml-indent
Browse files Browse the repository at this point in the history
yaml,toml: Add indent option for to_{toml,yaml}
  • Loading branch information
wader authored Nov 21, 2024
2 parents 6ac5713 + ed872d4 commit 95819c9
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 30 deletions.
48 changes: 26 additions & 22 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -682,18 +682,20 @@ name: Afghanistan
zip> ^D
```

- `from_xml`/`from_xml($opts)` Parse XML into jq value.<br>
`{seq: true}` preserve element ordering if more than one sibling.<br>
`{array: true}` use nested `[name, attributes, children]` arrays to represent elements. Attributes will be `null` if none and children will be `[]` if none, this is to make it easier to work with as the array as 3 values. `to_xml` does not require this.<br>
- `from_xml`/`from_xml($opts)` Parse XML into jq value. `$opts` are:
- `{seq: true}` preserve element ordering if more than one sibling.<br>
- `{array: true}` use nested `[name, attributes, children]` arrays to represent elements. Attributes will be `null` if none and children will be `[]` if none, this is to make it easier to work with as the array as 3 values. `to_xml` does not require this.<br>
- `from_html`/`from_html($opts)` Parse HTML into jq value.<br>
Similar to `from_xml` but parses html5 in non-script mode. Will always have a `html` root with `head` and `body` elements.<br>
`{array: true}` use nested arrays to represent elements.<br>
`{seq: true}` preserve element ordering if more than one sibling.<br>
`$opts` are:
- `{array: true}` use nested arrays to represent elements.<br>
- `{seq: true}` preserve element ordering if more than one sibling.<br>
- `to_xml`/`to_xml($opts})` Serialize jq value into XML.<br>
`{indent: number}` indent child elements.<br>
Assumes object representation if input is an object, and nested arrays if input is an array.<br>
Will automatically add a root `doc` element if jq value has more then one root element.<br>
If a `#seq` is found on at least one element all siblings will be sort by sequence number. Attributes are always sorted.<br>
`$opts` are:
- `{indent: number}` indent child elements.<br>

XML elements can be represented as jq value in two ways, as objects (inspired by [mxj](https://github.com/clbanning/mxj) and [xml.com's Converting Between XML and JSON
](https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html)) or nested arrays. Both representations are lossy and might lose ordering of elements, text nodes and comments. In object representation `from_xml`, `from_html` and `to_xml` support `{seq:true}` option to parse/serialize `{"#seq"=<number>}` attributes to preserve element sibling ordering.
Expand Down Expand Up @@ -801,32 +803,34 @@ zip> ^D

JSON and jq-flavoured JSON
- `fromjson` Parse JSON into jq value.
- `tojson`/`tojson($opt)` Serialize jq value into JSON.<br>
`{indent: number}` indent array/object values.<br>
- `tojson`/`tojson($opts)` Serialize jq value into JSON. `$opts` are:
- `{indent: number}` Indent depth.
- `from_jq` Parse jq-flavoured JSON into jq value.
- `to_jq`/`to_jq($opt)` Serialize jq value into jq-flavoured JSON<br>
`{indent: number}` indent array/object values.<br>
jq-flavoured JSON has optional key quotes, `#` comments and can have trailing comma in arrays.
- `to_jq`/`to_jq($opts)` Serialize jq value into jq-flavoured JSON. jq-flavoured JSON has optional key quotes, `#` comments and can have trailing comma in arrays. `$opts` are:
- `{indent: number}` Indent depth.
- `from_jsonl` Parse JSON lines into jq array.
- `to_jsonl` Serialize jq array into JSONL.

Note that `fromjson` and `tojson` use different naming conventions as they originate from jq's standard library.

YAML
- `from_yaml` Parse YAML into jq value.
- `to_yaml` Serialize jq value into YAML.
- `to_yaml`/`to_yaml($opts)` Serialize jq value into YAML. `$opts` are:
- `{indent: number}` Indent depth.

TOML
- `from_toml` Parse TOML into jq value.
- `to_toml` Serialize jq value into TOML.
- `to_toml`/`to_toml($opts)` Serialize jq value into TOML. `$opts` are:
- `{indent: number}` Indent depth.

CSV
- `from_csv`/`from_cvs($opts)` Parse CSV into jq value.<br>
`{comma: string}` field separator, default ",".<br>
`{comment: string}` comment line character, default "#".<br>
To work with tab separated values you can use `fromcvs({comma: "\t"})` or `fq -d csv -o 'comma="\t"'`
- `to_csv`/`to_csv($opts)` Serialize jq value into CSV.<br>
`{comma: string}` field separator, default ",".<br>
To work with tab separated values you can use `fromcvs({comma: "\t"})` or `fq -d csv -o 'comma="\t"'`<br>
`$opts` are:
- `{comma: string}` field separator, default ",".<br>
- `{comment: string}` comment line character, default "#".<br>
- `to_csv`/`to_csv($opts)` Serialize jq value into CSV. `$opts` are:
- `{comma: string}` field separator, default ",".<br>

XML encoding
- `from_xmlentities` Decode XML entities.
Expand Down Expand Up @@ -862,10 +866,10 @@ URL parts and XML encodings
Binary encodings like hex and base64
- `from_hex` Decode hex string to binary.
- `to_hex` Encode binary into hex string.
- `from_base64`/`from_base64($opts)` Decode base64 encodings into binary.<br>
`{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl`
- `to_base64`/`to_base64($opts)` Encode binary into base64 encodings.<br>
`{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl`
- `from_base64`/`from_base64($opts)` Decode base64 encodings into binary. `$opts` are:
- `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl`
- `to_base64`/`to_base64($opts)` Encode binary into base64 encodings. `$opts` are:
- `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl`

Hash functions
- `to_md4` Hash binary using md4.
Expand Down
29 changes: 29 additions & 0 deletions format/toml/testdata/indent.fqtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
$ fq -rn "{a: {b: 123}} | to_toml"
[a]
b = 123

$ fq -rn "{a: {b: 123}} | to_toml({indent: range(8)})"
[a]
b = 123

[a]
b = 123

[a]
b = 123

[a]
b = 123

[a]
b = 123

[a]
b = 123

[a]
b = 123

[a]
b = 123

13 changes: 10 additions & 3 deletions format/toml/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"embed"
"fmt"
"io"
"strings"
"unicode/utf8"

"github.com/BurntSushi/toml"
Expand All @@ -31,7 +32,7 @@ func init() {
Functions: []string{"_todisplay"},
})
interp.RegisterFS(tomlFS)
interp.RegisterFunc0("to_toml", toTOML)
interp.RegisterFunc1("_to_toml", toTOML)
}

func decodeTOMLSeekFirstValidRune(br io.ReadSeeker) error {
Expand Down Expand Up @@ -88,13 +89,19 @@ func decodeTOML(d *decode.D) any {
return nil
}

func toTOML(_ *interp.Interp, c any) any {
type ToTOMLOpts struct {
Indent int `default:"2"` // 2 is default for BurntSushi/toml
}

func toTOML(_ *interp.Interp, c any, opts ToTOMLOpts) any {
if c == nil {
return gojqx.FuncTypeError{Name: "to_toml", V: c}
}

b := &bytes.Buffer{}
if err := toml.NewEncoder(b).Encode(gojqx.Normalize(c)); err != nil {
e := toml.NewEncoder(b)
e.Indent = strings.Repeat(" ", opts.Indent)
if err := e.Encode(gojqx.Normalize(c)); err != nil {
return err
}
return b.String()
Expand Down
2 changes: 2 additions & 0 deletions format/toml/toml.jq
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
def _toml__todisplay: tovalue;
def to_toml($opts): _to_toml($opts);
def to_toml: _to_toml(null);
29 changes: 29 additions & 0 deletions format/yaml/testdata/indent.fqtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
$ fq -rn "{a: {b: 123}} | to_yaml"
a:
b: 123

$ fq -rn "{a: {b: 123}} | to_yaml({indent: range(8)})"
a:
b: 123

a:
b: 123

a:
b: 123

a:
b: 123

a:
b: 123

a:
b: 123

a:
b: 123

a:
b: 123

21 changes: 16 additions & 5 deletions format/yaml/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package yaml
// TODO: yaml type eval? walk eval?

import (
"bytes"
"embed"
"errors"
"io"
Expand Down Expand Up @@ -30,7 +31,7 @@ func init() {
Functions: []string{"_todisplay"},
})
interp.RegisterFS(yamlFS)
interp.RegisterFunc0("to_yaml", toYAML)
interp.RegisterFunc1("_to_yaml", toYAML)
}

func decodeYAML(d *decode.D) any {
Expand Down Expand Up @@ -61,10 +62,20 @@ func decodeYAML(d *decode.D) any {
return nil
}

func toYAML(_ *interp.Interp, c any) any {
b, err := yaml.Marshal(gojqx.Normalize(c))
if err != nil {
type ToYAMLOpts struct {
Indent int `default:"4"` // 4 is default for gopkg.in/yaml.v3
}

func toYAML(_ *interp.Interp, c any, opts ToYAMLOpts) any {
b := &bytes.Buffer{}
e := yaml.NewEncoder(b)
// yaml.SetIndent panics if < 0
if opts.Indent >= 0 {
e.SetIndent(opts.Indent)
}
if err := e.Encode(gojqx.Normalize(c)); err != nil {
return err
}
return string(b)

return b.String()
}
2 changes: 2 additions & 0 deletions format/yaml/yaml.jq
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
def _yaml__todisplay: tovalue;
def to_yaml($opts): _to_yaml($opts);
def to_yaml: _to_yaml(null);

0 comments on commit 95819c9

Please sign in to comment.