3
0
Files
corteza/pkg/expr/vars.go
Vivek Patel 422f870f40 Add nil check for Vars methods
Fix nil pointer error while calling Vars method with nil Vars, that caused due to 1st statement in method was locking the Vars and expecting the mux preset in given Vars.
2022-10-04 17:16:17 +05:30

611 lines
10 KiB
Go

package expr
import (
"context"
"database/sql/driver"
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/sql"
"reflect"
"strings"
"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
}
// 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)}
)
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
}