From cf3a12bf0d4ddad6ed067e85f0c9a1ef1a05447a Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 4 May 2024 10:14:44 +1000 Subject: [PATCH] Fixed a few bugs with the 'index' builtin --- ucl/builtins.go | 2 +- ucl/inst.go | 1 - ucl/objs.go | 14 +++++++++++++- ucl/testbuiltins_test.go | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/ucl/builtins.go b/ucl/builtins.go index a13544d..a9ad5f0 100644 --- a/ucl/builtins.go +++ b/ucl/builtins.go @@ -144,7 +144,7 @@ func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) { case listable: intIdx, ok := idx.(intObject) if !ok { - return nil, errors.New("expected int for listable") + return nil, nil } if int(intIdx) >= 0 && int(intIdx) < v.Len() { val = v.Index(int(intIdx)) diff --git a/ucl/inst.go b/ucl/inst.go index 0eef348..8fa9561 100644 --- a/ucl/inst.go +++ b/ucl/inst.go @@ -36,7 +36,6 @@ func New(opts ...InstOption) *Inst { rootEC.addCmd("echo", invokableFunc(echoBuiltin)) rootEC.addCmd("set", invokableFunc(setBuiltin)) rootEC.addCmd("toUpper", invokableFunc(toUpperBuiltin)) - //rootEC.addCmd("cat", invokableFunc(catBuiltin)) rootEC.addCmd("len", invokableFunc(lenBuiltin)) rootEC.addCmd("index", invokableFunc(indexBuiltin)) rootEC.addCmd("call", invokableFunc(callBuiltin)) diff --git a/ucl/objs.go b/ucl/objs.go index bfa3081..3d3373d 100644 --- a/ucl/objs.go +++ b/ucl/objs.go @@ -425,7 +425,19 @@ func (s structProxyObject) Len() int { } func (s structProxyObject) Value(k string) object { - e, err := fromGoValue(s.v.FieldByName(k).Interface()) + f := s.v.FieldByName(k) + if !f.IsValid() { + return nil + } + + if f.Kind() == reflect.Ptr { + if f.IsNil() { + return nil + } + f = f.Elem() + } + + e, err := fromGoValue(f.Interface()) if err != nil { return nil } diff --git a/ucl/testbuiltins_test.go b/ucl/testbuiltins_test.go index 11f6c8d..10a16ab 100644 --- a/ucl/testbuiltins_test.go +++ b/ucl/testbuiltins_test.go @@ -524,9 +524,21 @@ func TestBuiltins_Index(t *testing.T) { {desc: "go list 1", expr: `goInt | index 1`, want: "5\n"}, {desc: "go list 2", expr: `goInt | index 2`, want: "4\n"}, + {desc: "go list 3", expr: `goInt | index 555`, want: "(nil)\n"}, + {desc: "go list 4", expr: `goInt | index -12`, want: "(nil)\n"}, + {desc: "go list 5", expr: `goInt | index NotAnIndex`, want: "(nil)\n"}, {desc: "go struct 1", expr: `goStruct | index Alpha`, want: "foo\n"}, {desc: "go struct 2", expr: `goStruct | index Beta`, want: "bar\n"}, {desc: "go struct 3", expr: `goStruct | index Gamma 1`, want: "33\n"}, + {desc: "go struct 4", expr: `goStruct | index Nested This`, want: "fla\n"}, + {desc: "go struct 5", expr: `goStruct | index Nested That`, want: "132\n"}, + {desc: "go struct 6", expr: `goStruct | index NestedPtr This`, want: "flaPtr\n"}, + {desc: "go struct 7", expr: `goStruct | index NestedPtr That`, want: "6678\n"}, + {desc: "go struct 8", expr: `goStruct | index Missing`, want: "(nil)\n"}, + {desc: "go struct 9", expr: `goStruct | index Nested Missing 123 Stuff`, want: "(nil)\n"}, + {desc: "go struct 10", expr: `goStruct | index NestedPtrNil`, want: "(nil)\n"}, + {desc: "go struct 11", expr: `goStruct | index NestedPtrNil This`, want: "(nil)\n"}, + {desc: "go struct 12", expr: `goStruct | index NestedPtrNil Missing`, want: "(nil)\n"}, } for _, tt := range tests { @@ -539,14 +551,29 @@ func TestBuiltins_Index(t *testing.T) { return []int{6, 5, 4}, nil }) inst.SetBuiltin("goStruct", func(ctx context.Context, args CallArgs) (any, error) { + type nested struct { + This string + That int + } return struct { - Alpha string - Beta string - Gamma []int + Alpha string + Beta string + Gamma []int + Nested nested + NestedPtr *nested + NestedPtrNil *nested }{ Alpha: "foo", Beta: "bar", Gamma: []int{22, 33}, + Nested: nested{ + This: "fla", + That: 132, + }, + NestedPtr: &nested{ + This: "flaPtr", + That: 6678, + }, }, nil }) err := EvalAndDisplay(ctx, inst, tt.expr)