Skip to content

Commit

Permalink
Merge pull request #160 from gf3/pointer-value
Browse files Browse the repository at this point in the history
fix: follow pointers when evaling identifiers
  • Loading branch information
paganotoni authored Jul 19, 2022
2 parents 55ead9a + 65549cc commit 2719ced
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
9 changes: 7 additions & 2 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,23 @@ 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() {
return nil, fmt.Errorf("'%s' does not have a field or method named '%s' (%s)", node.Callee.String(), node.Value, node)
}
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) {
Expand Down
46 changes: 45 additions & 1 deletion struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down

0 comments on commit 2719ced

Please sign in to comment.