Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interp: Add skip_gaps option for tovalue/-V #649

Merged
merged 1 commit into from
Apr 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -796,16 +796,36 @@ fq has some general options in addition to decode and decoders specific options.

`<value>` is fuzzily parsed based on the type of the option. Ex: a string can be specified as `-o name=string` or `-o name="string"`.

### `bits_format`
### `-o bits_format=<string>`

How to represent raw bits as JSON.
How to represent raw binary as JSON.

- `-o bits_foramt=string` String with raw bytes (zero bit padded). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turn to JSON.
- `-o bits_foramt=string` String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turn to JSON.
- `-o bits_format=md5` MD5 hex string (zero bit padded).
- `-o bits_format=base64` Base64 string.
- `-p bits_foramt=truncate` Truncated string.
- `-o bits_format=snippet` Truncated Base64 string prefixed with bit length.

```sh
$ fq -V -o bits_format=base64 . file`
```
In query
```jq
tovalue({bits_format: "md5"})
```

### `-o skip_gaps=<boolean>`

Skip gaps fields (`gap0` etc) when using `tovalue` or `-V`. Note that this might affect array indexes if one more more gaps fields are skipped in an array.

```sh
$ fq -V -o skip_gaps=true . file`
```
In query
```jq
tovalue({skip_gaps: true})
```

## Color and unicode output

fq by default tries to use colors if possible, this can be disabled with `-M`. You can also
Expand Down
48 changes: 38 additions & 10 deletions pkg/interp/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ func (i *Interp) _registry(c any) any {
}
}

