mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Make sure get() returns nil for map[string]string on missing keys
Fixes #723
This commit is contained in:
+1
-8
@@ -627,14 +627,7 @@ var Builtins = []*Function{
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Func: func(args ...any) (out any, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
return runtime.Fetch(args[0], args[1]), nil
|
||||
},
|
||||
Func: get,
|
||||
},
|
||||
{
|
||||
Name: "take",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
"github.com/expr-lang/expr/vm/runtime"
|
||||
)
|
||||
|
||||
func Len(x any) any {
|
||||
@@ -367,3 +368,69 @@ func flatten(arg reflect.Value) []any {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func get(params ...any) (out any, err error) {
|
||||
from := params[0]
|
||||
i := params[1]
|
||||
v := reflect.ValueOf(from)
|
||||
|
||||
if v.Kind() == reflect.Invalid {
|
||||
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
|
||||
}
|
||||
|
||||
// Methods can be defined on any type.
|
||||
if v.NumMethod() > 0 {
|
||||
if methodName, ok := i.(string); ok {
|
||||
method := v.MethodByName(methodName)
|
||||
if method.IsValid() {
|
||||
return method.Interface(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v = deref.Value(v)
|
||||
i = deref.Deref(i)
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
index := runtime.ToInt(i)
|
||||
l := v.Len()
|
||||
if index < 0 {
|
||||
index = l + index
|
||||
}
|
||||
if 0 <= index && index < l {
|
||||
value := v.Index(index)
|
||||
if value.IsValid() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
var value reflect.Value
|
||||
if i == nil {
|
||||
value = v.MapIndex(reflect.Zero(v.Type().Key()))
|
||||
} else {
|
||||
value = v.MapIndex(reflect.ValueOf(i))
|
||||
}
|
||||
if value.IsValid() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
fieldName := i.(string)
|
||||
value := v.FieldByNameFunc(func(name string) bool {
|
||||
field, _ := v.Type().FieldByName(name)
|
||||
if field.Tag.Get("expr") == fieldName {
|
||||
return true
|
||||
}
|
||||
return name == fieldName
|
||||
})
|
||||
if value.IsValid() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Main difference from runtime.Fetch
|
||||
// is that we return `nil` instead of panic.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -7088,58 +7088,33 @@ get(false ? i32 : list, i64)
|
||||
get(false ? i64 : true, f64)
|
||||
get(false ? score : ok, trimSuffix("bar", "bar"))
|
||||
get(filter(list, true), i)
|
||||
get(groupBy(array, "bar"), add)
|
||||
get(groupBy(array, "bar"), i)
|
||||
get(groupBy(array, "bar"), ok)
|
||||
get(groupBy(array, "foo"), half)
|
||||
get(groupBy(array, #), add)
|
||||
get(groupBy(array, #), array)
|
||||
get(groupBy(array, #), f32)
|
||||
get(groupBy(array, #), f64)
|
||||
get(groupBy(array, #), foo)
|
||||
get(groupBy(array, #), greet)
|
||||
get(groupBy(array, #), half)
|
||||
get(groupBy(array, #), i)
|
||||
get(groupBy(array, #), i64 * 0.5)
|
||||
get(groupBy(array, #), i64)
|
||||
get(groupBy(array, #)?.String, i64)
|
||||
get(groupBy(array, 0.5), add)
|
||||
get(groupBy(array, 0.5), div)
|
||||
get(groupBy(array, 0.5), ok)
|
||||
get(groupBy(array, 1), add)
|
||||
get(groupBy(array, 1), f32)
|
||||
get(groupBy(array, f64), i64)
|
||||
get(groupBy(array, false), findIndex(array, ok))
|
||||
get(groupBy(array, i), ok)
|
||||
get(groupBy(array, i), score)
|
||||
get(groupBy(array, i64), add)
|
||||
get(groupBy(array, ok), add)
|
||||
get(groupBy(array, true), i32)
|
||||
get(groupBy(list, "bar"), i32)
|
||||
get(groupBy(list, "foo"), greet)
|
||||
get(groupBy(list, #), div)
|
||||
get(groupBy(list, #), f32 <= f64)
|
||||
get(groupBy(list, #), f32)
|
||||
get(groupBy(list, #), foo)
|
||||
get(groupBy(list, #), greet)
|
||||
get(groupBy(list, #), half)
|
||||
get(groupBy(list, #), i)
|
||||
get(groupBy(list, #), i32)
|
||||
get(groupBy(list, #), i64)
|
||||
get(groupBy(list, #), int(f32))
|
||||
get(groupBy(list, #), list)
|
||||
get(groupBy(list, #), reduce(array, foo))
|
||||
get(groupBy(list, #), score(1))
|
||||
get(groupBy(list, #), score)
|
||||
get(groupBy(list, 0.5), array)
|
||||
get(groupBy(list, 0.5), div)
|
||||
get(groupBy(list, 0.5), ok)
|
||||
get(groupBy(list, 1), i32)
|
||||
get(groupBy(list, f32), greet)
|
||||
get(groupBy(list, foo), add)
|
||||
get(groupBy(list, foo), i32)
|
||||
get(groupBy(list, ok), greet)
|
||||
get(groupBy(list, ok), list)
|
||||
get(i .. 1, 1 * 1)
|
||||
get(i .. i32, i)
|
||||
get(i32 .. i, i64)
|
||||
@@ -7200,12 +7175,10 @@ get(list, max(i32))
|
||||
get(list, min(i32))
|
||||
get(list, min(i64))
|
||||
get(list, ok ? 0.5 : "foo")
|
||||
get(list, ok ? array : "foo")
|
||||
get(list, reduce(list, i64))
|
||||
get(list, score(1))
|
||||
get(list, score(i))
|
||||
get(list, true ? i : i32)
|
||||
get(list, {"foo": i32}?.i)
|
||||
get(list[i64:1], i)
|
||||
get(map(array, #), i)
|
||||
get(map(array, #), i32)
|
||||
@@ -7221,18 +7194,15 @@ get(map(list, 1), i32)
|
||||
get(map(list, array), i)
|
||||
get(map(list, i32), i64)
|
||||
get(map(list, i64), i64)
|
||||
get(ok ? "bar" : false, array)
|
||||
get(ok ? 1 : "bar", f32)
|
||||
get(ok ? add : f32, array)
|
||||
get(ok ? f64 : div, i64)
|
||||
get(ok ? false : list, f32 > i)
|
||||
get(ok ? foo : 0.5, div)
|
||||
get(ok ? half : i32, i)
|
||||
get(ok ? half : ok, f64)
|
||||
get(ok ? i : 0.5, half)
|
||||
get(ok ? i32 : half, f64)
|
||||
get(ok ? i64 : foo, f32)
|
||||
get(ok ? list : "foo", add)
|
||||
get(ok ? list : i32, f32)
|
||||
get(ok ? ok : div, greet)
|
||||
get(ok ? score : f64, i)
|
||||
@@ -7241,7 +7211,6 @@ get(reduce(list, array), i32)
|
||||
get(sort(array), i32)
|
||||
get(take(list, i), i64)
|
||||
get(true ? "bar" : ok, score(i))
|
||||
get(true ? "foo" : half, list)
|
||||
get(true ? 0.5 : i32, array)
|
||||
get(true ? f32 : 0.5, ok)
|
||||
get(true ? false : foo, i64 > 0.5)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package issue_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/expr-lang/expr/internal/testify/require"
|
||||
)
|
||||
|
||||
func TestIssue723(t *testing.T) {
|
||||
emptyMap := make(map[string]string)
|
||||
env := map[string]interface{}{
|
||||
"empty_map": emptyMap,
|
||||
}
|
||||
|
||||
code := `get(empty_map, "non_existing_key")`
|
||||
|
||||
program, err := expr.Compile(code, expr.Env(env))
|
||||
require.NoError(t, err)
|
||||
|
||||
output, err := expr.Run(program, env)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, output)
|
||||
}
|
||||
Vendored
-31
@@ -7088,58 +7088,33 @@ get(false ? i32 : list, i64)
|
||||
get(false ? i64 : true, f64)
|
||||
get(false ? score : ok, trimSuffix("bar", "bar"))
|
||||
get(filter(list, true), i)
|
||||
get(groupBy(array, "bar"), add)
|
||||
get(groupBy(array, "bar"), i)
|
||||
get(groupBy(array, "bar"), ok)
|
||||
get(groupBy(array, "foo"), half)
|
||||
get(groupBy(array, #), add)
|
||||
get(groupBy(array, #), array)
|
||||
get(groupBy(array, #), f32)
|
||||
get(groupBy(array, #), f64)
|
||||
get(groupBy(array, #), foo)
|
||||
get(groupBy(array, #), greet)
|
||||
get(groupBy(array, #), half)
|
||||
get(groupBy(array, #), i)
|
||||
get(groupBy(array, #), i64 * 0.5)
|
||||
get(groupBy(array, #), i64)
|
||||
get(groupBy(array, #)?.String, i64)
|
||||
get(groupBy(array, 0.5), add)
|
||||
get(groupBy(array, 0.5), div)
|
||||
get(groupBy(array, 0.5), ok)
|
||||
get(groupBy(array, 1), add)
|
||||
get(groupBy(array, 1), f32)
|
||||
get(groupBy(array, f64), i64)
|
||||
get(groupBy(array, false), findIndex(array, ok))
|
||||
get(groupBy(array, i), ok)
|
||||
get(groupBy(array, i), score)
|
||||
get(groupBy(array, i64), add)
|
||||
get(groupBy(array, ok), add)
|
||||
get(groupBy(array, true), i32)
|
||||
get(groupBy(list, "bar"), i32)
|
||||
get(groupBy(list, "foo"), greet)
|
||||
get(groupBy(list, #), div)
|
||||
get(groupBy(list, #), f32 <= f64)
|
||||
get(groupBy(list, #), f32)
|
||||
get(groupBy(list, #), foo)
|
||||
get(groupBy(list, #), greet)
|
||||
get(groupBy(list, #), half)
|
||||
get(groupBy(list, #), i)
|
||||
get(groupBy(list, #), i32)
|
||||
get(groupBy(list, #), i64)
|
||||
get(groupBy(list, #), int(f32))
|
||||
get(groupBy(list, #), list)
|
||||
get(groupBy(list, #), reduce(array, foo))
|
||||
get(groupBy(list, #), score(1))
|
||||
get(groupBy(list, #), score)
|
||||
get(groupBy(list, 0.5), array)
|
||||
get(groupBy(list, 0.5), div)
|
||||
get(groupBy(list, 0.5), ok)
|
||||
get(groupBy(list, 1), i32)
|
||||
get(groupBy(list, f32), greet)
|
||||
get(groupBy(list, foo), add)
|
||||
get(groupBy(list, foo), i32)
|
||||
get(groupBy(list, ok), greet)
|
||||
get(groupBy(list, ok), list)
|
||||
get(i .. 1, 1 * 1)
|
||||
get(i .. i32, i)
|
||||
get(i32 .. i, i64)
|
||||
@@ -7200,12 +7175,10 @@ get(list, max(i32))
|
||||
get(list, min(i32))
|
||||
get(list, min(i64))
|
||||
get(list, ok ? 0.5 : "foo")
|
||||
get(list, ok ? array : "foo")
|
||||
get(list, reduce(list, i64))
|
||||
get(list, score(1))
|
||||
get(list, score(i))
|
||||
get(list, true ? i : i32)
|
||||
get(list, {"foo": i32}?.i)
|
||||
get(list[i64:1], i)
|
||||
get(map(array, #), i)
|
||||
get(map(array, #), i32)
|
||||
@@ -7221,18 +7194,15 @@ get(map(list, 1), i32)
|
||||
get(map(list, array), i)
|
||||
get(map(list, i32), i64)
|
||||
get(map(list, i64), i64)
|
||||
get(ok ? "bar" : false, array)
|
||||
get(ok ? 1 : "bar", f32)
|
||||
get(ok ? add : f32, array)
|
||||
get(ok ? f64 : div, i64)
|
||||
get(ok ? false : list, f32 > i)
|
||||
get(ok ? foo : 0.5, div)
|
||||
get(ok ? half : i32, i)
|
||||
get(ok ? half : ok, f64)
|
||||
get(ok ? i : 0.5, half)
|
||||
get(ok ? i32 : half, f64)
|
||||
get(ok ? i64 : foo, f32)
|
||||
get(ok ? list : "foo", add)
|
||||
get(ok ? list : i32, f32)
|
||||
get(ok ? ok : div, greet)
|
||||
get(ok ? score : f64, i)
|
||||
@@ -7241,7 +7211,6 @@ get(reduce(list, array), i32)
|
||||
get(sort(array), i32)
|
||||
get(take(list, i), i64)
|
||||
get(true ? "bar" : ok, score(i))
|
||||
get(true ? "foo" : half, list)
|
||||
get(true ? 0.5 : i32, array)
|
||||
get(true ? f32 : 0.5, ok)
|
||||
get(true ? false : foo, i64 > 0.5)
|
||||
|
||||
Reference in New Issue
Block a user