mirror of
https://github.com/wader/fq.git
synced 2024-12-25 06:12:30 +03:00
518f6af4a8
Upstream changes: Many performance improvements Error message improvments Lots of refactoring Most of the JQValue interface changes in gojq fork had to be reworked but resultet in a much nicer and cleaner changeset. fq changes: Assignment to JQValue (like a decode value) now shallowly converts the value into a jq value before assigning. Was a bit hacky as it was and this makes JQValue behave more like real jq values. This also fixes some advanced indexing issues. Actual custom path updates will be something for the future.
346 lines
11 KiB
Go
346 lines
11 KiB
Go
//nolint:gosimple
|
|
package gojqextra
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/wader/fq/internal/colorjson"
|
|
|
|
"github.com/wader/gojq"
|
|
)
|
|
|
|
func Typeof(v interface{}) string {
|
|
switch v := v.(type) {
|
|
case nil:
|
|
return "null"
|
|
case bool:
|
|
return "boolean"
|
|
case int, float64, *big.Int:
|
|
return "number"
|
|
case string:
|
|
return "string"
|
|
case []interface{}:
|
|
return "array"
|
|
case map[string]interface{}:
|
|
return "object"
|
|
case gojq.JQValue:
|
|
return v.JQValueType()
|
|
default:
|
|
panic(fmt.Sprintf("invalid value: %v", v))
|
|
}
|
|
}
|
|
|
|
// TODO: preview errors
|
|
|
|
// array
|
|
|
|
var _ gojq.JQValue = Array{}
|
|
|
|
type Array []interface{}
|
|
|
|
func (v Array) JQValueLength() interface{} { return len(v) }
|
|
func (v Array) JQValueSliceLen() interface{} { return len(v) }
|
|
func (v Array) JQValueIndex(index int) interface{} {
|
|
if index < 0 {
|
|
return nil
|
|
}
|
|
return v[index]
|
|
}
|
|
func (v Array) JQValueSlice(start int, end int) interface{} { return v[start:end] }
|
|
func (v Array) JQValueKey(name string) interface{} {
|
|
return ExpectedObjectError{Typ: "array"}
|
|
}
|
|
func (v Array) JQValueEach() interface{} {
|
|
vs := make([]gojq.PathValue, len(v))
|
|
for i, v := range v {
|
|
vs[i] = gojq.PathValue{Path: i, Value: v}
|
|
}
|
|
return vs
|
|
}
|
|
func (v Array) JQValueKeys() interface{} {
|
|
vs := make([]interface{}, len(v))
|
|
for i := range v {
|
|
vs[i] = i
|
|
}
|
|
return vs
|
|
}
|
|
func (v Array) JQValueHas(key interface{}) interface{} {
|
|
intKey, ok := key.(int)
|
|
if !ok {
|
|
return HasKeyTypeError{L: "array", R: fmt.Sprintf("%v", key)}
|
|
}
|
|
return intKey >= 0 && intKey < len(v)
|
|
}
|
|
func (v Array) JQValueType() string { return "array" }
|
|
func (v Array) JQValueToNumber() interface{} {
|
|
return FuncTypeNameError{Name: "tonumber", Typ: "array"}
|
|
}
|
|
func (v Array) JQValueToString() interface{} {
|
|
return FuncTypeNameError{Name: "tostring", Typ: "array"}
|
|
}
|
|
func (v Array) JQValueToGoJQ() interface{} { return []interface{}(v) }
|
|
|
|
// object
|
|
|
|
var _ gojq.JQValue = Object{}
|
|
|
|
type Object map[string]interface{}
|
|
|
|
func (v Object) JQValueLength() interface{} { return len(v) }
|
|
func (v Object) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: "object"} }
|
|
func (v Object) JQValueIndex(index int) interface{} { return ExpectedArrayError{Typ: "object"} }
|
|
func (v Object) JQValueSlice(start int, end int) interface{} {
|
|
return ExpectedArrayError{Typ: "object"}
|
|
}
|
|
func (v Object) JQValueKey(name string) interface{} { return v[name] }
|
|
func (v Object) JQValueEach() interface{} {
|
|
vs := make([]gojq.PathValue, len(v))
|
|
i := 0
|
|
for k, v := range v {
|
|
vs[i] = gojq.PathValue{Path: k, Value: v}
|
|
i++
|
|
}
|
|
return vs
|
|
}
|
|
func (v Object) JQValueKeys() interface{} {
|
|
vs := make([]interface{}, len(v))
|
|
i := 0
|
|
for k := range v {
|
|
vs[i] = k
|
|
i++
|
|
}
|
|
return vs
|
|
}
|
|
func (v Object) JQValueHas(key interface{}) interface{} {
|
|
stringKey, ok := key.(string)
|
|
if !ok {
|
|
return HasKeyTypeError{L: "object", R: fmt.Sprintf("%v", key)}
|
|
}
|
|
_, ok = v[stringKey]
|
|
return ok
|
|
}
|
|
func (v Object) JQValueType() string { return "object" }
|
|
func (v Object) JQValueToNumber() interface{} {
|
|
return FuncTypeNameError{Name: "tonumber", Typ: "object"}
|
|
}
|
|
func (v Object) JQValueToString() interface{} {
|
|
return FuncTypeNameError{Name: "tostring", Typ: "object"}
|
|
}
|
|
func (v Object) JQValueToGoJQ() interface{} { return map[string]interface{}(v) }
|
|
|
|
// number
|
|
|
|
var _ gojq.JQValue = Number{}
|
|
|
|
type Number struct {
|
|
V interface{}
|
|
}
|
|
|
|
func (v Number) JQValueLength() interface{} { return v.V }
|
|
func (v Number) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: "number"} }
|
|
func (v Number) JQValueIndex(index int) interface{} { return ExpectedArrayError{Typ: "number"} }
|
|
func (v Number) JQValueSlice(start int, end int) interface{} {
|
|
return ExpectedArrayError{Typ: "number"}
|
|
}
|
|
func (v Number) JQValueKey(name string) interface{} { return ExpectedObjectError{Typ: "number"} }
|
|
func (v Number) JQValueEach() interface{} { return IteratorError{Typ: "number"} }
|
|
func (v Number) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "number"} }
|
|
func (v Number) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeNameError{Name: "has", Typ: "number"}
|
|
}
|
|
func (v Number) JQValueType() string { return "number" }
|
|
func (v Number) JQValueToNumber() interface{} { return v.V }
|
|
func (v Number) JQValueToString() interface{} {
|
|
b := &bytes.Buffer{}
|
|
// uses colorjson encode based on gojq encoder to support big.Int
|
|
if err := colorjson.NewEncoder(false, false, 0, nil, colorjson.Colors{}).Marshal(v.V, b); err != nil {
|
|
return err
|
|
}
|
|
return b.String()
|
|
}
|
|
func (v Number) JQValueToGoJQ() interface{} { return v.V }
|
|
|
|
// string
|
|
|
|
var _ gojq.JQValue = String("")
|
|
|
|
type String []rune
|
|
|
|
func (v String) JQValueLength() interface{} { return len(v) }
|
|
func (v String) JQValueSliceLen() interface{} { return len(v) }
|
|
func (v String) JQValueIndex(index int) interface{} {
|
|
// -1 outside after string, -2 outside before string
|
|
if index < 0 {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%c", v[index])
|
|
}
|
|
func (v String) JQValueSlice(start int, end int) interface{} { return string(v[start:end]) }
|
|
func (v String) JQValueKey(name string) interface{} { return ExpectedObjectError{Typ: "string"} }
|
|
func (v String) JQValueEach() interface{} { return IteratorError{Typ: "string"} }
|
|
func (v String) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "string"} }
|
|
func (v String) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeNameError{Name: "has", Typ: "string"}
|
|
}
|
|
func (v String) JQValueType() string { return "string" }
|
|
func (v String) JQValueToNumber() interface{} { return gojq.NormalizeNumbers(string(v)) }
|
|
func (v String) JQValueToString() interface{} { return string(v) }
|
|
func (v String) JQValueToGoJQ() interface{} { return string(v) }
|
|
|
|
// boolean
|
|
|
|
var _ gojq.JQValue = Boolean(true)
|
|
|
|
type Boolean bool
|
|
|
|
func (v Boolean) JQValueLength() interface{} {
|
|
return FuncTypeNameError{Name: "length", Typ: "boolean"}
|
|
}
|
|
func (v Boolean) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: "boolean"} }
|
|
func (v Boolean) JQValueIndex(index int) interface{} { return ExpectedArrayError{Typ: "boolean"} }
|
|
func (v Boolean) JQValueSlice(start int, end int) interface{} {
|
|
return ExpectedArrayError{Typ: "boolean"}
|
|
}
|
|
func (v Boolean) JQValueKey(name string) interface{} { return ExpectedObjectError{Typ: "boolean"} }
|
|
func (v Boolean) JQValueEach() interface{} { return IteratorError{Typ: "boolean"} }
|
|
func (v Boolean) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "boolean"} }
|
|
func (v Boolean) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeNameError{Name: "has", Typ: "boolean"}
|
|
}
|
|
func (v Boolean) JQValueType() string { return "boolean" }
|
|
func (v Boolean) JQValueToNumber() interface{} {
|
|
return FuncTypeNameError{Name: "tonumber", Typ: "boolean"}
|
|
}
|
|
func (v Boolean) JQValueToString() interface{} {
|
|
if v {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
}
|
|
func (v Boolean) JQValueToGoJQ() interface{} { return bool(v) }
|
|
|
|
// null
|
|
|
|
var _ gojq.JQValue = Null{}
|
|
|
|
type Null struct{}
|
|
|
|
func (v Null) JQValueLength() interface{} { return 0 }
|
|
func (v Null) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: "null"} }
|
|
func (v Null) JQValueIndex(index int) interface{} { return ExpectedArrayError{Typ: "null"} }
|
|
func (v Null) JQValueSlice(start int, end int) interface{} { return ExpectedArrayError{Typ: "null"} }
|
|
func (v Null) JQValueKey(name string) interface{} { return ExpectedObjectError{Typ: "null"} }
|
|
|
|
func (v Null) JQValueEach() interface{} { return IteratorError{Typ: "null"} }
|
|
func (v Null) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "null"} }
|
|
func (v Null) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeNameError{Name: "has", Typ: "null"}
|
|
}
|
|
func (v Null) JQValueType() string { return "null" }
|
|
func (v Null) JQValueToNumber() interface{} { return FuncTypeNameError{Name: "tonumber", Typ: "null"} }
|
|
func (v Null) JQValueToString() interface{} { return "null" }
|
|
func (v Null) JQValueToGoJQ() interface{} { return nil }
|
|
|
|
// Base
|
|
|
|
var _ gojq.JQValue = Base{}
|
|
|
|
type Base struct {
|
|
Typ string
|
|
}
|
|
|
|
func (v Base) JQValueLength() interface{} { return ExpectedArrayError{Typ: v.Typ} }
|
|
func (v Base) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: v.Typ} }
|
|
func (v Base) JQValueIndex(index int) interface{} {
|
|
return ExpectedArrayWithIndexError{Typ: v.Typ, Index: index}
|
|
}
|
|
func (v Base) JQValueSlice(start int, end int) interface{} { return ExpectedArrayError{Typ: v.Typ} }
|
|
func (v Base) JQValueKey(name string) interface{} {
|
|
return ExpectedObjectWithKeyError{Typ: v.Typ, Key: name}
|
|
}
|
|
func (v Base) JQValueEach() interface{} { return IteratorError{Typ: v.Typ} }
|
|
func (v Base) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: v.Typ} }
|
|
func (v Base) JQValueHas(key interface{}) interface{} {
|
|
return HasKeyTypeError{L: "array", R: fmt.Sprintf("%v", key)}
|
|
}
|
|
func (v Base) JQValueType() string { return v.Typ }
|
|
func (v Base) JQValueToNumber() interface{} { return FuncTypeNameError{Name: "tonumber", Typ: v.Typ} }
|
|
func (v Base) JQValueToString() interface{} { return FuncTypeNameError{Name: "tostring", Typ: v.Typ} }
|
|
func (v Base) JQValueToGoJQ() interface{} { return nil }
|
|
|
|
// lazy
|
|
|
|
var _ gojq.JQValue = &Lazy{}
|
|
|
|
type Lazy struct {
|
|
Type string
|
|
IsScalar bool
|
|
Fn func() (gojq.JQValue, error)
|
|
|
|
called bool
|
|
err error
|
|
jv gojq.JQValue
|
|
}
|
|
|
|
func (v *Lazy) v() (gojq.JQValue, error) {
|
|
if !v.called {
|
|
v.jv, v.err = v.Fn()
|
|
v.called = true
|
|
}
|
|
return v.jv, v.err
|
|
}
|
|
|
|
func (v *Lazy) f(fn func(jv gojq.JQValue) interface{}) interface{} {
|
|
jv, err := v.v()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return fn(jv)
|
|
}
|
|
|
|
func (v *Lazy) JQValueLength() interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueLength() })
|
|
}
|
|
func (v *Lazy) JQValueSliceLen() interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueSliceLen() })
|
|
}
|
|
func (v *Lazy) JQValueIndex(index int) interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueIndex(index) })
|
|
}
|
|
func (v *Lazy) JQValueSlice(start int, end int) interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueSlice(start, end) })
|
|
}
|
|
func (v *Lazy) JQValueKey(name string) interface{} {
|
|
if v.IsScalar {
|
|
return ExpectedObjectWithKeyError{Typ: v.Type, Key: name}
|
|
}
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueKey(name) })
|
|
}
|
|
func (v *Lazy) JQValueEach() interface{} {
|
|
if v.IsScalar {
|
|
return IteratorError{Typ: v.Type}
|
|
}
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueEach() })
|
|
}
|
|
func (v *Lazy) JQValueKeys() interface{} {
|
|
if v.IsScalar {
|
|
return FuncTypeNameError{Name: "keys", Typ: "string"}
|
|
}
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueKeys() })
|
|
}
|
|
func (v *Lazy) JQValueHas(key interface{}) interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueHas(key) })
|
|
}
|
|
func (v *Lazy) JQValueType() string { return v.Type }
|
|
func (v *Lazy) JQValueToNumber() interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueToNumber() })
|
|
}
|
|
func (v *Lazy) JQValueToString() interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueToString() })
|
|
}
|
|
func (v *Lazy) JQValueToGoJQ() interface{} {
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueToGoJQ() })
|
|
}
|