Improvments on expressions, gw tests
This commit is contained in:
parent
723c361171
commit
597c18fd16
@ -388,13 +388,6 @@ func (t *ComposeRecord) Assign(val interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Record
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeRecord) Select(k string) (TypedValue, error) {
|
||||
return composeRecordTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeRecord) Has(k string) bool {
|
||||
switch k {
|
||||
case "ID", "recordID":
|
||||
@ -576,41 +569,3 @@ func (t *ComposeRecordValueErrorSet) Assign(val interface{}) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ComposeRecordValues is an expression type, wrapper for types.RecordValueSet type
|
||||
type ComposeRecordValues struct{ value types.RecordValueSet }
|
||||
|
||||
// NewComposeRecordValues creates new instance of ComposeRecordValues expression type
|
||||
func NewComposeRecordValues(val interface{}) (*ComposeRecordValues, error) {
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeRecordValues: %w", err)
|
||||
} else {
|
||||
return &ComposeRecordValues{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeRecordValues
|
||||
func (t ComposeRecordValues) Get() interface{} { return t.value }
|
||||
|
||||
// Return underlying value on ComposeRecordValues
|
||||
func (t ComposeRecordValues) GetValue() types.RecordValueSet { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeRecordValues) Type() string { return "ComposeRecordValues" }
|
||||
|
||||
// Convert value to types.RecordValueSet
|
||||
func (ComposeRecordValues) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeRecordValues(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeRecordValues
|
||||
//
|
||||
// value is first passed through CastToComposeRecordValues
|
||||
func (t *ComposeRecordValues) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ var _ expr.DeepFieldAssigner = &ComposeRecord{}
|
||||
func (t *ComposeRecord) AssignFieldValue(kk []string, val expr.TypedValue) error {
|
||||
switch kk[0] {
|
||||
case "values":
|
||||
return assignToComposeRecordValues(&t.value.Values, kk[1:], val)
|
||||
return assignToComposeRecordValues(t.value, kk[1:], val)
|
||||
// @todo deep setting labels
|
||||
default:
|
||||
return assignToComposeRecord(t.value, kk[0], val)
|
||||
@ -123,6 +123,23 @@ func (t ComposeRecord) SelectGVal(ctx context.Context, k string) (interface{}, e
|
||||
return composeRecordGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.ComposeRecord
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeRecord) Select(k string) (expr.TypedValue, error) {
|
||||
if k == "values" {
|
||||
if t.value.Values == nil {
|
||||
t.value.Values = types.RecordValueSet{}
|
||||
}
|
||||
|
||||
return &ComposeRecordValues{value: t.value}, nil
|
||||
}
|
||||
|
||||
return composeRecordTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
type ComposeRecordValues struct{ value *types.Record }
|
||||
|
||||
func CastToComposeRecordValues(val interface{}) (out types.RecordValueSet, err error) {
|
||||
out = types.RecordValueSet{}
|
||||
switch val := val.(type) {
|
||||
@ -174,7 +191,7 @@ func CastToComposeRecordValues(val interface{}) (out types.RecordValueSet, err e
|
||||
}
|
||||
|
||||
func (t *ComposeRecordValues) AssignFieldValue(pp []string, val expr.TypedValue) error {
|
||||
return assignToComposeRecordValues(&t.value, pp, val)
|
||||
return assignToComposeRecordValues(t.value, pp, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
@ -182,34 +199,60 @@ func (t *ComposeRecordValues) AssignFieldValue(pp []string, val expr.TypedValue)
|
||||
// It allows gval lib to access Record's underlying value (*types.RecordValues)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeRecordValues) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
func (t *ComposeRecordValues) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
return composeRecordValuesGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Record
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeRecordValues) Select(k string) (expr.TypedValue, error) {
|
||||
func (t *ComposeRecordValues) Select(k string) (expr.TypedValue, error) {
|
||||
return composeRecordValuesTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeRecordValues) Has(k string) bool {
|
||||
return t.value.Get(k, 0) != nil
|
||||
return t.value.Values.Get(k, 0) != nil
|
||||
}
|
||||
|
||||
// recordGValSelector is field accessor for *types.RecordValueSet
|
||||
func composeRecordValuesGValSelector(res types.RecordValueSet, k string) (interface{}, error) {
|
||||
vv := res.FilterByName(k)
|
||||
func composeRecordValuesGValSelector(res *types.Record, k string) (interface{}, error) {
|
||||
var (
|
||||
vv = res.Values.FilterByName(k)
|
||||
|
||||
multiValueField bool
|
||||
field *types.ModuleField
|
||||
)
|
||||
|
||||
if mod := res.GetModule(); mod != nil {
|
||||
fld := mod.Fields.FindByName(k)
|
||||
|
||||
if fld == nil {
|
||||
return nil, fmt.Errorf("field '%s' does not exist on module %s", k, mod.Name)
|
||||
}
|
||||
|
||||
multiValueField = fld.Multi
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(vv) == 0:
|
||||
if field != nil && field.IsBoolean() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
switch len(vv) {
|
||||
case 0:
|
||||
return nil, nil
|
||||
case 1:
|
||||
return vv[0].Value, nil
|
||||
|
||||
case len(vv) == 1 && !multiValueField:
|
||||
return recordValueCast(field, vv[0])
|
||||
|
||||
default:
|
||||
out := make([]string, 0, len(vv))
|
||||
out := make([]interface{}, 0, len(vv))
|
||||
return out, vv.Walk(func(v *types.RecordValue) error {
|
||||
out = append(out, v.Value)
|
||||
i, err := recordValueCast(field, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out = append(out, i)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -218,34 +261,49 @@ func composeRecordValuesGValSelector(res types.RecordValueSet, k string) (interf
|
||||
// recordValuesTypedValueSelector is field accessor for *types.RecordValueSet
|
||||
//
|
||||
// @todo return appropriate types (atm all values are returned as String)
|
||||
func composeRecordValuesTypedValueSelector(res types.RecordValueSet, k string) (expr.TypedValue, error) {
|
||||
vv := res.FilterByName(k)
|
||||
func composeRecordValuesTypedValueSelector(res *types.Record, k string) (expr.TypedValue, error) {
|
||||
var (
|
||||
vv = res.Values.FilterByName(k)
|
||||
|
||||
multiValueField bool
|
||||
field *types.ModuleField
|
||||
)
|
||||
|
||||
if mod := res.GetModule(); mod != nil {
|
||||
field = mod.Fields.FindByName(k)
|
||||
|
||||
if field == nil {
|
||||
return nil, fmt.Errorf("field '%s' does not exist on module %s", k, mod.Name)
|
||||
}
|
||||
|
||||
multiValueField = field.Multi
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(vv) == 0:
|
||||
return nil, nil
|
||||
case len(vv) == 1:
|
||||
return expr.NewString(vv[0].Value)
|
||||
default:
|
||||
mval := make([]expr.TypedValue, 0, len(vv))
|
||||
_ = vv.Walk(func(v *types.RecordValue) error {
|
||||
mval = append(mval, expr.Must(expr.NewString(v.Value)))
|
||||
return nil
|
||||
})
|
||||
if field != nil && field.IsBoolean() {
|
||||
return &expr.Boolean{}, nil
|
||||
}
|
||||
|
||||
return expr.NewArray(mval)
|
||||
return nil, nil
|
||||
case len(vv) == 1 && !multiValueField:
|
||||
return recordValueToExprTypedValue(field, vv[0])
|
||||
default:
|
||||
return recordValueSetoToExprArray(field, vv...)
|
||||
}
|
||||
}
|
||||
|
||||
// assignToRecordValuesSet is field value setter for *types.Record
|
||||
func assignToComposeRecordValues(res *types.RecordValueSet, pp []string, val interface{}) (err error) {
|
||||
// assignToRecordValuesSet is field value setter for *types.RecordValueSet
|
||||
//
|
||||
// We'll be using types.Record for the base (and not types.RecordValueSet)
|
||||
func assignToComposeRecordValues(res *types.Record, pp []string, val interface{}) (err error) {
|
||||
if len(pp) < 1 {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case types.RecordValueSet:
|
||||
*res = val
|
||||
res.Values = val
|
||||
return
|
||||
case *types.Record:
|
||||
*res = val.Values
|
||||
*res = *val
|
||||
return
|
||||
}
|
||||
|
||||
@ -273,7 +331,7 @@ func assignToComposeRecordValues(res *types.RecordValueSet, pp []string, val int
|
||||
return err
|
||||
}
|
||||
|
||||
*res = res.Set(rv)
|
||||
res.Values = res.Values.Set(rv)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -317,11 +375,51 @@ func assignToComposeRecordValues(res *types.RecordValueSet, pp []string, val int
|
||||
}
|
||||
}
|
||||
|
||||
*res = res.Set(rv)
|
||||
res.Values = res.Values.Set(rv)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewComposeRecordValues creates new instance of ComposeRecordValues expression type
|
||||
func NewComposeRecordValues(val interface{}) (*ComposeRecordValues, error) {
|
||||
// Try to cast to ComposeRecord first
|
||||
if rec, err := CastToComposeRecord(val); err == nil {
|
||||
return &ComposeRecordValues{value: rec}, nil
|
||||
}
|
||||
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeRecordValues: %w", err)
|
||||
} else {
|
||||
return &ComposeRecordValues{value: &types.Record{Values: c}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeRecordValues
|
||||
func (t ComposeRecordValues) Get() interface{} { return t.value }
|
||||
|
||||
// Return underlying value on ComposeRecordValues
|
||||
func (t ComposeRecordValues) GetValue() types.RecordValueSet { return t.value.Values }
|
||||
|
||||
// Return type name
|
||||
func (ComposeRecordValues) Type() string { return "ComposeRecordValues" }
|
||||
|
||||
// Convert value to types.RecordValueSet
|
||||
func (ComposeRecordValues) Cast(val interface{}) (expr.TypedValue, error) {
|
||||
return NewComposeRecordValues(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeRecordValues
|
||||
//
|
||||
// value is first passed through CastToComposeRecordValues
|
||||
func (t *ComposeRecordValues) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value.Values = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func CastToComposeRecordValueErrorSet(val interface{}) (out *types.RecordValueErrorSet, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.RecordValueErrorSet:
|
||||
@ -330,3 +428,80 @@ func CastToComposeRecordValueErrorSet(val interface{}) (out *types.RecordValueEr
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
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.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)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func recordValueSetoToExprArray(field *types.ModuleField, vv ...*types.RecordValue) (arr *expr.Array, err error) {
|
||||
var (
|
||||
tv expr.TypedValue
|
||||
)
|
||||
|
||||
arr = &expr.Array{}
|
||||
|
||||
for _, v := range vv {
|
||||
tv, err = recordValueToExprTypedValue(field, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
arr.Push(tv)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -40,11 +40,13 @@ types:
|
||||
- { name: 'updatedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
customGValSelector: true
|
||||
customSelector: true
|
||||
customGValSelector: true
|
||||
customFieldAssigner: true
|
||||
|
||||
ComposeRecordValues:
|
||||
as: 'types.RecordValueSet'
|
||||
customType: true
|
||||
as: 'types.RecordValueSet'
|
||||
|
||||
ComposeRecordValueErrorSet:
|
||||
as: '*types.RecordValueErrorSet'
|
||||
|
||||
@ -13,13 +13,13 @@ func TestSetRecordValuesWithPath(t *testing.T) {
|
||||
|
||||
var (
|
||||
r = require.New(t)
|
||||
rvs = &ComposeRecordValues{types.RecordValueSet{}}
|
||||
rvs = &ComposeRecordValues{&types.Record{}}
|
||||
)
|
||||
|
||||
r.NoError(expr.Assign(rvs, "field1", expr.Must(expr.NewString("a"))))
|
||||
r.NoError(expr.Assign(rvs, "field1.1", expr.Must(expr.NewString("a"))))
|
||||
r.True(rvs.value.Has("field1", 0))
|
||||
r.True(rvs.value.Has("field1", 1))
|
||||
r.True(rvs.value.Values.Has("field1", 0))
|
||||
r.True(rvs.value.Values.Has("field1", 1))
|
||||
})
|
||||
|
||||
t.Run("cast string map", func(t *testing.T) {
|
||||
@ -56,14 +56,17 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
&types.ModuleField{Name: "m1", Multi: true},
|
||||
&types.ModuleField{Name: "m2", Multi: true},
|
||||
&types.ModuleField{Name: "s2", Multi: false},
|
||||
&types.ModuleField{Name: "b0", Multi: false, Kind: "Bool"},
|
||||
&types.ModuleField{Name: "b1", Multi: false, Kind: "Bool"},
|
||||
}}
|
||||
|
||||
raw = &types.Record{Values: types.RecordValueSet{
|
||||
&types.RecordValue{Name: "s1", Value: "sVal1"},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.0"},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.0", Place: 0},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.1", Place: 1},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.2", Place: 2},
|
||||
&types.RecordValue{Name: "m2", Value: "mVal2.0"},
|
||||
&types.RecordValue{Name: "m2", Value: "mVal2.0", Place: 0},
|
||||
&types.RecordValue{Name: "b1", Value: "1", Place: 0},
|
||||
}}
|
||||
|
||||
tval = &ComposeRecord{value: raw}
|
||||
@ -74,61 +77,70 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
raw.SetModule(mod)
|
||||
|
||||
t.Run("via typed value", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
tcc := []struct {
|
||||
expects interface{}
|
||||
path string
|
||||
}{
|
||||
{"sVal1", "rec.values.s1"},
|
||||
{"mVal1.0", "rec.values.m1.0"},
|
||||
{"mVal1.1", "rec.values.m1.1"},
|
||||
{"mVal2.0", "rec.values.m2.0"},
|
||||
// expecting valid value (false) even when boolean fields are not set
|
||||
{false, "rec.values.b0"},
|
||||
{true, "rec.values.b1"},
|
||||
}
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.s1")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("sVal1", v.Get())
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.path, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.m1.0")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("mVal1.0", v.Get())
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.m1.1")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("mVal1.1", v.Get())
|
||||
|
||||
// @todo when RecordValueSet supports back-ref to record,
|
||||
// we can employ better field access:
|
||||
// - no error on missing values when field exists
|
||||
// - proper handling of multi-value field values
|
||||
// - proper value-types that corelate to field types
|
||||
//v, err = expr.Select(scope, "rec.values.m2.0")
|
||||
//req.NoError(err)
|
||||
//req.NotEmpty(v)
|
||||
//req.Equal("mVal2.0", v.Get())
|
||||
v, err = expr.Select(scope, tc.path)
|
||||
req.NoError(err)
|
||||
req.Equal(tc.expects, v.Get())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("via gval selector", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = expr.NewParser()
|
||||
)
|
||||
tcc := []struct {
|
||||
test bool
|
||||
expr string
|
||||
}{
|
||||
{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`},
|
||||
{true, `rec.values.s2 != "foo"`},
|
||||
{false, `rec.values.s2 == "foo"`},
|
||||
{true, `rec.values.m1[0] == "mVal1.0"`},
|
||||
{true, `rec.values.m1[1] == "mVal1.1"`},
|
||||
{true, `rec.values.m2[0] == "mVal2.0"`},
|
||||
{true, `!rec.values.b0`},
|
||||
{false, `rec.values.b0`},
|
||||
{true, `rec.values.b1`},
|
||||
{false, `!rec.values.b1`},
|
||||
}
|
||||
|
||||
eval, err := parser.Parse(`rec.values.s1 == "sVal1"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
for _, tc := range tcc {
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = expr.NewParser()
|
||||
)
|
||||
|
||||
eval, err = parser.Parse(`rec.values.s1 != "foo"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
t.Run(tc.expr, func(t *testing.T) {
|
||||
eval, err := parser.Parse(tc.expr)
|
||||
req.NoError(err)
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m1[0] == "mVal1.0"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m1[1] == "mVal1.1"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m2[0] == "mVal2.0"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
test, err := eval.Test(context.Background(), scope)
|
||||
req.NoError(err)
|
||||
req.Equal(tc.test, test)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -136,63 +148,63 @@ func TestAssignToComposeRecordValues(t *testing.T) {
|
||||
t.Run("assign simple", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
target = types.RecordValueSet{}
|
||||
target = &types.Record{Values: types.RecordValueSet{}}
|
||||
)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, []string{"a"}, "b"))
|
||||
req.Len(target, 1)
|
||||
req.True(target.Has("a", 0))
|
||||
req.NoError(assignToComposeRecordValues(&target, []string{"a", "1"}, "b"))
|
||||
req.Len(target, 2)
|
||||
req.True(target.Has("a", 0))
|
||||
req.True(target.Has("a", 1))
|
||||
req.NoError(assignToComposeRecordValues(target, []string{"a"}, "b"))
|
||||
req.Len(target.Values, 1)
|
||||
req.True(target.Values.Has("a", 0))
|
||||
req.NoError(assignToComposeRecordValues(target, []string{"a", "1"}, "b"))
|
||||
req.Len(target.Values, 2)
|
||||
req.True(target.Values.Has("a", 0))
|
||||
req.True(target.Values.Has("a", 1))
|
||||
})
|
||||
|
||||
t.Run("assign rvs", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
target = types.RecordValueSet{}
|
||||
target = &types.Record{Values: types.RecordValueSet{}}
|
||||
)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, nil, types.RecordValueSet{{}}))
|
||||
req.Len(target, 1)
|
||||
req.NoError(assignToComposeRecordValues(target, nil, types.RecordValueSet{{}}))
|
||||
req.Len(target.Values, 1)
|
||||
})
|
||||
|
||||
t.Run("assign record", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
target = types.RecordValueSet{}
|
||||
target = &types.Record{Values: types.RecordValueSet{}}
|
||||
)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, nil, &types.Record{Values: types.RecordValueSet{{}}}))
|
||||
req.Len(target, 1)
|
||||
req.NoError(assignToComposeRecordValues(target, nil, &types.Record{Values: types.RecordValueSet{{}}}))
|
||||
req.Len(target.Values, 1)
|
||||
})
|
||||
|
||||
t.Run("overwrite rvs", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
target = types.RecordValueSet{{Name: "a"}}
|
||||
target = &types.Record{Values: types.RecordValueSet{{Name: "a"}}}
|
||||
)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, nil, types.RecordValueSet{{Name: "b"}}))
|
||||
req.Len(target, 1)
|
||||
req.False(target.Has("a", 0))
|
||||
req.True(target.Has("b", 0))
|
||||
req.NoError(assignToComposeRecordValues(target, nil, types.RecordValueSet{{Name: "b"}}))
|
||||
req.Len(target.Values, 1)
|
||||
req.False(target.Values.Has("a", 0))
|
||||
req.True(target.Values.Has("b", 0))
|
||||
})
|
||||
|
||||
t.Run("assign multiple values", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
target = types.RecordValueSet{}
|
||||
target = &types.Record{Values: types.RecordValueSet{}}
|
||||
)
|
||||
|
||||
req.Error(assignToComposeRecordValues(&target, []string{"a", "2"}, expr.Must(expr.NewAny([]interface{}{"1", "2"}))))
|
||||
req.Len(target, 0)
|
||||
req.Error(assignToComposeRecordValues(target, []string{"a", "2"}, expr.Must(expr.NewAny([]interface{}{"1", "2"}))))
|
||||
req.Len(target.Values, 0)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, []string{"a"}, expr.Must(expr.NewAny([]interface{}{"1", "2"}))))
|
||||
req.Len(target, 2)
|
||||
req.NoError(assignToComposeRecordValues(target, []string{"a"}, expr.Must(expr.NewAny([]interface{}{"1", "2"}))))
|
||||
req.Len(target.Values, 2)
|
||||
|
||||
req.NoError(assignToComposeRecordValues(&target, []string{"a"}, expr.Must(expr.NewAny([]string{"1", "2"}))))
|
||||
req.Len(target, 2)
|
||||
req.NoError(assignToComposeRecordValues(target, []string{"a"}, expr.Must(expr.NewAny([]string{"1", "2"}))))
|
||||
req.Len(target.Values, 2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ var _ = context.Background
|
||||
var _ = fmt.Errorf
|
||||
|
||||
{{ range $exprType, $def := .Types }}
|
||||
{{ if not $def.CustomType }}
|
||||
// {{ $exprType }} is an expression type, wrapper for {{ $def.As }} type
|
||||
type {{ $exprType }} struct{ value {{ $def.As }} }
|
||||
|
||||
@ -81,12 +82,14 @@ func (t {{ $exprType }}) SelectGVal(ctx context.Context, k string) (interface{},
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not $def.CustomSelector }}
|
||||
// Select is field accessor for {{ $def.As }}
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t {{ $exprType }}) Select(k string) (TypedValue, error) {
|
||||
return {{ unexport $exprType "TypedValueSelector" }}(t.value, k)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (t {{ $exprType }}) Has(k string) bool {
|
||||
switch k {
|
||||
@ -150,7 +153,7 @@ func {{ $def.AssignerFn }}(res {{ $def.As }}, k string, val interface{}) (error)
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }} {{/* if $def.Struct */}}
|
||||
{{ end }} {{/* if not $def.CustomType */}}
|
||||
{{ end }} {{/* types loop */}}
|
||||
|
||||
|
||||
@ -28,7 +28,9 @@ type (
|
||||
AssignerFn string `yaml:"assignerFn"`
|
||||
BuiltInCastFn bool
|
||||
BuiltInAssignerFn bool
|
||||
CustomType bool `yaml:"customType"`
|
||||
CustomGValSelector bool `yaml:"customGValSelector"`
|
||||
CustomSelector bool `yaml:"customSelector"`
|
||||
CustomFieldAssigner bool `yaml:"customFieldAssigner"`
|
||||
Struct []*exprTypeStructDef
|
||||
|
||||
|
||||
@ -213,6 +213,11 @@ func (t Array) Has(k string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Push appends value to array
|
||||
func (t *Array) Push(v TypedValue) {
|
||||
t.value = append(t.value, v)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Array
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
|
||||
@ -3,7 +3,6 @@ package expr
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/PaesslerAG/gval"
|
||||
)
|
||||
|
||||
@ -71,7 +70,12 @@ func (e *gvalEval) Eval(ctx context.Context, scope *Vars) (interface{}, error) {
|
||||
}
|
||||
|
||||
func (e *gvalEval) Test(ctx context.Context, scope *Vars) (bool, error) {
|
||||
return e.evaluable.EvalBool(ctx, scope.Dict())
|
||||
r, err := e.evaluable(ctx, scope.Dict())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return !isEmpty(r), nil
|
||||
}
|
||||
|
||||
func Parser(ll ...gval.Language) gval.Language {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user