func (i *Interp) _toValue(c any, opts map[string]any) any {
func (i *Interp) _toValue(c any, om map[string]any) any {
return toValue(
func() Options { return OptionsFromValue(opts) },
func() *Options {
opts := OptionsFromValue(om)
return &opts
},
c,
)
}
Expand Down Expand Up @@ -307,7 +310,7 @@ func valueHas(key any, a func(name string) any, b func(key any) any) any {

// TODO: make more efficient somehow? shallow values but might be hard
// when things like tovalue.key should behave like a jq value and not a decode value etc
func toValue(optsFn func() Options, v any) any {
func toValue(optsFn func() *Options, v any) any {
nv, _ := gojqex.ToGoJQValueFn(v, func(v any) (any, bool) {
switch v := v.(type) {
case JQValueEx:
Expand Down Expand Up @@ -540,7 +543,7 @@ func (dvb decodeValueBase) JQValueKey(name string) any {
case "_gap":
switch vv := dv.V.(type) {
case Scalarable:
return vv.ScalarGap()
return vv.ScalarIsGap()
default:
return false
}
Expand Down Expand Up @@ -606,7 +609,7 @@ func (v decodeValue) JQValueKey(name string) any {
func (v decodeValue) JQValueHas(key any) any {
return valueHas(key, v.decodeValueBase.JQValueKey, v.JQValue.JQValueHas)
}
func (v decodeValue) JQValueToGoJQEx(optsFn func() Options) any {
func (v decodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
if !v.bitsFormat {
return v.JQValueToGoJQ()
}
Expand Down Expand Up @@ -695,13 +698,26 @@ func (v ArrayDecodeValue) JQValueHas(key any) any {
return intKey >= 0 && intKey < len(v.Compound.Children)
})
}
func (v ArrayDecodeValue) JQValueToGoJQ() any {
vs := make([]any, len(v.Compound.Children))
for i, f := range v.Compound.Children {
vs[i] = makeDecodeValue(f, decodeValueValue)
func (v ArrayDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()

vs := make([]any, 0, len(v.Compound.Children))
for _, f := range v.Compound.Children {
switch s := f.V.(type) {
case Scalarable:
if s.ScalarIsGap() && opts.SkipGaps {
// skip, note for arrays this will affect indexes
continue
}
}

vs = append(vs, makeDecodeValue(f, decodeValueValue))
}
return vs
}
func (v ArrayDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
}

// decode value struct

Expand Down Expand Up @@ -767,10 +783,22 @@ func (v StructDecodeValue) JQValueHas(key any) any {
},
)
}
func (v StructDecodeValue) JQValueToGoJQ() any {
func (v StructDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()

vm := make(map[string]any, len(v.Compound.Children))
for _, f := range v.Compound.Children {
switch s := f.V.(type) {
case Scalarable:
if s.ScalarIsGap() && opts.SkipGaps {
continue
}
}

vm[f.Name] = makeDecodeValue(f, decodeValueValue)
}
return vm
}
func (v StructDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
}
7 changes: 4 additions & 3 deletions pkg/interp/interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type Scalarable interface {
ScalarValue() any
ScalarSym() any
ScalarDescription() string
ScalarGap() bool
ScalarIsGap() bool
ScalarDisplayFormat() scalar.DisplayFormat
}

Expand Down Expand Up @@ -210,7 +210,7 @@ type Display interface {

type JQValueEx interface {
gojq.JQValue
JQValueToGoJQEx(optsFn func() Options) any
JQValueToGoJQEx(optsFn func() *Options) any
}

func valuePath(v *decode.Value) []any {
Expand Down Expand Up @@ -682,7 +682,7 @@ func (i *Interp) _printColorJSON(c any, v any) gojq.Iter {
Color: opts.Color,
Tab: false,
Indent: indent,
ValueFn: func(v any) any { return toValue(func() Options { return opts }, v) },
ValueFn: func(v any) any { return toValue(func() *Options { return &opts }, v) },
Colors: colorjson.Colors{
Reset: []byte(ansi.Reset.SetString),
Null: []byte(opts.Decorator.Null.SetString),
Expand Down Expand Up @@ -1034,6 +1034,7 @@ type Options struct {
DisplayBytes int
Addrbase int
Sizebase int
SkipGaps bool

Decorator Decorator
BitsFormatFn func(br bitio.ReaderAtSeeker) (any, error)
Expand Down
4 changes: 3 additions & 1 deletion pkg/interp/options.jq
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def _opt_build_default_fixed:
raw_output: ($stdout.is_terminal | not),
raw_string: false,
repl: false,
skip_gaps: false,
sizebase: 10,
show_formats: false,
show_help: false,
Expand Down Expand Up @@ -101,9 +102,10 @@ def _opt_options:
raw_output: "boolean",
raw_string: "boolean",
repl: "boolean",
sizebase: "number",
show_formats: "boolean",
show_help: "boolean",
sizebase: "number",
skip_gaps: "boolean",
slurp: "boolean",
string_input: "boolean",
unicode: "boolean",
Expand Down
1 change: 1 addition & 0 deletions pkg/interp/testdata/args.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ repl false
show_formats false
show_help options
sizebase 10
skip_gaps false
slurp false
string_input false
unicode false
Expand Down
1 change: 1 addition & 0 deletions pkg/interp/testdata/options.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ $ fq -n options
"show_formats": false,
"show_help": false,
"sizebase": 10,
"skip_gaps": false,
"slurp": false,
"string_input": false,
"unicode": false,
Expand Down
22 changes: 17 additions & 5 deletions pkg/interp/testdata/tovalue.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,21 @@
$ fq -i . test.mp3
mp3> .headers[0] | tovalue.header.magic
"ID3"
mp3> [tobytes, "abc"] | mp3 | tovalue, tovalue({skip_gaps: true}) | keys
[
"footers",
"frames",
"gap0",
"headers"
]
[
"footers",
"frames",
"headers"
]
mp3> "abc" | mpeg_ts | tovalue, tovalue({skip_gaps: true}) | keys
[
"gap0"
]
[]
mp3> ^D
$ fq -i
null> "aaa" | mp3_frame | .gap0 | tovalue, tovalue({sizebase: 2})
"aaa"
"aaa"
null> ^D
16 changes: 8 additions & 8 deletions pkg/scalar/scalar_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (s Any) ScalarValue() any {
}
func (s Any) ScalarSym() any { return s.Sym }
func (s Any) ScalarDescription() string { return s.Description }
func (s Any) ScalarGap() bool { return s.Gap }
func (s Any) ScalarIsGap() bool { return s.Gap }
func (s Any) ScalarDisplayFormat() DisplayFormat { return 0 }

func AnyActual(v any) AnyMapper {
Expand Down Expand Up @@ -224,7 +224,7 @@ func (s BigInt) ScalarValue() any {
}
func (s BigInt) ScalarSym() any { return s.Sym }
func (s BigInt) ScalarDescription() string { return s.Description }
func (s BigInt) ScalarGap() bool { return s.Gap }
func (s BigInt) ScalarIsGap() bool { return s.Gap }
func (s BigInt) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func BigIntActual(v *big.Int) BigIntMapper {
Expand Down Expand Up @@ -420,7 +420,7 @@ func (s BitBuf) ScalarValue() any {
}
func (s BitBuf) ScalarSym() any { return s.Sym }
func (s BitBuf) ScalarDescription() string { return s.Description }
func (s BitBuf) ScalarGap() bool { return s.Gap }
func (s BitBuf) ScalarIsGap() bool { return s.Gap }
func (s BitBuf) ScalarDisplayFormat() DisplayFormat { return 0 }

func BitBufActual(v bitio.ReaderAtSeeker) BitBufMapper {
Expand Down Expand Up @@ -616,7 +616,7 @@ func (s Bool) ScalarValue() any {
}
func (s Bool) ScalarSym() any { return s.Sym }
func (s Bool) ScalarDescription() string { return s.Description }
func (s Bool) ScalarGap() bool { return s.Gap }
func (s Bool) ScalarIsGap() bool { return s.Gap }
func (s Bool) ScalarDisplayFormat() DisplayFormat { return 0 }

func BoolActual(v bool) BoolMapper {
Expand Down Expand Up @@ -812,7 +812,7 @@ func (s Flt) ScalarValue() any {
}
func (s Flt) ScalarSym() any { return s.Sym }
func (s Flt) ScalarDescription() string { return s.Description }
func (s Flt) ScalarGap() bool { return s.Gap }
func (s Flt) ScalarIsGap() bool { return s.Gap }
func (s Flt) ScalarDisplayFormat() DisplayFormat { return 0 }

func FltActual(v float64) FltMapper {
Expand Down Expand Up @@ -1009,7 +1009,7 @@ func (s Sint) ScalarValue() any {
}
func (s Sint) ScalarSym() any { return s.Sym }
func (s Sint) ScalarDescription() string { return s.Description }
func (s Sint) ScalarGap() bool { return s.Gap }
func (s Sint) ScalarIsGap() bool { return s.Gap }
func (s Sint) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func SintActual(v int64) SintMapper {
Expand Down Expand Up @@ -1205,7 +1205,7 @@ func (s Str) ScalarValue() any {
}
func (s Str) ScalarSym() any { return s.Sym }
func (s Str) ScalarDescription() string { return s.Description }
func (s Str) ScalarGap() bool { return s.Gap }
func (s Str) ScalarIsGap() bool { return s.Gap }
func (s Str) ScalarDisplayFormat() DisplayFormat { return 0 }

func StrActual(v string) StrMapper {
Expand Down Expand Up @@ -1402,7 +1402,7 @@ func (s Uint) ScalarValue() any {
}
func (s Uint) ScalarSym() any { return s.Sym }
func (s Uint) ScalarDescription() string { return s.Description }
func (s Uint) ScalarGap() bool { return s.Gap }
func (s Uint) ScalarIsGap() bool { return s.Gap }
func (s Uint) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func UintActual(v uint64) UintMapper {
Expand Down
2 changes: 1 addition & 1 deletion pkg/scalar/scalar_gen.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
}
func (s {{$name}}) ScalarSym() any { return s.Sym }
func (s {{$name}}) ScalarDescription() string { return s.Description }
func (s {{$name}}) ScalarGap() bool { return s.Gap }
func (s {{$name}}) ScalarIsGap() bool { return s.Gap }
{{- if $t.display_format}}
func (s {{$name}}) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }
{{- else }}
Expand Down