1
1
mirror of https://github.com/wader/fq.git synced 2024-11-23 09:56:07 +03:00
fq/internal/gojqextra/gojqextra.go
2021-09-12 13:08:50 +02:00

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 }