mirror of
https://github.com/wader/fq.git
synced 2024-11-23 09:56:07 +03:00
402 lines
12 KiB
Go
402 lines
12 KiB
Go
//nolint:gosimple
|
|
package gojqextra
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/wader/fq/internal/colorjson"
|
|
|
|
"github.com/wader/gojq"
|
|
)
|
|
|
|
// TODO: refactor to use errors from gojq?
|
|
// TODO: preview errors
|
|
|
|
type FuncTypeError struct {
|
|
Name string
|
|
Typ string
|
|
}
|
|
|
|
func (err FuncTypeError) Error() string { return err.Name + " cannot be applied to: " + err.Typ }
|
|
|
|
type ExpectedObjectError struct {
|
|
Typ string
|
|
}
|
|
|
|
func (err ExpectedObjectError) Error() string {
|
|
return "expected an object but got: " + err.Typ
|
|
}
|
|
|
|
type ExpectedArrayError struct {
|
|
Typ string
|
|
}
|
|
|
|
func (err ExpectedArrayError) Error() string {
|
|
return "expected an array but got: " + err.Typ
|
|
}
|
|
|
|
type ExpectedObjectWithKeyError struct {
|
|
Typ string
|
|
Key string
|
|
}
|
|
|
|
func (err ExpectedObjectWithKeyError) Error() string {
|
|
return fmt.Sprintf("expected an object with key %q but got: %s", err.Key, err.Typ)
|
|
}
|
|
|
|
type ExpectedArrayWithIndexError struct {
|
|
Typ string
|
|
Index int
|
|
}
|
|
|
|
func (err ExpectedArrayWithIndexError) Error() string {
|
|
return fmt.Sprintf("expected an array with index %d but got: %s", err.Index, err.Typ)
|
|
}
|
|
|
|
type IteratorError struct {
|
|
Typ string
|
|
}
|
|
|
|
func (err IteratorError) Error() string {
|
|
return "cannot iterate over: " + err.Typ
|
|
}
|
|
|
|
type HasKeyTypeError struct {
|
|
L, R string
|
|
}
|
|
|
|
func (err HasKeyTypeError) Error() string {
|
|
return "cannot check whether " + err.L + " has a key: " + err.R
|
|
}
|
|
|
|
type ArrayIndexTooLargeError struct {
|
|
V interface{}
|
|
}
|
|
|
|
func (err *ArrayIndexTooLargeError) Error() string {
|
|
return fmt.Sprintf("array index too large: %v", err.V)
|
|
}
|
|
|
|
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")
|
|
}
|
|
}
|
|
|
|
type NonUpdatableTypeError struct {
|
|
Typ string
|
|
Key string
|
|
}
|
|
|
|
func (err NonUpdatableTypeError) Error() string {
|
|
return fmt.Sprintf("update key %v cannot be applied to: %s", err.Key, err.Typ)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
return FuncTypeError{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 FuncTypeError{Name: "tonumber", Typ: "array"}
|
|
}
|
|
func (v Array) JQValueToString() interface{} {
|
|
return FuncTypeError{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 FuncTypeError{Name: "tonumber", Typ: "object"}
|
|
}
|
|
func (v Object) JQValueToString() interface{} {
|
|
return FuncTypeError{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 FuncTypeError{Name: "keys", Typ: "number"} }
|
|
func (v Number) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeError{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 string
|
|
|
|
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 FuncTypeError{Name: "keys", Typ: "string"} }
|
|
func (v String) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeError{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 FuncTypeError{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 FuncTypeError{Name: "keys", Typ: "boolean"} }
|
|
func (v Boolean) JQValueHas(key interface{}) interface{} {
|
|
return FuncTypeError{Name: "has", Typ: "boolean"}
|
|
}
|
|
func (v Boolean) JQValueType() string { return "boolean" }
|
|
func (v Boolean) JQValueToNumber() interface{} {
|
|
return FuncTypeError{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 FuncTypeError{Name: "keys", Typ: "null"} }
|
|
func (v Null) JQValueHas(key interface{}) interface{} { return FuncTypeError{Name: "has", Typ: "null"} }
|
|
func (v Null) JQValueType() string { return "null" }
|
|
func (v Null) JQValueToNumber() interface{} { return FuncTypeError{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 FuncTypeError{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 FuncTypeError{Name: "tonumber", Typ: v.Typ} }
|
|
func (v Base) JQValueToString() interface{} { return FuncTypeError{Name: "tostring", Typ: v.Typ} }
|
|
func (v Base) JQValueToGoJQ() interface{} { return nil }
|