From 8609bca96dfc84a799599804a78d1a93c1a28a26 Mon Sep 17 00:00:00 2001 From: Iurii Panarin Date: Thu, 8 Feb 2024 19:21:31 +0100 Subject: [PATCH] code(builtin) - add reverse function --- builtin/builtin.go | 35 +++++++++++++++++++++++++++++++++++ builtin/builtin_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/builtin/builtin.go b/builtin/builtin.go index 14fc8a4b..41b1816f 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -868,6 +868,41 @@ var Builtins = []*Function{ return anyType, fmt.Errorf("cannot transform %s from pairs", args[0]) }, }, + { + Name: "reverse", + Func: func(args ...any) (any, error) { + if len(args) != 1 { + return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + + v := reflect.ValueOf(args[0]) + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + return nil, fmt.Errorf("cannot reverse %s", v.Kind()) + } + + arr := make([]any, v.Len()) + + size := v.Len() + + for i := 0; i < size; i++ { + arr[i] = v.Index(size - i - 1).Interface() + } + + return arr, nil + + }, + Validate: func(args []reflect.Type) (reflect.Type, error) { + if len(args) != 1 { + return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + switch kind(args[0]) { + case reflect.Interface, reflect.Slice, reflect.Array: + default: + return anyType, fmt.Errorf("cannot reverse %s", args[0]) + } + return arrayType, nil + }, + }, { Name: "sort", Func: func(args ...any) (any, error) { diff --git a/builtin/builtin_test.go b/builtin/builtin_test.go index e302d719..d6b967d1 100644 --- a/builtin/builtin_test.go +++ b/builtin/builtin_test.go @@ -483,6 +483,38 @@ func TestBuiltin_type(t *testing.T) { } } +func TestBuiltin_reverse(t *testing.T) { + env := map[string]any{ + "ArrayOfString": []string{"foo", "bar", "baz"}, + "ArrayOfInt": []int{2, 1, 3}, + "ArrayOfFloat": []float64{3.0, 2.0, 1.0}, + "ArrayOfFoo": []mock.Foo{{Value: "c"}, {Value: "a"}, {Value: "b"}}, + } + tests := []struct { + input string + want any + }{ + {`reverse([])`, []any{}}, + {`reverse(ArrayOfInt)`, []any{3, 1, 2}}, + {`reverse(ArrayOfFloat)`, []any{1.0, 2.0, 3.0}}, + {`reverse(ArrayOfFoo)`, []any{mock.Foo{Value: "b"}, mock.Foo{Value: "a"}, mock.Foo{Value: "c"}}}, + {`reverse([[1,2], [2,2]])`, []any{[]any{2, 2}, []any{1, 2}}}, + {`reverse(reverse([[1,2], [2,2]]))`, []any{[]any{1, 2}, []any{2, 2}}}, + {`reverse([{"test": true}, {id:4}, {name: "value"}])`, []any{map[string]any{"name": "value"}, map[string]any{"id": 4}, map[string]any{"test": true}}}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + program, err := expr.Compile(test.input, expr.Env(env)) + require.NoError(t, err) + + out, err := expr.Run(program, env) + require.NoError(t, err) + assert.Equal(t, test.want, out) + }) + } +} + func TestBuiltin_sort(t *testing.T) { env := map[string]any{ "ArrayOfString": []string{"foo", "bar", "baz"},