3
0
2024-02-06 13:39:22 +01:00

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
}