Skip to content

Commit

Permalink
feat: additional template render methods
Browse files Browse the repository at this point in the history
  • Loading branch information
twelvelabs committed Jun 10, 2023
1 parent e7723cd commit f66770a
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 3 deletions.
2 changes: 1 addition & 1 deletion render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func String(s string, data any) (string, error) {

func execute(t *template.Template, data any) (string, error) {
if t == nil {
return "", nil
return strEmpty, nil
}
buf := bytes.Buffer{}
err := t.Execute(&buf, data)
Expand Down
56 changes: 54 additions & 2 deletions render/template.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package render

import (
"fmt"
"text/template"

"github.com/spf13/cast"
)

const (
strEmpty string = ""
strNoValue string = "<no value>"
)

// Compile parses a template string and returns, if successful,
Expand Down Expand Up @@ -45,7 +53,51 @@ func (ts *Template) UnmarshalText(text []byte) error {
return err
}

// Render renders the template using data.
// Render renders the template as a string.
func (ts *Template) Render(data any) (string, error) {
return execute(ts.t, data)
rendered, err := execute(ts.t, data)
if err != nil {
return strEmpty, err
}
if rendered == strEmpty || rendered == strNoValue {
return strEmpty, nil
}
return rendered, nil
}

// Render renders the template as a bool.
func (ts *Template) RenderBool(data any) (bool, error) {
rendered, err := ts.Render(data)
if err != nil {
return false, err
}
if rendered == strEmpty {
return false, nil
}
return cast.ToBoolE(rendered)
}

// Render renders the template as an int.
func (ts *Template) RenderInt(data any) (int, error) {
rendered, err := ts.Render(data)
if err != nil {
return 0, err
}
if rendered == strEmpty {
return 0, nil
}
return cast.ToIntE(rendered)
}

// RenderRequired renders the template as a string,
// but returns an error if the result is empty.
func (ts *Template) RenderRequired(data any) (string, error) {
rendered, err := ts.Render(data)
if err != nil {
return strEmpty, err
}
if rendered == strEmpty {
return strEmpty, fmt.Errorf("evaluated to an empty string")
}
return rendered, nil
}
78 changes: 78 additions & 0 deletions render/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,90 @@ func TestTemplate_Render(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "Hello, World", rendered)

ts, _ = Compile(`{{ .Something }}`)
rendered, err = ts.Render(nil)
assert.NoError(t, err)
assert.Equal(t, "", rendered)

ts, _ = Compile(`Hello, {{ fail "boom" }}`)
rendered, err = ts.Render(nil)
assert.ErrorContains(t, err, "fail: boom")
assert.Equal(t, "", rendered)
}

func TestTemplate_RenderBool(t *testing.T) {
ts, _ := Compile(`{{ .Value }}`)
rendered, err := ts.RenderBool(map[string]any{
"Value": "true",
})
assert.NoError(t, err)
assert.Equal(t, true, rendered)

ts, _ = Compile(`{{ .Value }}`)
rendered, err = ts.RenderBool(nil)
assert.NoError(t, err)
assert.Equal(t, false, rendered)

ts, _ = Compile(`{{ .Value }}`)
rendered, err = ts.RenderBool(map[string]any{
"Value": "not-a-bool",
})
assert.ErrorContains(t, err, "invalid syntax")
assert.Equal(t, false, rendered)

ts, _ = Compile(`{{ fail "boom" }}`)
rendered, err = ts.RenderBool(nil)
assert.ErrorContains(t, err, "fail: boom")
assert.Equal(t, false, rendered)
}

func TestTemplate_RenderInt(t *testing.T) {
ts, _ := Compile(`{{ .Value }}`)
rendered, err := ts.RenderInt(map[string]any{
"Value": "12",
})
assert.NoError(t, err)
assert.Equal(t, 12, rendered)

ts, _ = Compile(`{{ .Value }}`)
rendered, err = ts.RenderInt(nil)
assert.NoError(t, err)
assert.Equal(t, 0, rendered)

ts, _ = Compile(`{{ .Value }}`)
rendered, err = ts.RenderInt(map[string]any{
"Value": "not-a-number",
})
assert.ErrorContains(t, err, "unable to cast")
assert.Equal(t, 0, rendered)

ts, _ = Compile(`{{ fail "boom" }}`)
rendered, err = ts.RenderInt(nil)
assert.ErrorContains(t, err, "fail: boom")
assert.Equal(t, 0, rendered)
}

func TestTemplate_RenderRequired(t *testing.T) {
ts, _ := Compile(`{{ .Value }}`)
rendered, err := ts.RenderRequired(map[string]any{
"Value": "Howdy",
})
assert.NoError(t, err)
assert.Equal(t, "Howdy", rendered)

ts, _ = Compile(`{{ fail "boom" }}`)
rendered, err = ts.RenderRequired(nil)
assert.ErrorContains(t, err, "fail: boom")
assert.Equal(t, "", rendered)

ts, _ = Compile(`{{ .Value }}`)
rendered, err = ts.RenderRequired(map[string]any{
"Value": "",
})
assert.ErrorContains(t, err, "empty string")
assert.Equal(t, "", rendered)
}

func TestTemplate_MarshalText(t *testing.T) {
ts, err := Compile(`Hello, {{ .Name }}`)
assert.NoError(t, err)
Expand Down

0 comments on commit f66770a

Please sign in to comment.