From 65549cc8bad723052ef83f778aa04bd3fafa9518 Mon Sep 17 00:00:00 2001 From: Gianni Chiappetta Date: Tue, 19 Jul 2022 14:38:13 -0400 Subject: [PATCH] fix: follow pointers when evaling identifiers --- compiler.go | 9 +++++++-- struct_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/compiler.go b/compiler.go index 8a3131f..e73293d 100644 --- a/compiler.go +++ b/compiler.go @@ -418,6 +418,12 @@ func (c *compiler) evalIdentifier(node *ast.Identifier) (interface{}, error) { } f := rv.FieldByName(node.Value) + if f.Kind() == reflect.Ptr { + if f.IsNil() { + return nil, nil + } + f = f.Elem() + } if !f.IsValid() { m := rv.MethodByName(node.Value) if !m.IsValid() { @@ -425,11 +431,10 @@ func (c *compiler) evalIdentifier(node *ast.Identifier) (interface{}, error) { } return m.Interface(), nil } - if !f.CanInterface() { - return nil, fmt.Errorf("'%s'cannot return value obtained from unexported field or method '%s' (%s)", node.Callee.String(), node.Value, node) } + return f.Interface(), nil } if c.ctx.Has(node.Value) { diff --git a/struct_test.go b/struct_test.go index d45cf26..e6dd7ee 100644 --- a/struct_test.go +++ b/struct_test.go @@ -106,7 +106,6 @@ func Test_Render_Struct_PointerMethod_IsNil(t *testing.T) { } for p := first; p != nil; p = p.Next { - ctx := NewContextWith(map[string]interface{}{"p": p}) res, err := Render(input, ctx) r.NoError(err) @@ -115,6 +114,51 @@ func Test_Render_Struct_PointerMethod_IsNil(t *testing.T) { } } +func Test_Render_Struct_PointerValue_Nil(t *testing.T) { + r := require.New(t) + + type user struct { + Name string + Image *string + } + + u := user{ + Name: "Garn Clapstick", + Image: nil, + } + ctx := NewContextWith(map[string]interface{}{ + "user": u, + }) + input := `<%= user.Name %>: <%= user.Image %>` + res, err := Render(input, ctx) + + r.NoError(err) + r.Equal(`Garn Clapstick: `, res) +} + +func Test_Render_Struct_PointerValue_NonNil(t *testing.T) { + r := require.New(t) + + type user struct { + Name string + Image *string + } + + image := "bicep.png" + u := user{ + Name: "Scrinch Archipeligo", + Image: &image, + } + ctx := NewContextWith(map[string]interface{}{ + "user": u, + }) + input := `<%= user.Name %>: <%= user.Image %>` + res, err := Render(input, ctx) + + r.NoError(err) + r.Equal(`Scrinch Archipeligo: bicep.png`, res) +} + func Test_Render_Struct_Multiple_Access(t *testing.T) { r := require.New(t)