Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExecFuncStringWithErr will not panick when f call returns error #19

Merged
merged 3 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int

ni, err = f(w, unsafeBytes2String(s[:n]))
nn += int64(ni)
if err != nil {
return nn, err
}
s = s[n+len(b):]
}
ni, err = w.Write(s)
Expand Down Expand Up @@ -81,19 +84,32 @@ func Execute(template, startTag, endTag string, w io.Writer, m map[string]interf
// This function is optimized for constantly changing templates.
// Use Template.ExecuteFuncString for frozen templates.
func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
if err != nil {
panic(fmt.Sprintf("unexpected error: %s", err))
}
return s
}

// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
// it just returns an empty string and the error f returned
func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
if tagsCount == 0 {
return template
return template, nil
}

bb := byteBufferPool.Get()
if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
panic(fmt.Sprintf("unexpected error: %s", err))
bb.Reset()
byteBufferPool.Put(bb)
return "", err
}
s := string(bb.B)
bb.Reset()
byteBufferPool.Put(bb)
return s
return s, nil
}

var byteBufferPool bytebufferpool.Pool
Expand Down Expand Up @@ -275,14 +291,31 @@ func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error)
// This function is optimized for frozen templates.
// Use ExecuteFuncString for constantly changing templates.
func (t *Template) ExecuteFuncString(f TagFunc) string {
s, err := t.ExecuteFuncStringWithErr(f)
if err != nil {
panic(fmt.Sprintf("unexpected error: %s", err))
}
return s
}

// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
// and substitutes it with the data written to TagFunc's w.
//
// Returns the resulting string.
//
// This function is optimized for frozen templates.
// Use ExecuteFuncString for constantly changing templates.
func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
bb := t.byteBufferPool.Get()
if _, err := t.ExecuteFunc(bb, f); err != nil {
panic(fmt.Sprintf("unexpected error: %s", err))
bb.Reset()
t.byteBufferPool.Put(bb)
return "", err
}
s := string(bb.Bytes())
bb.Reset()
t.byteBufferPool.Put(bb)
return s
return s, nil
}

// ExecuteString substitutes template tags (placeholders) with the corresponding
Expand Down
61 changes: 61 additions & 0 deletions template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fasttemplate

import (
"bytes"
"errors"
"io"
"testing"
)
Expand Down Expand Up @@ -315,3 +316,63 @@ func expectPanic(t *testing.T, f func()) {
}()
f()
}

func TestExecuteFuncStringWithErr(t *testing.T) {
var expectErr = errors.New("test111")
result, err := ExecuteFuncStringWithErr(`{a} is {b}'s best friend`, "{", "}", func(w io.Writer, tag string) (int, error) {
if tag == "a" {
return w.Write([]byte("Alice"))
}
return 0, expectErr
})
if err != expectErr {
t.Fatalf("error must be the same as the error returned from f, expect: %s, actual: %s", expectErr, err)
}
if result != "" {
t.Fatalf("result should be an empty string if error occurred")
}
result, err = ExecuteFuncStringWithErr(`{a} is {b}'s best friend`, "{", "}", func(w io.Writer, tag string) (int, error) {
if tag == "a" {
return w.Write([]byte("Alice"))
}
return w.Write([]byte("Bob"))
})
if err != nil {
t.Fatalf("should success but found err: %s", err)
}
if result != "Alice is Bob's best friend" {
t.Fatalf("expect: %s, but: %s", "Alice is Bob's best friend", result)
}
}

func TestTpl_ExecuteFuncStringWithErr(t *testing.T) {
var expectErr = errors.New("test111")
tpl, err := NewTemplate(`{a} is {b}'s best friend`, "{", "}")
if err != nil {
t.Fatalf("should success, but found err: %s", err)
}
result, err := tpl.ExecuteFuncStringWithErr(func(w io.Writer, tag string) (int, error) {
if tag == "a" {
return w.Write([]byte("Alice"))
}
return 0, expectErr
})
if err != expectErr {
t.Fatalf("error must be the same as the error returned from f, expect: %s, actual: %s", expectErr, err)
}
if result != "" {
t.Fatalf("result should be an empty string if error occurred")
}
result, err = tpl.ExecuteFuncStringWithErr(func(w io.Writer, tag string) (int, error) {
if tag == "a" {
return w.Write([]byte("Alice"))
}
return w.Write([]byte("Bob"))
})
if err != nil {
t.Fatalf("should success but found err: %s", err)
}
if result != "Alice is Bob's best friend" {
t.Fatalf("expect: %s, but: %s", "Alice is Bob's best friend", result)
}
}