More improvements on expr vars & rec values
This commit is contained in:
parent
597c18fd16
commit
333b2f344f
@ -71,7 +71,7 @@ func (i *collectionIterator) Next(context.Context, *Vars) (out *Vars, err error)
|
||||
case TypedValue:
|
||||
item = c
|
||||
default:
|
||||
if item, err = Cast(c); err != nil {
|
||||
if item, err = Typify(c); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +164,6 @@ func (ctrl Workflow) Exec(ctx context.Context, r *request.WorkflowExec) (interfa
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve ComposeRecord type: %w", err)
|
||||
}
|
||||
|
||||
c.GetValue().SetModule(mod)
|
||||
}
|
||||
|
||||
|
||||
@ -168,7 +168,7 @@ func (set ExprSet) Eval(ctx context.Context, in *expr.Vars) (*expr.Vars, error)
|
||||
return nil, fmt.Errorf("cannot cast value %T to %s: %w", value, e.typ.Type(), err)
|
||||
}
|
||||
} else {
|
||||
typedValue, err = expr.Cast(value)
|
||||
typedValue, err = expr.Typify(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot cast value %T to %s: %w", value, e.typ.Type(), err)
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func TestExprSet_Eval(t *testing.T) {
|
||||
name: "vars with path",
|
||||
set: ExprSet{&Expr{Target: "l1.l2", Expr: `"bar"`}},
|
||||
input: RVars{"l1": RVars{}.Vars()},
|
||||
output: RVars{"l1": RVars{"l2": Must(Cast("bar"))}.Vars()},
|
||||
output: RVars{"l1": RVars{"l2": Must(Typify("bar"))}.Vars()},
|
||||
},
|
||||
{
|
||||
name: "copy vars with same types",
|
||||
@ -118,7 +118,7 @@ func TestExprSet_Eval(t *testing.T) {
|
||||
},
|
||||
output: RVars{
|
||||
"arr": Must(NewArray([]TypedValue{
|
||||
Must(Cast("foo")),
|
||||
Must(Typify("foo")),
|
||||
})),
|
||||
},
|
||||
},
|
||||
|
||||
@ -111,7 +111,7 @@ var _ gval.Selector = &ComposeRecord{}
|
||||
// It allows gval lib to access Record's underlying value (*types.Record)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeRecord) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, error) {
|
||||
if k == "values" {
|
||||
if t.value.Values == nil {
|
||||
t.value.Values = types.RecordValueSet{}
|
||||
@ -242,12 +242,12 @@ func composeRecordValuesGValSelector(res *types.Record, k string) (interface{},
|
||||
return nil, nil
|
||||
|
||||
case len(vv) == 1 && !multiValueField:
|
||||
return recordValueCast(field, vv[0])
|
||||
return vv[0].Cast(field)
|
||||
|
||||
default:
|
||||
out := make([]interface{}, 0, len(vv))
|
||||
return out, vv.Walk(func(v *types.RecordValue) error {
|
||||
i, err := recordValueCast(field, v)
|
||||
i, err := v.Cast(field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -429,61 +429,20 @@ func CastToComposeRecordValueErrorSet(val interface{}) (out *types.RecordValueEr
|
||||
}
|
||||
}
|
||||
|
||||
func recordValueCast(field *types.ModuleField, rv *types.RecordValue) (interface{}, error) {
|
||||
if field == nil {
|
||||
// safe fallback to string
|
||||
return rv.Value, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case field.IsRef():
|
||||
return rv.Ref, nil
|
||||
|
||||
case field.IsDateTime():
|
||||
return cast.ToTimeE(rv.Value)
|
||||
|
||||
case field.IsBoolean():
|
||||
return cast.ToBoolE(rv.Value)
|
||||
|
||||
case field.IsNumeric():
|
||||
if field.Options.Precision() == 0 {
|
||||
return cast.ToInt64E(rv.Value)
|
||||
}
|
||||
|
||||
return cast.ToFloat64E(rv.Value)
|
||||
|
||||
default:
|
||||
return rv.Value, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func recordValueToExprTypedValue(field *types.ModuleField, rv *types.RecordValue) (expr.TypedValue, error) {
|
||||
if field == nil {
|
||||
// safe fallback to string
|
||||
return expr.NewString(rv.Value)
|
||||
}
|
||||
|
||||
switch {
|
||||
case field == nil:
|
||||
return expr.NewString(rv.Value)
|
||||
|
||||
case field.IsRef():
|
||||
return expr.NewID(rv.Ref)
|
||||
|
||||
case field.IsDateTime():
|
||||
return expr.NewDateTime(rv.Value)
|
||||
|
||||
case field.IsBoolean():
|
||||
return expr.NewBoolean(rv.Value)
|
||||
|
||||
case field.IsNumeric():
|
||||
if field.Options.Precision() == 0 {
|
||||
return expr.NewInteger(rv.Value)
|
||||
}
|
||||
|
||||
return expr.NewFloat(rv.Value)
|
||||
|
||||
default:
|
||||
return expr.NewString(rv.Value)
|
||||
|
||||
if v, err := rv.Cast(field); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return expr.Typify(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -52,10 +52,10 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
v expr.TypedValue
|
||||
|
||||
mod = &types.Module{Fields: types.ModuleFieldSet{
|
||||
&types.ModuleField{Name: "s1", Multi: false},
|
||||
&types.ModuleField{Name: "m1", Multi: true},
|
||||
&types.ModuleField{Name: "m2", Multi: true},
|
||||
&types.ModuleField{Name: "s2", Multi: false},
|
||||
&types.ModuleField{Name: "s1", Multi: false, Kind: "String"},
|
||||
&types.ModuleField{Name: "m1", Multi: true, Kind: "String"},
|
||||
&types.ModuleField{Name: "m2", Multi: true, Kind: "String"},
|
||||
&types.ModuleField{Name: "s2", Multi: false, Kind: "String"},
|
||||
&types.ModuleField{Name: "b0", Multi: false, Kind: "Bool"},
|
||||
&types.ModuleField{Name: "b1", Multi: false, Kind: "Bool"},
|
||||
}}
|
||||
@ -108,18 +108,24 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
test bool
|
||||
expr string
|
||||
}{
|
||||
// interaction with set values
|
||||
{true, `rec.values.s1 == "sVal1"`},
|
||||
{false, `rec.values.s1 == "sVal2"`},
|
||||
{true, `rec.values.s1`},
|
||||
{false, `rec.values.s2`},
|
||||
{true, `rec.values.s1 != "foo"`},
|
||||
{true, `rec.values.s2 != "foo"`},
|
||||
{true, `!rec.values.s2`},
|
||||
|
||||
// interaction with unset (= nil) values
|
||||
{true, `rec.values.s2 != "foo"`},
|
||||
{false, `rec.values.s2 == "foo"`},
|
||||
{true, `!rec.values.s2`},
|
||||
{false, `rec.values.s2`},
|
||||
|
||||
// multival
|
||||
{true, `rec.values.m1[0] == "mVal1.0"`},
|
||||
{true, `rec.values.m1[1] == "mVal1.1"`},
|
||||
{true, `rec.values.m2[0] == "mVal2.0"`},
|
||||
|
||||
// booleans
|
||||
{true, `!rec.values.b0`},
|
||||
{false, `rec.values.b0`},
|
||||
{true, `rec.values.b1`},
|
||||
|
||||
@ -5,10 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/spf13/cast"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -53,6 +51,34 @@ func (v RecordValue) Clone() *RecordValue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v RecordValue) Cast(f *ModuleField) (interface{}, error) {
|
||||
if f == nil {
|
||||
// safe fallback to string
|
||||
return v.Value, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.IsRef():
|
||||
return v.Ref, nil
|
||||
|
||||
case f.IsDateTime():
|
||||
return cast.ToTimeE(v.Value)
|
||||
|
||||
case f.IsBoolean():
|
||||
return cast.ToBoolE(v.Value)
|
||||
|
||||
case f.IsNumeric():
|
||||
if f.Options.Precision() == 0 {
|
||||
return cast.ToInt64E(v.Value)
|
||||
}
|
||||
|
||||
return cast.ToFloat64E(v.Value)
|
||||
|
||||
default:
|
||||
return v.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (set RecordValueSet) Clone() (vv RecordValueSet) {
|
||||
vv = make(RecordValueSet, len(vv))
|
||||
for i := range set {
|
||||
@ -337,23 +363,6 @@ func (set RecordValueSet) String() (o string) {
|
||||
func (set RecordValueSet) Dict(fields ModuleFieldSet) map[string]interface{} {
|
||||
var (
|
||||
rval = make(map[string]interface{})
|
||||
|
||||
format = func(f *ModuleField, v string) interface{} {
|
||||
switch strings.ToLower(f.Kind) {
|
||||
case "bool":
|
||||
return payload.ParseBool(v)
|
||||
case "number":
|
||||
if f.Options.Precision() > 0 {
|
||||
num, _ := strconv.ParseFloat(v, 64)
|
||||
return num
|
||||
}
|
||||
|
||||
num, _ := strconv.ParseInt(v, 10, 64)
|
||||
return num
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
)
|
||||
|
||||
if len(fields) == 0 {
|
||||
@ -361,17 +370,20 @@ func (set RecordValueSet) Dict(fields ModuleFieldSet) map[string]interface{} {
|
||||
}
|
||||
|
||||
_ = fields.Walk(func(f *ModuleField) error {
|
||||
// make sure all fields are set at least to nil
|
||||
rval[f.Name] = nil
|
||||
|
||||
if f.Multi {
|
||||
var (
|
||||
rv = set.FilterByName(f.Name)
|
||||
vv = make([]interface{}, len(rv))
|
||||
)
|
||||
for i, val := range rv {
|
||||
vv[i] = format(f, val.Value)
|
||||
vv[i], _ = val.Cast(f)
|
||||
}
|
||||
rval[f.Name] = vv
|
||||
} else if v := set.Get(f.Name, 0); v != nil {
|
||||
rval[f.Name] = format(f, v.Value)
|
||||
rval[f.Name], _ = v.Cast(f)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -28,8 +28,8 @@ func ResolveTypes(rt resolvableType, resolver func(typ string) Type) error {
|
||||
return rt.ResolveTypes(resolver)
|
||||
}
|
||||
|
||||
// cast intput into some well-known types
|
||||
func Cast(in interface{}) (tv TypedValue, err error) {
|
||||
// Typify detects input type and wraps it with expression type
|
||||
func Typify(in interface{}) (tv TypedValue, err error) {
|
||||
var is bool
|
||||
if tv, is = in.(TypedValue); is {
|
||||
return
|
||||
@ -135,7 +135,7 @@ func CastToArray(val interface{}) (out []TypedValue, err error) {
|
||||
out = make([]TypedValue, ref.Len())
|
||||
for i := 0; i < ref.Len(); i++ {
|
||||
item := ref.Index(i).Interface()
|
||||
out[i], err = Cast(item)
|
||||
out[i], err = Typify(item)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ func push(arr interface{}, nn ...interface{}) (out interface{}, err error) {
|
||||
stv = append(stv, tv)
|
||||
} else {
|
||||
// wrap unknown types...
|
||||
stv = append(stv, Must(Cast(n)))
|
||||
stv = append(stv, Must(Typify(n)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -66,11 +66,13 @@ func (p *gvalParser) ParseEvaluators(ee ...Evaluator) error {
|
||||
}
|
||||
|
||||
func (e *gvalEval) Eval(ctx context.Context, scope *Vars) (interface{}, error) {
|
||||
return e.evaluable(ctx, scope.Dict())
|
||||
return e.evaluable(ctx, scope)
|
||||
//return e.evaluable(ctx, scope.Dict())
|
||||
}
|
||||
|
||||
func (e *gvalEval) Test(ctx context.Context, scope *Vars) (bool, error) {
|
||||
r, err := e.evaluable(ctx, scope.Dict())
|
||||
r, err := e.evaluable(ctx, scope)
|
||||
//r, err := e.evaluable(ctx, scope.Dict())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -225,6 +226,10 @@ func (t *Vars) Value() (driver.Value, error) {
|
||||
return json.Marshal(t)
|
||||
}
|
||||
|
||||
func (t Vars) SelectGVal(_ context.Context, k string) (interface{}, error) {
|
||||
return t.Select(k)
|
||||
}
|
||||
|
||||
// UnmarshalJSON
|
||||
func (t *Vars) UnmarshalJSON(in []byte) (err error) {
|
||||
if len(in) == 0 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user