Skip to content

Commit

Permalink
Property access to struct values
Browse files Browse the repository at this point in the history
  • Loading branch information
osteele committed Jul 20, 2017
1 parent 1b0f0cf commit 2cdd59d
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
54 changes: 54 additions & 0 deletions evaluator/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func ValueOf(value interface{}) Value {
return arrayValue{wrapperValue{value}}
case reflect.Map:
return mapValue{wrapperValue{value}}
case reflect.Struct:
return structValue{wrapperValue{value}}
default:
return wrapperValue{value}
}
Expand Down Expand Up @@ -87,6 +89,7 @@ var trueValue = wrapperValue{true}
type arrayValue struct{ wrapperValue }
type mapValue struct{ wrapperValue }
type stringValue struct{ wrapperValue }
type structValue struct{ wrapperValue }

func (v arrayValue) Contains(elem Value) bool {
rv := reflect.ValueOf(v.basis)
Expand Down Expand Up @@ -116,6 +119,21 @@ func (v stringValue) Contains(substr Value) bool {
return strings.Contains(v.basis.(string), s)
}

func (v structValue) Contains(elem Value) bool {
name, ok := elem.Interface().(string)
if !ok {
return false
}
rt := reflect.TypeOf(v.basis)
if _, found := rt.FieldByName(name); found {
return true
}
if _, found := rt.MethodByName(name); found {
return true
}
return false
}

func (v arrayValue) IndexValue(index Value) Value {
rv := reflect.ValueOf(v.basis)
if n, ok := index.Interface().(int); ok {
Expand Down Expand Up @@ -184,3 +202,39 @@ func (v stringValue) PropertyValue(index Value) Value {
}
return nilValue
}

func (v structValue) PropertyValue(index Value) Value {
name, ok := index.Interface().(string)
if !ok {
return nilValue
}
rv := reflect.ValueOf(v.basis)
rt := reflect.TypeOf(v.basis)
if _, found := rt.FieldByName(name); found {
fv := rv.FieldByName(name)
if fv.Kind() == reflect.Func {
return v.invoke(fv)
}
return ValueOf(fv.Interface())
}
if _, found := rt.MethodByName(name); found {
m := rv.MethodByName(name)
return v.invoke(m)
}
return nilValue
}

func (v structValue) invoke(fv reflect.Value) Value {
if fv.IsNil() {
return nilValue
}
mt := fv.Type()
if mt.NumIn() > 0 || mt.NumOut() > 2 {
return nilValue
}
results := fv.Call([]reflect.Value{})
if len(results) > 1 && !results[1].IsNil() {
panic(results[1].Interface())
}
return ValueOf(results[0].Interface())
}
34 changes: 34 additions & 0 deletions evaluator/value_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package evaluator

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -64,3 +65,36 @@ func TestValue_hash(t *testing.T) {
require.Equal(t, nil, hashPtr.IndexValue(ValueOf("missing_key")).Interface())
require.Equal(t, 1, hashPtr.PropertyValue(ValueOf("size")).Interface())
}

type testValueStruct struct {
F int
F1 func() int
F2 func() (int, error)
F2e func() (int, error)
}

func (tv testValueStruct) M1() int { return 3 }
func (tv testValueStruct) M2() (int, error) { return 4, nil }
func (tv testValueStruct) M2e() (int, error) { return 4, fmt.Errorf("expected error") }

func (tv *testValueStruct) PM1() int { return 3 }
func (tv *testValueStruct) PM2() (int, error) { return 4, nil }
func (tv *testValueStruct) PM2e() (int, error) { return 4, fmt.Errorf("expected error") }

func TestValue_struct(t *testing.T) {
s := ValueOf(testValueStruct{
F: -1,
F1: func() int { return 1 },
F2: func() (int, error) { return 2, nil },
F2e: func() (int, error) { return 0, fmt.Errorf("expected error") },
})
require.True(t, s.Contains(ValueOf("F")))
require.True(t, s.Contains(ValueOf("F1")))
require.Equal(t, -1, s.PropertyValue(ValueOf("F")).Interface())
require.Equal(t, 1, s.PropertyValue(ValueOf("F1")).Interface())
require.Equal(t, 2, s.PropertyValue(ValueOf("F2")).Interface())
require.Panics(t, func() { s.PropertyValue(ValueOf("F2e")) })
require.Equal(t, 3, s.PropertyValue(ValueOf("M1")).Interface())
require.Equal(t, 4, s.PropertyValue(ValueOf("M2")).Interface())
require.Panics(t, func() { s.PropertyValue(ValueOf("M2e")) })
}

0 comments on commit 2cdd59d

Please sign in to comment.