1
1
mirror of https://github.com/wader/fq.git synced 2024-11-27 14:14:58 +03:00
fq/pkg/interp/value.go

416 lines
9.5 KiB
Go
Raw Normal View History

2020-06-08 03:29:51 +03:00
package interp
import (
"bytes"
2020-06-08 03:29:51 +03:00
"errors"
2021-08-12 00:51:00 +03:00
"fmt"
2020-06-08 03:29:51 +03:00
"io"
"math/big"
"strings"
"github.com/wader/fq/internal/gojqextra"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
"github.com/wader/gojq"
2020-06-08 03:29:51 +03:00
)
2021-08-12 00:51:00 +03:00
type expectedExtkeyError struct {
Key string
}
func (err expectedExtkeyError) Error() string {
return "expected a extkey but got: " + err.Key
}
type notUpdateableError struct {
Typ string
Key string
}
func (err notUpdateableError) Error() string {
return fmt.Sprintf("cannot update key %s for %s", err.Key, err.Typ)
}
2020-06-08 03:29:51 +03:00
// TODO: rename
2021-09-23 19:35:04 +03:00
type DecodeValue interface {
2021-09-27 12:01:14 +03:00
Value
2021-10-05 23:26:05 +03:00
ToBufferView
2021-09-23 19:35:04 +03:00
DecodeValue() *decode.Value
2020-06-08 03:29:51 +03:00
}
2021-09-28 00:06:46 +03:00
func valueKey(name string, a, b func(name string) interface{}) interface{} {
2021-08-12 00:51:00 +03:00
if strings.HasPrefix(name, "_") {
return a(name)
}
return b(name)
}
2021-09-28 00:06:46 +03:00
func valueHas(key interface{}, a func(name string) interface{}, b func(key interface{}) interface{}) interface{} {
stringKey, ok := key.(string)
if ok && strings.HasPrefix(stringKey, "_") {
if err, ok := a(stringKey).(error); ok {
return err
}
return true
}
return b(key)
}
2021-08-12 00:51:00 +03:00
func makeDecodeValue(dv *decode.Value) interface{} {
2020-06-08 03:29:51 +03:00
switch vv := dv.V.(type) {
case decode.Array:
return NewArrayDecodeValue(dv, vv)
2020-06-08 03:29:51 +03:00
case decode.Struct:
return NewStructDecodeValue(dv, vv)
2021-08-12 00:51:00 +03:00
case *bitio.Buffer:
buf := &bytes.Buffer{}
if _, err := io.Copy(buf, vv.Copy()); err != nil {
return err
}
// TODO: split *bitio.Buffer into just marker (bit range in root bitbuf)
// or *bitio.Buffer if actually other bitbuf
return decodeValue{
JQValue: gojqextra.String(buf.String()),
decodeValueBase: decodeValueBase{dv},
bitsFormat: true,
}
2020-06-08 03:29:51 +03:00
case bool:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Boolean(vv),
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case int:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Number{V: vv},
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case int64:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Number{V: big.NewInt(vv)},
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case uint64:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Number{V: new(big.Int).SetUint64(vv)},
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case float64:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Number{V: vv},
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case string:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.String(vv),
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case []byte:
// TODO: not sure about this
// TODO: only synthentic value without range?
return newBufferRangeFromBuffer(bitio.NewBufferFromBytes(vv, -1), 8)
2020-06-08 03:29:51 +03:00
case []interface{}:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Array(vv),
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case map[string]interface{}:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Object(vv),
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
case nil:
return decodeValue{
2021-08-12 00:51:00 +03:00
JQValue: gojqextra.Null{},
decodeValueBase: decodeValueBase{dv},
2020-06-08 03:29:51 +03:00
}
default:
panic("unreachable")
}
}
type decodeValueBase struct {
dv *decode.Value
}
2021-09-23 19:35:04 +03:00
func (dvb decodeValueBase) DecodeValue() *decode.Value {
return dvb.dv
}
2020-06-08 03:29:51 +03:00
func (dvb decodeValueBase) Display(w io.Writer, opts Options) error { return dump(dvb.dv, w, opts) }
func (dvb decodeValueBase) ToBufferView() (BufferRange, error) {
return BufferRange{bb: dvb.dv.RootBitBuf, r: dvb.dv.Range, unit: 8}, nil
2020-06-08 03:29:51 +03:00
}
func (dvb decodeValueBase) ExtKeys() []string {
kv := []string{
"_start",
"_stop",
"_len",
"_name",
"_root",
"_buffer_root",
"_format_root",
"_parent",
2020-06-08 03:29:51 +03:00
"_symbol",
"_description",
"_path",
"_bits",
"_bytes",
"_error",
"_unknown",
}
if dvb.dv.Format != nil {
kv = append(kv, "_format")
}
return kv
}
func (dvb decodeValueBase) JQValueKey(name string) interface{} {
2021-08-12 00:51:00 +03:00
dv := dvb.dv
switch name {
case "_start":
return big.NewInt(dv.Range.Start)
case "_stop":
return big.NewInt(dv.Range.Stop())
case "_len":
return big.NewInt(dv.Range.Len)
case "_name":
return dv.Name
case "_root":
return makeDecodeValue(dv.Root())
case "_buffer_root":
// TODO: rename?
return makeDecodeValue(dv.BufferRoot())
case "_format_root":
// TODO: rename?
return makeDecodeValue(dv.FormatRoot())
case "_parent":
if dv.Parent == nil {
return nil
}
return makeDecodeValue(dv.Parent)
2021-08-12 00:51:00 +03:00
case "_symbol":
return dv.Symbol
case "_description":
return dv.Description
case "_path":
return valuePath(dv)
case "_error":
var formatErr decode.FormatError
if errors.As(dv.Err, &formatErr) {
return formatErr.Value()
2020-06-08 03:29:51 +03:00
}
2021-08-12 00:51:00 +03:00
return dv.Err
case "_bits":
return BufferRange{
2021-10-05 23:26:05 +03:00
bb: dv.RootBitBuf,
r: dv.Range,
unit: 1,
2021-08-12 00:51:00 +03:00
}
case "_bytes":
return BufferRange{
2021-10-05 23:26:05 +03:00
bb: dv.RootBitBuf,
r: dv.Range,
unit: 8,
2021-08-12 00:51:00 +03:00
}
case "_format":
if dvb.dv.Format == nil {
return nil
}
return dvb.dv.Format.Name
case "_unknown":
return dvb.dv.Unknown
2020-06-08 03:29:51 +03:00
}
2021-08-12 00:51:00 +03:00
return expectedExtkeyError{Key: name}
2020-06-08 03:29:51 +03:00
}
2021-09-23 19:35:04 +03:00
var _ DecodeValue = decodeValue{}
2020-06-08 03:29:51 +03:00
type decodeValue struct {
2021-08-12 00:51:00 +03:00
gojq.JQValue
decodeValueBase
bitsFormat bool
2021-08-12 00:51:00 +03:00
}
func (v decodeValue) JQValueKey(name string) interface{} {
2021-09-28 00:06:46 +03:00
return valueKey(name, v.decodeValueBase.JQValueKey, v.JQValue.JQValueKey)
}
func (v decodeValue) JQValueHas(key interface{}) interface{} {
return valueHas(key, v.decodeValueBase.JQValueKey, v.JQValue.JQValueHas)
2021-08-12 00:51:00 +03:00
}
func (v decodeValue) JQValueToGoJQEx(optsFn func() Options) interface{} {
if !v.bitsFormat {
return v.JQValueToGoJQ()
2021-08-12 00:51:00 +03:00
}
bv, err := v.decodeValueBase.ToBufferView()
if err != nil {
return err
2020-06-08 03:29:51 +03:00
}
bb, err := bv.toBuffer()
if err != nil {
return err
2020-06-08 03:29:51 +03:00
}
s, err := optsFn().BitsFormatFn(bb.Copy())
if err != nil {
return err
}
return s
2020-06-08 03:29:51 +03:00
}
// decode value array
2021-09-23 19:35:04 +03:00
var _ DecodeValue = ArrayDecodeValue{}
2020-06-08 03:29:51 +03:00
2021-09-27 12:01:14 +03:00
type ArrayDecodeValue struct {
2021-08-12 00:51:00 +03:00
gojqextra.Base
decodeValueBase
2020-06-08 03:29:51 +03:00
decode.Array
}
2021-09-27 12:01:14 +03:00
func NewArrayDecodeValue(dv *decode.Value, a decode.Array) ArrayDecodeValue {
return ArrayDecodeValue{
2021-08-12 00:51:00 +03:00
decodeValueBase: decodeValueBase{dv},
Base: gojqextra.Base{Typ: "array"},
Array: a,
}
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueKey(name string) interface{} {
2021-09-28 00:06:46 +03:00
return valueKey(name, v.decodeValueBase.JQValueKey, v.Base.JQValueKey)
2021-08-12 00:51:00 +03:00
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueSliceLen() interface{} { return len(v.Array) }
func (v ArrayDecodeValue) JQValueLength() interface{} { return len(v.Array) }
func (v ArrayDecodeValue) JQValueIndex(index int) interface{} {
2020-06-08 03:29:51 +03:00
// -1 outside after string, -2 outside before string
if index < 0 {
return nil
}
return makeDecodeValue(v.Array[index])
2020-06-08 03:29:51 +03:00
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueSlice(start int, end int) interface{} {
2020-06-08 03:29:51 +03:00
vs := make([]interface{}, end-start)
for i, e := range v.Array[start:end] {
vs[i] = makeDecodeValue(e)
2020-06-08 03:29:51 +03:00
}
return vs
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
return notUpdateableError{Key: fmt.Sprintf("%v", key), Typ: "array"}
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueEach() interface{} {
2020-06-08 03:29:51 +03:00
props := make([]gojq.PathValue, len(v.Array))
for i, f := range v.Array {
props[i] = gojq.PathValue{Path: i, Value: makeDecodeValue(f)}
2020-06-08 03:29:51 +03:00
}
return props
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueKeys() interface{} {
2020-06-08 03:29:51 +03:00
vs := make([]interface{}, len(v.Array))
for i := range v.Array {
vs[i] = i
}
return vs
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueHas(key interface{}) interface{} {
2021-09-28 00:06:46 +03:00
return valueHas(
key,
v.decodeValueBase.JQValueKey,
func(key interface{}) interface{} {
intKey, ok := key.(int)
if !ok {
return gojqextra.HasKeyTypeError{L: "array", R: fmt.Sprintf("%v", key)}
}
return intKey >= 0 && intKey < len(v.Array)
})
2020-06-08 03:29:51 +03:00
}
2021-09-27 12:01:14 +03:00
func (v ArrayDecodeValue) JQValueToGoJQ() interface{} {
2020-06-08 03:29:51 +03:00
vs := make([]interface{}, len(v.Array))
for i, f := range v.Array {
vs[i] = makeDecodeValue(f)
2020-06-08 03:29:51 +03:00
}
return vs
}
// decode value struct
2021-09-23 19:35:04 +03:00
var _ DecodeValue = StructDecodeValue{}
2020-06-08 03:29:51 +03:00
2021-09-27 12:01:14 +03:00
type StructDecodeValue struct {
2021-08-12 00:51:00 +03:00
gojqextra.Base
decodeValueBase
2020-06-08 03:29:51 +03:00
decode.Struct
}
2021-09-27 12:01:14 +03:00
func NewStructDecodeValue(dv *decode.Value, s decode.Struct) StructDecodeValue {
return StructDecodeValue{
2021-08-12 00:51:00 +03:00
decodeValueBase: decodeValueBase{dv},
Base: gojqextra.Base{Typ: "object"},
Struct: s,
}
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueLength() interface{} { return len(v.Struct) }
func (v StructDecodeValue) JQValueSliceLen() interface{} { return len(v.Struct) }
func (v StructDecodeValue) JQValueKey(name string) interface{} {
2021-08-12 00:51:00 +03:00
if strings.HasPrefix(name, "_") {
return v.decodeValueBase.JQValueKey(name)
}
2020-06-08 03:29:51 +03:00
for _, f := range v.Struct {
if f.Name == name {
return makeDecodeValue(f)
2020-06-08 03:29:51 +03:00
}
}
return nil
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
return notUpdateableError{Key: fmt.Sprintf("%v", key), Typ: "object"}
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueEach() interface{} {
2020-06-08 03:29:51 +03:00
props := make([]gojq.PathValue, len(v.Struct))
for i, f := range v.Struct {
props[i] = gojq.PathValue{Path: f.Name, Value: makeDecodeValue(f)}
2020-06-08 03:29:51 +03:00
}
return props
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueKeys() interface{} {
2020-06-08 03:29:51 +03:00
vs := make([]interface{}, len(v.Struct))
for i, f := range v.Struct {
vs[i] = f.Name
}
return vs
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueHas(key interface{}) interface{} {
2021-09-28 00:06:46 +03:00
return valueHas(
key,
v.decodeValueBase.JQValueKey,
func(key interface{}) interface{} {
stringKey, ok := key.(string)
if !ok {
return gojqextra.HasKeyTypeError{L: "object", R: fmt.Sprintf("%v", key)}
}
for _, f := range v.Struct {
if f.Name == stringKey {
return true
}
}
return false
},
)
2020-06-08 03:29:51 +03:00
}
2021-09-27 12:01:14 +03:00
func (v StructDecodeValue) JQValueToGoJQ() interface{} {
2020-06-08 03:29:51 +03:00
vm := make(map[string]interface{}, len(v.Struct))
for _, f := range v.Struct {
vm[f.Name] = makeDecodeValue(f)
2020-06-08 03:29:51 +03:00
}
return vm
}