Fixed array & type handlign for gval&expr
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user