diff --git a/evaluationFailure_test.go b/evaluationFailure_test.go index e04f24f..67cda15 100644 --- a/evaluationFailure_test.go +++ b/evaluationFailure_test.go @@ -45,6 +45,41 @@ var EVALUATION_FAILURE_PARAMETERS = map[string]interface{}{ "bool": true, } +func TestUnexportedStructParameter(test *testing.T) { + parameters := map[string]interface{}{ + "foo": struct{ a string }{a: "hello"}, + } + expression, err := NewEvaluableExpression(`foo.a == "hello"`) + if err != nil { + test.Fatal(err) + } + _, err = expression.Evaluate(parameters) + if err == nil { + test.Error("expected non-nil error") + } +} + +func TestNestedParameters(test *testing.T) { + parameters := map[string]interface{}{ + "foo": MapParameters{ + "bar": MapParameters{ + "baz": 5, + }, + }, + } + expression, err := NewEvaluableExpression(`foo.bar.baz == 5`) + if err != nil { + test.Fatal(err) + } + result, err := expression.Evaluate(parameters) + if err != nil { + test.Error(err) + } + if got, want := result.(bool), true; got != want { + test.Errorf("bad result: got %t, want %t", got, want) + } +} + func TestComplexParameter(test *testing.T) { var expression *EvaluableExpression diff --git a/evaluationStage.go b/evaluationStage.go index 11ea587..4f23809 100644 --- a/evaluationStage.go +++ b/evaluationStage.go @@ -314,6 +314,13 @@ func makeAccessorStage(pair []string) evaluationOperator { }() for i := 1; i < len(pair); i++ { + if p, ok := value.(Parameters); ok { + value, err = p.Get(pair[i]) + if err != nil { + return nil, err + } + continue + } coreValue := reflect.ValueOf(value) @@ -325,6 +332,11 @@ func makeAccessorStage(pair []string) evaluationOperator { coreValue = coreValue.Elem() } + if field, ok := coreValue.Type().FieldByName(pair[i]); ok && len(field.PkgPath) > 0 { + // According to reflect docs, if len(PkgPath) > 0 then the field is unexported + return nil, fmt.Errorf("Unable to access unexported field '%s'", field.PkgPath) + } + if coreValue.Kind() != reflect.Struct { return nil, errors.New("Unable to access '" + pair[i] + "', '" + pair[i-1] + "' is not a struct") } diff --git a/parsing.go b/parsing.go index 40c7ed2..f4e444d 100644 --- a/parsing.go +++ b/parsing.go @@ -189,16 +189,6 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre splits := strings.Split(tokenString, ".") tokenValue = splits - // check that none of them are unexported - for i := 1; i < len(splits); i++ { - - firstCharacter := getFirstRune(splits[i]) - - if unicode.ToUpper(firstCharacter) != firstCharacter { - errorMsg := fmt.Sprintf("Unable to access unexported field '%s' in token '%s'", splits[i], tokenString) - return ExpressionToken{}, errors.New(errorMsg), false - } - } } break } diff --git a/parsingFailure_test.go b/parsingFailure_test.go index d8a3184..957d575 100644 --- a/parsingFailure_test.go +++ b/parsingFailure_test.go @@ -178,13 +178,6 @@ func TestParsingFailure(test *testing.T) { Input: "foo.Bar.", Expected: HANGING_ACCESSOR, }, - ParsingFailureTest{ - - // this is expected to change once there are structtags in place that allow aliasing of fields - Name: "Unexported parameter access", - Input: "foo.bar", - Expected: UNEXPORTED_ACCESSOR, - }, ParsingFailureTest{ Name: "Incomplete Hex", Input: "0x",