mirror of
https://github.com/wader/fq.git
synced 2024-12-25 14:23:18 +03:00
412 lines
13 KiB
Go
412 lines
13 KiB
Go
//nolint:gosimple
|
|
package gojqextra
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/wader/fq/internal/colorjson"
|
|
|
|
"github.com/wader/gojq"
|
|
)
|
|
|
|
// TODO: preview errors
|
|
|
|
func expectedArrayOrObject(key interface{}, typ string) error {
|
|
switch v := key.(type) {
|
|
case string:
|
|
return ExpectedObjectWithKeyError{Typ: typ, Key: v}
|
|
case int:
|
|
return ExpectedArrayWithIndexError{Typ: typ, Index: v}
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
// 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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
// TODO: handle {start:, end: }
|
|
// TODO: maybe should use gojq implementation as it's quite complex
|
|
intKey, ok := key.(int)
|
|
if !ok {
|
|
return HasKeyTypeError{L: "array", R: fmt.Sprintf("%v", key)}
|
|
}
|
|
|
|
if intKey > 0x3ffffff {
|
|
return ArrayIndexTooLargeError{V: intKey}
|
|
}
|
|
|
|
l := len(v)
|
|
if intKey >= l {
|
|
if delpath {
|
|
return v
|
|
}
|
|
l = intKey + 1
|
|
} else if intKey < -l {
|
|
if delpath {
|
|
return v
|
|
}
|
|
// TODO: wrong error?
|
|
return FuncTypeNameError{Name: "setpath", Typ: "number"}
|
|
} else if intKey < 0 {
|
|
intKey += len(v)
|
|
}
|
|
|
|
var uu []interface{}
|
|
if delpath {
|
|
uu = append(v[0:intKey], v[intKey+1:]...)
|
|
} else {
|
|
uu = make([]interface{}, l)
|
|
copy(uu, v)
|
|
uu[intKey] = u
|
|
}
|
|
|
|
return uu
|
|
}
|
|
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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
stringKey, ok := key.(string)
|
|
if !ok {
|
|
return HasKeyTypeError{L: "object", R: fmt.Sprintf("%v", key)}
|
|
}
|
|
|
|
uu := make(map[string]interface{}, len(v))
|
|
for kv, vv := range v {
|
|
uu[kv] = vv
|
|
}
|
|
if delpath {
|
|
delete(uu, stringKey)
|
|
} else {
|
|
uu[stringKey] = u
|
|
}
|
|
|
|
return uu
|
|
}
|
|
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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
return expectedArrayOrObject(key, "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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
return expectedArrayOrObject(key, "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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
return expectedArrayOrObject(key, "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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
return expectedArrayOrObject(key, "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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
return expectedArrayOrObject(key, v.Typ)
|
|
}
|
|
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) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
|
|
if v.IsScalar {
|
|
return expectedArrayOrObject(key, v.Type)
|
|
}
|
|
return v.f(func(jv gojq.JQValue) interface{} { return jv.JQValueUpdate(key, u, delpath) })
|
|
}
|
|
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() })
|
|
}
|