3
0

Fixed array & type handlign for gval&expr

This commit is contained in:
Denis Arh
2021-03-27 11:56:23 +01:00
parent 5a159838ba
commit 4a58d9c7cb
10 changed files with 598 additions and 331 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ func (i *collectionIterator) Next(context.Context, *Vars) (out *Vars, err error)
case TypedValue:
item = c
default:
if item, err = NewAny(c); err != nil {
if item, err = Cast(c); err != nil {
return
}
}
+16 -2
View File
@@ -157,8 +157,22 @@ func (set ExprSet) Eval(ctx context.Context, in *expr.Vars) (*expr.Vars, error)
typedValue, is := value.(expr.TypedValue)
if !is {
if e.typ == nil {
typedValue, _ = expr.NewAny(value)
// value to be assigned (evaled, copied..) is not typed!
// try to figure out what we can do
if !knownType(e.typ) {
// Expression does not have type set
if out.Has(e.Target) {
t, _ := out.Select(e.Target)
typedValue, err = t.Cast(value)
if err != nil {
return nil, fmt.Errorf("cannot cast value %T to %s: %w", value, e.typ.Type(), err)
}
} else {
typedValue, err = expr.Cast(value)
if err != nil {
return nil, fmt.Errorf("cannot cast value %T to %s: %w", value, e.typ.Type(), err)
}
}
} else if typedValue, err = e.typ.Cast(value); err != nil {
return nil, fmt.Errorf("cannot cast value %T to %s (target %s)", value, e.typ.Type(), e.Target)
}
+37 -2
View File
@@ -26,13 +26,13 @@ func TestExprSet_Eval(t *testing.T) {
{
name: "constant assignment",
set: ExprSet{&Expr{Target: "foo", Expr: `"bar"`}},
output: RVars{"foo": Must(NewAny("bar"))},
output: RVars{"foo": Must(NewString("bar"))},
},
{
name: "vars with path",
set: ExprSet{&Expr{Target: "l1.l2", Expr: `"bar"`}},
input: RVars{"l1": RVars{}.Vars()},
output: RVars{"l1": RVars{"l2": Must(NewAny("bar"))}.Vars()},
output: RVars{"l1": RVars{"l2": Must(Cast("bar"))}.Vars()},
},
{
name: "copy vars with same types",
@@ -87,6 +87,41 @@ func TestExprSet_Eval(t *testing.T) {
})),
},
},
{
name: "slice push",
set: ExprSet{
&Expr{Target: "arr", typ: &Array{}, Expr: `[]`},
&Expr{Target: "arr", typ: &Array{}, Expr: `push(arr, "foo")`},
},
output: RVars{
"arr": Must(NewArray([]string{
"foo",
})),
},
},
{
name: "slice push w/o type",
set: ExprSet{
&Expr{Target: "arr", typ: &Array{}, Expr: `[]`},
&Expr{Target: "arr", typ: &Any{}, Expr: `push(arr, "foo")`},
},
output: RVars{
"arr": Must(NewArray([]string{
"foo",
})),
},
},
{
name: "slice create & push w/o type",
set: ExprSet{
&Expr{Target: "arr", typ: &Array{}, Expr: `push([], "foo")`},
},
output: RVars{
"arr": Must(NewArray([]TypedValue{
Must(Cast("foo")),
})),
},
},
}
)
+89 -14
View File
@@ -5,17 +5,15 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/handle"
"github.com/spf13/cast"
"io"
"net/http"
"net/url"
"reflect"
"strings"
"time"
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/handle"
"github.com/spf13/cast"
)
type (
@@ -29,6 +27,64 @@ 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) {
var is bool
if tv, is = in.(TypedValue); is {
return
}
switch c := in.(type) {
// @todo
//case map[string]interface{}:
// return NewVars()
case []TypedValue:
return &Array{value: c}, nil
case bool:
return &Boolean{value: c}, nil
case uint8:
return &UnsignedInteger{value: uint64(c)}, nil
case uint16:
return &UnsignedInteger{value: uint64(c)}, nil
case uint32:
return &UnsignedInteger{value: uint64(c)}, nil
case uint64:
return &UnsignedInteger{value: c}, nil
case int8:
return &Integer{value: int64(c)}, nil
case int16:
return &Integer{value: int64(c)}, nil
case int32:
return &Integer{value: int64(c)}, nil
case int64:
return &Integer{value: c}, nil
case float32:
return &Float{value: float64(c)}, nil
case float64:
return &Float{value: c}, nil
case string:
return &String{value: c}, nil
case []byte:
return &String{value: string(c)}, nil
case *time.Time:
return &DateTime{value: c}, nil
case time.Time:
return &DateTime{value: &c}, nil
case *time.Duration:
return &Duration{value: *c}, nil
case time.Duration:
return &Duration{value: c}, nil
case map[string]string:
return &KV{value: c}, nil
case map[string][]string:
return &KVV{value: c}, nil
case io.Reader, io.ReadCloser, io.ReadSeeker, io.ReadSeekCloser, io.ReadWriteSeeker:
return &Reader{value: c.(io.Reader)}, nil
default:
return &Any{value: c}, nil
}
}
// Unresolved is a special type that holds value + type it needs to be resolved to
//
// This solves problem with typed value serialization
@@ -64,25 +120,27 @@ func CastToAny(val interface{}) (interface{}, error) {
return val, nil
}
func CastToArray(val interface{}) ([]TypedValue, error) {
func CastToArray(val interface{}) (out []TypedValue, err error) {
switch val := val.(type) {
case nil:
return make([]TypedValue, 0), nil
case *Array:
return val.value, nil
}
ref := reflect.ValueOf(val)
if ref.Kind() == reflect.Slice {
out := make([]TypedValue, ref.Len())
out = make([]TypedValue, ref.Len())
for i := 0; i < ref.Len(); i++ {
item := ref.Index(i).Interface()
if tVal, is := item.(TypedValue); is {
out[i] = tVal
} else {
out[i] = &Any{value: item}
out[i], err = Cast(item)
if err != nil {
return
}
}
return out, nil
return
}
return nil, fmt.Errorf("unable to cast %T to []TypedValue", val)
@@ -90,6 +148,24 @@ func CastToArray(val interface{}) ([]TypedValue, error) {
var _ TypeValueDecoder = &Array{}
func (t Array) MarshalJSON() ([]byte, error) {
var (
aux = make([]*typedValueWrap, len(t.value))
)
for i, v := range t.value {
aux[i] = &typedValueWrap{Type: v.Type()}
if _, is := v.(json.Marshaler); is {
aux[i].Value = v
} else {
aux[i].Value = v.Get()
}
}
return json.Marshal(aux)
}
func (t *Array) Decode(dst reflect.Value) error {
if dst.Kind() != reflect.Slice {
return fmt.Errorf("failed to decode Array to non-slice")
@@ -136,7 +212,7 @@ func (t Array) Has(k string) bool {
}
}
// Select is field accessor for *types.Record
// Select is field accessor for *types.Array
//
// Similar to SelectGVal but returns typed values
func (t Array) Select(k string) (TypedValue, error) {
@@ -261,7 +337,6 @@ func assignToKVV(t *KVV, key string, val TypedValue) error {
t.value = make(map[string][]string)
}
spew.Dump(val)
str, err := cast.ToStringSliceE(val.Get())
t.value[key] = str
return err
+90 -34
View File
@@ -1,9 +1,9 @@
package expr
import (
"reflect"
"fmt"
"github.com/PaesslerAG/gval"
"reflect"
)
func ArrayFunctions() []gval.Language {
@@ -18,24 +18,55 @@ func ArrayFunctions() []gval.Language {
}
// push adds a value to the end of slice, returns copy
func push(arr interface{}, p interface{}) (interface{}, error) {
if !isSlice(arr) {
return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()}
func push(arr interface{}, nn ...interface{}) (out interface{}, err error) {
if arr == nil {
// If base is empty, return pushed items directly
return nn, nil
} else if i, is := arr.([]interface{}); is {
// Simple append if we're dealing with []interface{} base
return append(i, nn...), nil
} else if arr, err = toSlice(arr); err != nil {
return
}
c := reflect.ValueOf(arr)
if stv, is := arr.([]TypedValue); is {
// slice of typed values, this will make things easier
for _, n := range nn {
if tv, is := n.(TypedValue); is {
stv = append(stv, tv)
} else {
// wrap unknown types...
stv = append(stv, Must(Cast(n)))
}
}
return stv, nil
}
var (
c = reflect.ValueOf(arr)
nval = reflect.MakeSlice(c.Type(), c.Len()+len(nn), c.Cap()+len(nn))
)
nval := reflect.MakeSlice(c.Type(), c.Len()+1, c.Cap()+1)
reflect.Copy(nval, c)
nval.Index(c.Len()).Set(reflect.ValueOf(p))
for i, n := range nn {
nt := reflect.ValueOf(n).Type()
it := nval.Index(c.Len() + i).Type()
if nt != it {
return nil, fmt.Errorf("can not push %v to %v slice", nt, it)
}
nval.Index(c.Len() + i).Set(reflect.ValueOf(n))
}
return nval.Interface(), nil
}
// pop takes the last value in slice, does not modify original
func pop(arr interface{}) (interface{}, error) {
if !isSlice(arr) {
return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()}
func pop(arr interface{}) (out interface{}, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
c := reflect.ValueOf(arr)
@@ -48,9 +79,9 @@ func pop(arr interface{}) (interface{}, error) {
}
// shifts takes the first value in slice, does not modify original
func shift(arr interface{}) (interface{}, error) {
if !isSlice(arr) {
return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()}
func shift(arr interface{}) (out interface{}, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
c := reflect.ValueOf(arr)
@@ -62,51 +93,76 @@ func shift(arr interface{}) (interface{}, error) {
return c.Index(0).Interface(), nil
}
// cound gets the count of occurences in the slice
func count(arr interface{}, v ...interface{}) int {
if !isSlice(arr) {
return 0
// count gets the count of occurrences in the slice
func count(arr interface{}, v ...interface{}) (count int, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
count := 0
var (
occ int
)
for _, vv := range v {
if find(arr, vv) != -1 {
if occ, err = find(arr, vv); err != nil {
return 0, err
} else if occ != -1 {
count++
}
}
return count
return
}
// has finds any occurence of the values in slice
func has(arr interface{}, v ...interface{}) bool {
return count(arr, v...) > 0
// has finds any occurrence of the values in slice
func has(arr interface{}, v ...interface{}) (b bool, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
var c int
if c, err = count(arr, v...); err != nil {
return
}
return c > 0, nil
}
// hasAll finds all the occurences in the slice
func hasAll(arr interface{}, v ...interface{}) bool { return count(arr, v...) == len(v) }
// hasAll finds all the occurrences in the slice
func hasAll(arr interface{}, v ...interface{}) (b bool, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
var c int
if c, err = count(arr, v...); err != nil {
return
}
return c == len(v), nil
}
// find takes a value and gets the position in slice
// if no results, returns -1
func find(arr interface{}, v interface{}) int {
if !isSlice(arr) {
return 0
func find(arr interface{}, v interface{}) (p int, err error) {
if arr, err = toSlice(arr); err != nil {
return
}
for i := 0; i < reflect.ValueOf(arr).Len(); i++ {
for p = 0; p < reflect.ValueOf(arr).Len(); p++ {
c := reflect.ValueOf(arr)
if c.Index(i).Interface() == v {
return i
if c.Index(p).Interface() == v {
return
}
}
return -1
return -1, nil
}
// slice slices slices
func slice(arr interface{}, start, end int) interface{} {
arr = UntypedValue(arr)
v := reflect.ValueOf(arr)
if start >= v.Len() {
+316 -267
View File
@@ -1,7 +1,7 @@
package expr
import (
"reflect"
"fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -24,14 +24,6 @@ var (
}
)
type (
fac struct {
expect interface{}
arr interface{}
val interface{}
}
)
func Example_push_int() {
eval(`push(intArr, intVal)`, vals)
@@ -81,298 +73,355 @@ func Example_pop_float() {
// 69.42
}
func Test_push(t *testing.T) {
tcc := []struct {
base interface{}
new []interface{}
expect interface{}
expError bool
}{
{
base: []string{"1", "2", "3"},
new: []interface{}{"4"},
expect: []string{"1", "2", "3", "4"},
},
{
base: Must(NewArray(nil)),
new: []interface{}{"foo"},
expect: []TypedValue{Must(NewString("foo"))},
},
{
base: Must(NewArray([]interface{}{"foo"})),
new: []interface{}{"bar"},
expect: []TypedValue{Must(NewString("foo")), Must(NewString("bar"))},
},
{
base: Must(NewArray([]interface{}{Must(NewString("foo"))})),
new: []interface{}{"bar"},
expect: []TypedValue{Must(NewString("foo")), Must(NewString("bar"))},
},
{
base: Must(NewArray([]interface{}{Must(NewString("foo"))})),
new: []interface{}{Must(NewString("bar"))},
expect: []TypedValue{Must(NewString("foo")), Must(NewString("bar"))},
},
{
base: []string{"1", "2", "3"},
new: []interface{}{4},
expError: true,
},
}
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
out, err = push(tc.base, tc.new...)
)
if tc.expError {
req.Error(err)
return
}
req.NoError(err)
req.Equal(tc.expect, out)
})
}
}
func Test_shift(t *testing.T) {
var (
req = require.New(t)
tcc := []struct {
value interface{}
expect interface{}
expError bool
}{
{
value: []string{"1", "2", "3"},
expect: "1",
},
{
value: map[string]string{"test": "123"},
expect: nil,
expError: true,
},
{
value: []int{4, 5, 6, 7},
expect: 4,
},
{
value: []int{},
expect: nil,
},
{
value: int(1),
expect: nil,
expError: true,
},
{
value: []float64{11.1},
expect: 11.1,
},
}
tcc = []tc{
{
value: []string{"1", "2", "3"},
expect: "1",
},
{
value: map[string]string{"test": "123"},
expect: nil,
},
{
value: []int{4, 5, 6, 7},
expect: 4,
},
{
value: []int{},
expect: nil,
},
{
value: int(1),
expect: nil,
},
{
value: []float64{11.1},
expect: 11.1,
},
}
)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
val, err = shift(tc.value)
)
for _, tst := range tcc {
val, _ := shift(tst.value)
if tc.expError {
req.Error(err)
return
}
req.Equal(tst.expect, val)
req.NoError(err)
req.Equal(tc.expect, val)
})
}
}
func Test_find(t *testing.T) {
var (
req = require.New(t)
tcc := []struct {
expect interface{}
arr interface{}
val interface{}
}{
{
arr: []string{"1", "2", "3"},
val: "3",
expect: 2,
},
{
arr: []bool{true, false, true},
val: true,
expect: 0,
},
{
arr: []int{4, 5, 6, 7},
val: 7,
expect: 3,
},
{
arr: []int{},
val: 0,
expect: -1,
},
{
arr: []float64{11.1, 12.4},
val: 11.1,
expect: 0,
},
{
arr: []float64{11.1, 12.4},
val: 11.2,
expect: -1,
},
}
tcc = []fac{
{
arr: []string{"1", "2", "3"},
val: "3",
expect: 2,
},
{
arr: []bool{true, false, true},
val: true,
expect: 0,
},
{
arr: []int{4, 5, 6, 7},
val: 7,
expect: 3,
},
{
arr: []int{},
val: 0,
expect: -1,
},
{
arr: []float64{11.1, 12.4},
val: 11.1,
expect: 0,
},
{
arr: []float64{11.1, 12.4},
val: 11.2,
expect: -1,
},
}
)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
loc, err = find(tc.arr, tc.val)
)
for _, tst := range tcc {
loc := find(tst.arr, tst.val)
req.Equal(tst.expect, loc)
req.NoError(err)
req.Equal(tc.expect, loc)
})
}
}
func Test_count(t *testing.T) {
var (
req = require.New(t)
tcc := []struct {
expect interface{}
arr interface{}
val []interface{}
}{
{
arr: []string{"1", "2", "3"},
val: []interface{}{"0", "3"},
expect: 1,
},
{
arr: []bool{true, true},
val: []interface{}{false, false},
expect: 0,
},
{
arr: []bool{true, true},
val: []interface{}{false, true},
expect: 1,
},
{
arr: []int{4, 5, 6, 7},
val: []interface{}{7, 4},
expect: 2,
},
{
arr: []float64{11.1, 12.4},
val: []interface{}{0.1, 1.1},
expect: 0,
},
}
tcc = []fac{
{
arr: []string{"1", "2", "3"},
val: []string{"0", "3"},
expect: 1,
},
{
arr: []bool{true, true},
val: []bool{false, false},
expect: 0,
},
{
arr: []bool{true, true},
val: []bool{false, true},
expect: 1,
},
{
arr: []int{4, 5, 6, 7},
val: []int{7, 4},
expect: 2,
},
{
arr: []float64{11.1, 12.4},
val: []float64{0.1, 1.1},
expect: 0,
},
}
)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
err error
loc int
)
for _, tst := range tcc {
var loc int
switch reflect.TypeOf(tst.val).Elem().Kind() {
case reflect.String:
loc = count(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1])
break
case reflect.Bool:
loc = count(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1])
break
case reflect.Int:
loc = count(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1])
break
case reflect.Float64:
loc = count(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1])
break
}
req.Equal(tst.expect, loc)
loc, err = count(tc.arr, tc.val...)
req.NoError(err)
req.Equal(tc.expect, loc)
})
}
}
func Test_has(t *testing.T) {
var (
req = require.New(t)
tcc := []struct {
expect interface{}
arr interface{}
val []interface{}
}{
{
arr: []string{"1", "2", "3"},
val: []interface{}{"0", "3"},
expect: true,
},
{
arr: []bool{true, true},
val: []interface{}{false, false},
expect: false,
},
{
arr: []bool{true, true},
val: []interface{}{false, true},
expect: true,
},
{
arr: []int{4, 5, 6, 7},
val: []interface{}{7, 4},
expect: true,
},
{
arr: []float64{11.1, 12.4},
val: []interface{}{0.1, 1.1},
expect: false,
},
}
tcc = []fac{
{
arr: []string{"1", "2", "3"},
val: []string{"0", "3"},
expect: true,
},
{
arr: []bool{true, true},
val: []bool{false, false},
expect: false,
},
{
arr: []bool{true, true},
val: []bool{false, true},
expect: true,
},
{
arr: []int{4, 5, 6, 7},
val: []int{7, 4},
expect: true,
},
{
arr: []float64{11.1, 12.4},
val: []float64{0.1, 1.1},
expect: false,
},
}
)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
loc bool
err error
)
for _, tst := range tcc {
var loc bool
switch reflect.TypeOf(tst.val).Elem().Kind() {
case reflect.String:
loc = has(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1])
break
case reflect.Bool:
loc = has(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1])
break
case reflect.Int:
loc = has(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1])
break
case reflect.Float64:
loc = has(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1])
break
}
req.Equal(tst.expect, loc)
loc, err = has(tc.arr, tc.val...)
req.NoError(err)
req.Equal(tc.expect, loc)
})
}
}
func Test_hasAll(t *testing.T) {
var (
req = require.New(t)
tcc := []struct {
arr interface{}
val []interface{}
hasAll bool
expError bool
}{
{
arr: []string{"1", "2", "3"},
val: []interface{}{"0", "3"},
hasAll: false,
},
{
arr: []bool{true, true},
val: []interface{}{false, false},
hasAll: false,
},
{
arr: []bool{true, true},
val: []interface{}{false, true},
hasAll: false,
},
{
arr: []int{4, 5, 6, 7},
val: []interface{}{7, 4},
hasAll: true,
},
{
arr: []float64{11.1, 12.4},
val: []interface{}{0.1, 1.1},
hasAll: false,
},
}
tcc = []fac{
{
arr: []string{"1", "2", "3"},
val: []string{"0", "3"},
expect: false,
},
{
arr: []bool{true, true},
val: []bool{false, false},
expect: false,
},
{
arr: []bool{true, true},
val: []bool{false, true},
expect: false,
},
{
arr: []int{4, 5, 6, 7},
val: []int{7, 4},
expect: true,
},
{
arr: []float64{11.1, 12.4},
val: []float64{0.1, 1.1},
expect: false,
},
}
)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
rval, err = hasAll(tc.arr, tc.val...)
)
for _, tst := range tcc {
var loc bool
if tc.expError {
req.Error(err)
return
}
switch reflect.TypeOf(tst.arr).Elem().Kind() {
case reflect.String:
loc = hasAll(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1])
break
case reflect.Bool:
loc = hasAll(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1])
break
case reflect.Int:
loc = hasAll(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1])
break
case reflect.Float64:
loc = hasAll(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1])
break
}
req.Equal(tst.expect, loc)
req.NoError(err)
req.Equal(tc.hasAll, rval)
})
}
}
func Test_slice(t *testing.T) {
type (
sct struct {
vals []int
arr interface{}
expect interface{}
}
)
tcc := []struct {
vals []int
arr interface{}
expect interface{}
}{
{
vals: []int{0, 3},
arr: []string{"1", "2", "3"},
expect: []string{"1", "2", "3"},
},
{
vals: []int{0, 1},
arr: []string{"1", "2", "3"},
expect: []string{"1"},
},
{
vals: []int{2, 3},
arr: []bool{true, true},
expect: []bool{true, true},
},
{
vals: []int{1, -1},
arr: []int{4, 5, 6, 7},
expect: []int{5, 6, 7},
},
{
vals: []int{3, -1},
arr: []float64{11.1, 12.4},
expect: []float64{11.1, 12.4},
},
}
var (
req = require.New(t)
for p, tc := range tcc {
t.Run(fmt.Sprintf("%d", p), func(t *testing.T) {
var (
req = require.New(t)
ss = slice(tc.arr, tc.vals[0], tc.vals[1])
)
tcc = []sct{
{
vals: []int{0, 3},
arr: []string{"1", "2", "3"},
expect: []string{"1", "2", "3"},
},
{
vals: []int{0, 1},
arr: []string{"1", "2", "3"},
expect: []string{"1"},
},
{
vals: []int{2, 3},
arr: []bool{true, true},
expect: []bool{true, true},
},
{
vals: []int{1, -1},
arr: []int{4, 5, 6, 7},
expect: []int{5, 6, 7},
},
{
vals: []int{3, -1},
arr: []float64{11.1, 12.4},
expect: []float64{11.1, 12.4},
},
}
)
for _, tst := range tcc {
new := slice(tst.arr, tst.vals[0], tst.vals[1])
req.Equal(tst.expect, new)
req.Equal(tc.expect, ss)
})
}
}
+16 -1
View File
@@ -1,6 +1,7 @@
package expr
import (
"fmt"
"reflect"
"github.com/PaesslerAG/gval"
@@ -88,5 +89,19 @@ func getKind(v interface{}) reflect.Kind {
}
func isSlice(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Slice
return reflect.TypeOf(v).Kind() == reflect.Slice || reflect.TypeOf(v).Kind() == reflect.Array
}
// toArray removes expr types (if wrapped) and checks if the variable is slice
// internal only
//
//
func toSlice(vv interface{}) (interface{}, error) {
vv = UntypedValue(vv)
if !isSlice(vv) {
return nil, fmt.Errorf("unexpected type: %T, expecting slice", vv)
}
return vv, nil
}
+4 -8
View File
@@ -6,13 +6,6 @@ import (
"github.com/stretchr/testify/require"
)
type (
tc struct {
value interface{}
expect interface{}
}
)
func Test_empty(t *testing.T) {
var (
req = require.New(t)
@@ -23,7 +16,10 @@ func Test_empty(t *testing.T) {
unsetString string
unsetInt64 int64
tcc = []tc{
tcc = []struct {
value interface{}
expect interface{}
}{
{
value: []string{},
expect: true,
+1 -1
View File
@@ -96,6 +96,6 @@ func eval(e string, p interface{}) {
if err != nil {
fmt.Printf("error: %v", err)
} else {
fmt.Printf("%v", result)
fmt.Printf("%+v", result)
}
}
+28 -1
View File
@@ -1,6 +1,7 @@
package expr
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)
@@ -89,7 +90,7 @@ func TestVars_UnmarshalJSON(t *testing.T) {
}{
{"empty", "", &Vars{}},
{"empty", "{}", &Vars{}},
{"empty", `{"a":{"@value":"b"}}`, RVars{"a": &Unresolved{value: "b"}}.Vars()},
{"string", `{"a":{"@value":"b"}}`, RVars{"a": &Unresolved{value: "b"}}.Vars()},
}
for _, c := range cases {
@@ -104,3 +105,29 @@ func TestVars_UnmarshalJSON(t *testing.T) {
})
}
}
func TestVars_UMarshalJSON(t *testing.T) {
cases := []struct {
name string
json string
vars *Vars
}{
{"empty", "{}", &Vars{}},
{"string", `{"a":{"@value":"b","@type":"String"}}`, RVars{"a": &String{value: "b"}}.Vars()},
{"array",
`{"arr":{"@value":[{"@value":"foo","@type":"String"},{"@value":"bar","@type":"String"}],"@type":"Array"}}`,
RVars{"arr": &Array{value: []TypedValue{&String{value: "foo"}, &String{value: "bar"}}}}.Vars()},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var (
r = require.New(t)
)
j, err := json.Marshal(c.vars)
r.NoError(err)
r.Equal(c.json, string(j))
})
}
}