From 28e290347c2e56850ed7362edb96f9a9a587688a Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Tue, 21 Aug 2018 22:29:29 +0700 Subject: [PATCH] Add possibility to use "in" operator with maps and structs. --- eval_test.go | 35 +++++++++++++++++++++++++++++++++++ runtime.go | 26 ++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/eval_test.go b/eval_test.go index 92066b0c4..7c62cd4f8 100644 --- a/eval_test.go +++ b/eval_test.go @@ -249,6 +249,26 @@ var evalTests = []evalTest{ nil, true, }, + { + `"a" in {a:1, b:2}`, + nil, + true, + }, + { + `"Bar" in Foo`, + struct{ Foo struct{ Bar bool } }{struct{ Bar bool }{true}}, + true, + }, + { + `"Bar" in Ptr`, + struct{ Ptr *struct{ Bar bool } }{&struct{ Bar bool }{true}}, + true, + }, + { + `"Bar" in NilPtr`, + struct{ NilPtr *bool }{nil}, + false, + }, { `0 in nil`, nil, @@ -432,6 +452,21 @@ var evalErrorTests = []evalErrorTest{ nil, `operator "in" not defined on string`, }, + { + `nil in map`, + map[string]interface{}{"map": map[string]interface{}{"true": "yes"}}, + `cannot use as index to map[string]interface {}`, + }, + { + `nil in foo`, + map[string]interface{}{"foo": struct{ Bar bool }{true}}, + `cannot use as field name of struct { Bar bool }`, + }, + { + `true in foo`, + map[string]interface{}{"foo": struct{ Bar bool }{true}}, + `cannot use bool as field name of struct { Bar bool }`, + }, { "len()", nil, diff --git a/runtime.go b/runtime.go index ab620b46a..43c5ed653 100644 --- a/runtime.go +++ b/runtime.go @@ -115,6 +115,32 @@ func contains(needle interface{}, array interface{}) (bool, error) { } } return false, nil + case reflect.Map: + n := reflect.ValueOf(needle) + if !n.IsValid() { + return false, fmt.Errorf("cannot use %T as index to %T", needle, array) + } + value := v.MapIndex(n) + if value.IsValid() { + return true, nil + } + return false, nil + case reflect.Struct: + n := reflect.ValueOf(needle) + if !n.IsValid() || n.Kind() != reflect.String { + return false, fmt.Errorf("cannot use %T as field name of %T", needle, array) + } + value := v.FieldByName(n.String()) + if value.IsValid() { + return true, nil + } + return false, nil + case reflect.Ptr: + value := v.Elem() + if value.IsValid() && value.CanInterface() { + return contains(needle, value.Interface()) + } + return false, nil } return false, fmt.Errorf("operator \"in\" not defined on %T", array) }