mirror of
https://github.com/wader/fq.git
synced 2024-11-29 04:10:01 +03:00
e3ae1440c9
Feels less cluttered, easier to read and more consistent. Still keep tovalue, tobytes etc that are more basic functions this only renamed format related functions. Also there is an exceptin for to/fromjson as it comes from jq. Also fixes lots of spelling errors while reading thru.
120 lines
2.2 KiB
Go
120 lines
2.2 KiB
Go
package json
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
stdjson "encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/internal/colorjson"
|
|
"github.com/wader/fq/pkg/bitio"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
"github.com/wader/gojq"
|
|
)
|
|
|
|
//go:embed json.jq
|
|
var jsonFS embed.FS
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.JSON,
|
|
Description: "JavaScript Object Notation",
|
|
ProbeOrder: format.ProbeOrderTextJSON,
|
|
Groups: []string{format.PROBE},
|
|
DecodeFn: decodeJSON,
|
|
Functions: []string{"_todisplay"},
|
|
})
|
|
interp.RegisterFS(jsonFS)
|
|
interp.RegisterFunc1("_to_json", toJSON)
|
|
}
|
|
|
|
func decodeJSONEx(d *decode.D, lines bool) any {
|
|
var vs []any
|
|
|
|
// keep in sync with gojq fromJSON
|
|
jd := stdjson.NewDecoder(bitio.NewIOReader(d.RawLen(d.Len())))
|
|
jd.UseNumber()
|
|
|
|
foundEOF := false
|
|
|
|
for {
|
|
var v any
|
|
if err := jd.Decode(&v); err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
foundEOF = true
|
|
if lines {
|
|
break
|
|
} else if len(vs) == 1 {
|
|
break
|
|
}
|
|
} else if lines {
|
|
d.Fatalf(err.Error())
|
|
}
|
|
break
|
|
}
|
|
|
|
vs = append(vs, v)
|
|
}
|
|
|
|
if !lines && (len(vs) != 1 || !foundEOF) {
|
|
d.Fatalf("trialing data after top-level value")
|
|
}
|
|
|
|
var s scalar.Any
|
|
if lines {
|
|
if len(vs) == 0 {
|
|
d.Fatalf("not lines found")
|
|
}
|
|
s.Actual = gojq.NormalizeNumbers(vs)
|
|
} else {
|
|
s.Actual = gojq.NormalizeNumbers(vs[0])
|
|
}
|
|
d.Value.V = &s
|
|
d.Value.Range.Len = d.Len()
|
|
|
|
return nil
|
|
}
|
|
|
|
func decodeJSON(d *decode.D, _ any) any {
|
|
return decodeJSONEx(d, false)
|
|
}
|
|
|
|
type ToJSONOpts struct {
|
|
Indent int
|
|
}
|
|
|
|
// TODO: share with interp code
|
|
func makeEncoder(opts ToJSONOpts) *colorjson.Encoder {
|
|
return colorjson.NewEncoder(
|
|
false,
|
|
false,
|
|
opts.Indent,
|
|
func(v any) any {
|
|
switch v := v.(type) {
|
|
case gojq.JQValue:
|
|
return v.JQValueToGoJQ()
|
|
case nil, bool, float64, int, string, *big.Int, map[string]any, []any:
|
|
return v
|
|
default:
|
|
panic(fmt.Sprintf("toValue not a JQValue value: %#v %T", v, v))
|
|
}
|
|
},
|
|
colorjson.Colors{},
|
|
)
|
|
}
|
|
|
|
func toJSON(_ *interp.Interp, c any, opts ToJSONOpts) any {
|
|
cj := makeEncoder(opts)
|
|
bb := &bytes.Buffer{}
|
|
if err := cj.Marshal(c, bb); err != nil {
|
|
return err
|
|
}
|
|
return bb.String()
|
|
}
|