616 lines
10 KiB
Go
616 lines
10 KiB
Go
package expr
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/cortezaproject/corteza/server/pkg/sql"
|
|
|
|
"github.com/PaesslerAG/gval"
|
|
"github.com/cortezaproject/corteza/server/pkg/errors"
|
|
"github.com/spf13/cast"
|
|
)
|
|
|
|
func (t *Vars) Len() (out int) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
return len(t.value)
|
|
}
|
|
|
|
func (t *Vars) Select(k string) (out TypedValue, err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if v, is := t.value[k]; is {
|
|
return v, nil
|
|
} else {
|
|
return nil, errors.NotFound("no such key '%s'", k)
|
|
}
|
|
}
|
|
|
|
func (t *Vars) AssignFieldValue(key string, val TypedValue) (err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.Lock()
|
|
defer t.mux.Unlock()
|
|
|
|
if t.value == nil {
|
|
t.value = make(map[string]TypedValue)
|
|
}
|
|
|
|
t.value[key] = val
|
|
|
|
return
|
|
}
|
|
|
|
func (t *Vars) ResolveTypes(res func(typ string) Type) (err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
for k, v := range t.value {
|
|
if u, is := v.(*Unresolved); is {
|
|
if res(u.Type()) == nil {
|
|
return errors.NotFound("failed to resolve unknown or unregistered type %q on %q", u.Type(), k)
|
|
}
|
|
|
|
t.value[k], err = res(u.Type()).Cast(t.value[k])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to resolve: %w", err)
|
|
}
|
|
}
|
|
|
|
if r, is := t.value[k].(resolvableType); is {
|
|
if err = r.ResolveTypes(res); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Merge combines the given Vars(es) into Vars
|
|
// NOTE: It will return CLONE of the original Vars, if it's called without any parameters
|
|
func (t *Vars) Merge(nn ...Iterator) (out TypedValue, err error) {
|
|
return t.MustMerge(nn...), nil
|
|
}
|
|
|
|
func (t *Vars) IsEmpty() bool {
|
|
return t == nil || len(t.value) == 0
|
|
}
|
|
|
|
// MustMerge returns Vars after merging the given Vars(es) into it
|
|
func (t *Vars) MustMerge(nn ...Iterator) *Vars {
|
|
if t != nil {
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
nn = append([]Iterator{t}, nn...)
|
|
}
|
|
|
|
var (
|
|
out = &Vars{value: make(map[string]TypedValue, 4)}
|
|
)
|
|
|
|
for _, i := range nn {
|
|
_ = i.Each(func(k string, v TypedValue) error {
|
|
out.value[k] = v
|
|
return nil
|
|
})
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// Copy takes base variables and copies all to dst
|
|
func (t *Vars) Copy(dst *Vars, kk ...string) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
if dst.value == nil {
|
|
dst.value = make(map[string]TypedValue)
|
|
}
|
|
|
|
for _, k := range kk {
|
|
dst.value[k] = t.value[k]
|
|
}
|
|
}
|
|
|
|
// Has returns true key is present
|
|
func (t *Vars) Has(key string) bool {
|
|
return t.HasAll(key)
|
|
}
|
|
|
|
// HasAll returns true if all keys are present
|
|
func (t *Vars) HasAll(key string, kk ...string) bool {
|
|
if t == nil {
|
|
return false
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t == nil {
|
|
return false
|
|
}
|
|
|
|
for _, key = range append([]string{key}, kk...) {
|
|
if _, has := t.value[key]; !has {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// HasAny returns true if all keys are present
|
|
func (t *Vars) HasAny(key string, kk ...string) bool {
|
|
if t == nil {
|
|
return false
|
|
}
|
|
|
|
for _, key = range append([]string{key}, kk...) {
|
|
if _, has := t.value[key]; has {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
var _ gval.Selector = &Vars{}
|
|
|
|
func (t *Vars) Dict() (out map[string]interface{}) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
out = make(map[string]interface{})
|
|
for k, v := range t.value {
|
|
switch v := v.(type) {
|
|
case Dict:
|
|
out[k] = v.Dict()
|
|
|
|
case Slice:
|
|
out[k] = v.Slice()
|
|
|
|
case TypedValue:
|
|
tmp := v.Get()
|
|
if d, is := tmp.(Dict); is {
|
|
out[k] = d.Dict()
|
|
} else {
|
|
out[k] = tmp
|
|
}
|
|
|
|
default:
|
|
out[k] = v
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (t *Vars) Decode(dst interface{}) (err error) {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
dstRef := reflect.ValueOf(dst)
|
|
|
|
if dstRef.Kind() != reflect.Ptr {
|
|
return fmt.Errorf("expecting a pointer, not a value")
|
|
}
|
|
|
|
if dstRef.IsNil() {
|
|
return fmt.Errorf("nil pointer passed")
|
|
}
|
|
|
|
dstRef = dstRef.Elem()
|
|
|
|
for i := 0; i < dstRef.NumField(); i++ {
|
|
var (
|
|
value TypedValue
|
|
has bool
|
|
ftyp = dstRef.Type().Field(i)
|
|
)
|
|
|
|
keyName := ftyp.Tag.Get("var")
|
|
if keyName == "" {
|
|
keyName = strings.ToLower(ftyp.Name[:1]) + ftyp.Name[1:]
|
|
}
|
|
|
|
value, has = t.value[keyName]
|
|
if !has {
|
|
continue
|
|
}
|
|
|
|
if tvd, is := value.(TypeValueDecoder); is {
|
|
if err = tvd.Decode(dstRef.Field(i)); err != nil {
|
|
return
|
|
}
|
|
} else if err = decode(dstRef.Field(i), value); err != nil {
|
|
return fmt.Errorf("failed to decode value to field %s: %w", ftyp.Name, err)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (t *Vars) Scan(src any) error { return sql.ParseJSON(src, t) }
|
|
|
|
func (t *Vars) Value() (driver.Value, error) {
|
|
if t != nil {
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
}
|
|
|
|
return json.Marshal(t)
|
|
}
|
|
|
|
func (t *Vars) SelectGVal(_ context.Context, k string) (out interface{}, err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
out, err = t.Select(k)
|
|
switch c := out.(type) {
|
|
case gval.Selector:
|
|
return c, err
|
|
default:
|
|
return UntypedValue(out), err
|
|
}
|
|
}
|
|
|
|
// UnmarshalJSON unmarshal JSON value into Vars
|
|
func (t *Vars) UnmarshalJSON(in []byte) (err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.Lock()
|
|
defer t.mux.Unlock()
|
|
|
|
var (
|
|
aux = make(map[string]*typedValueWrap)
|
|
)
|
|
|
|
if t.value == nil {
|
|
t.value = make(map[string]TypedValue)
|
|
}
|
|
|
|
if len(in) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if err = json.Unmarshal(in, &aux); err != nil {
|
|
return
|
|
}
|
|
|
|
for k, v := range aux {
|
|
if t.value[k], err = NewUnresolved(v.Type, v.Value); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (t *Vars) Each(fn func(k string, v TypedValue) error) (err error) {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t.value == nil {
|
|
return
|
|
}
|
|
|
|
for k, v := range t.value {
|
|
if err = fn(k, v); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Set or update the specific key value in Vars
|
|
func (t *Vars) Set(k string, v interface{}) (err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t.value == nil {
|
|
t.value = make(map[string]TypedValue)
|
|
}
|
|
|
|
t.value[k], err = Typify(v)
|
|
return
|
|
}
|
|
|
|
// MarshalJSON returns JSON encoding of expression
|
|
func (t *Vars) MarshalJSON() (out []byte, err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
aux := make(map[string]*typedValueWrap)
|
|
for k, v := range t.value {
|
|
if v == nil {
|
|
continue
|
|
}
|
|
|
|
aux[k] = &typedValueWrap{Type: v.Type()}
|
|
|
|
if _, is := v.(json.Marshaler); is {
|
|
aux[k].Value = v
|
|
} else {
|
|
aux[k].Value = v.Get()
|
|
}
|
|
}
|
|
|
|
return json.Marshal(aux)
|
|
}
|
|
|
|
func decode(dst reflect.Value, src TypedValue) (err error) {
|
|
defer func() {
|
|
r := recover()
|
|
if r == nil {
|
|
return
|
|
}
|
|
|
|
switch r := r.(type) {
|
|
case error:
|
|
err = r
|
|
default:
|
|
err = fmt.Errorf("%v", r)
|
|
}
|
|
}()
|
|
|
|
if dst.Kind() == reflect.Interface && reflect.ValueOf(src).Type().Implements(dst.Type()) {
|
|
dst.Set(reflect.ValueOf(src))
|
|
return
|
|
}
|
|
|
|
if reflect.ValueOf(src).Type().ConvertibleTo(dst.Type()) {
|
|
dst.Set(reflect.ValueOf(src))
|
|
return
|
|
}
|
|
|
|
raw := UntypedValue(src)
|
|
// Optimistically try to decode source to destination by comparing (internal) value type for destination
|
|
if reflect.ValueOf(raw).Type().ConvertibleTo(dst.Type()) {
|
|
dst.Set(reflect.ValueOf(raw))
|
|
return
|
|
}
|
|
|
|
var (
|
|
vBool bool
|
|
vInt64 int64
|
|
vUint64 uint64
|
|
vFloat64 float64
|
|
vString string
|
|
)
|
|
|
|
switch dst.Kind() {
|
|
case reflect.Bool:
|
|
if vBool, err = cast.ToBoolE(raw); err == nil {
|
|
dst.SetBool(vBool)
|
|
}
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if vInt64, err = cast.ToInt64E(raw); err == nil {
|
|
dst.SetInt(vInt64)
|
|
}
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
if vUint64, err = cast.ToUint64E(raw); err == nil {
|
|
dst.SetUint(vUint64)
|
|
}
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
if vFloat64, err = cast.ToFloat64E(raw); err == nil {
|
|
dst.SetFloat(vFloat64)
|
|
}
|
|
|
|
case reflect.String:
|
|
if vString, err = cast.ToStringE(raw); err == nil {
|
|
dst.SetString(vString)
|
|
}
|
|
|
|
case reflect.Map:
|
|
dst.Set(reflect.ValueOf(src.Get()))
|
|
|
|
//case reflect.Interface:
|
|
// dst.Set(reflect.ValueOf(raw))
|
|
|
|
default:
|
|
return fmt.Errorf("failed to cast %T to %s", raw, dst.Kind())
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to cast %T to %s: %w", raw, dst.Kind(), err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CastToMeta(val interface{}) (out map[string]any, err error) {
|
|
val = UntypedValue(val)
|
|
|
|
if val == nil {
|
|
return make(map[string]any), nil
|
|
}
|
|
|
|
switch c := val.(type) {
|
|
case *Vars:
|
|
c.mux.RLock()
|
|
defer c.mux.RUnlock()
|
|
|
|
out = make(map[string]any)
|
|
for k, v := range c.value {
|
|
out[k] = v.Get()
|
|
}
|
|
|
|
return
|
|
case map[string]string:
|
|
out = make(map[string]any)
|
|
for k, v := range c {
|
|
out[k] = v
|
|
}
|
|
|
|
return
|
|
case map[string][]string:
|
|
out = make(map[string]any)
|
|
for k, v := range c {
|
|
out[k] = v
|
|
}
|
|
|
|
return
|
|
case KV:
|
|
out = make(map[string]any)
|
|
for k, v := range c.value {
|
|
out[k] = v
|
|
}
|
|
|
|
return
|
|
case KVV:
|
|
out = make(map[string]any)
|
|
for k, v := range c.value {
|
|
out[k] = v
|
|
}
|
|
|
|
return
|
|
case map[string]any:
|
|
return c, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
|
}
|
|
func CastToVars(val interface{}) (out map[string]TypedValue, err error) {
|
|
val = UntypedValue(val)
|
|
|
|
if val == nil {
|
|
return make(map[string]TypedValue), nil
|
|
}
|
|
|
|
switch c := val.(type) {
|
|
case *Vars:
|
|
c.mux.RLock()
|
|
defer c.mux.RUnlock()
|
|
|
|
return c.value, nil
|
|
case map[string]TypedValue:
|
|
return c, nil
|
|
case map[string]interface{}:
|
|
out = make(map[string]TypedValue)
|
|
for k, v := range c {
|
|
out[k], err = Typify(v)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
|
}
|
|
|
|
// Filter take keys returns Vars with only those key value pair
|
|
func (t *Vars) Filter(keys ...string) (out TypedValue, err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t.value == nil {
|
|
return
|
|
}
|
|
vars := EmptyVars()
|
|
|
|
for _, k := range keys {
|
|
_, has := t.value[k]
|
|
if has {
|
|
vars.value[k] = t.value[k]
|
|
}
|
|
}
|
|
|
|
return vars, nil
|
|
}
|
|
|
|
// Delete take keys returns Vars without those key value pair
|
|
func (t *Vars) Delete(keys ...string) (out TypedValue, err error) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
t.mux.RLock()
|
|
defer t.mux.RUnlock()
|
|
|
|
if t.value == nil {
|
|
return
|
|
}
|
|
|
|
// get cloned Vars
|
|
out, err = t.Merge()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
vars := out.(*Vars)
|
|
|
|
// Delete key from t.value if exist
|
|
for _, k := range keys {
|
|
delete(vars.value, k)
|
|
}
|
|
|
|
return vars, nil
|
|
}
|