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 1 commit
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
44 changes: 44 additions & 0 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 @@ -96,6 +99,27 @@ func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
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, nil
}

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

var byteBufferPool bytebufferpool.Pool

// ExecuteString substitutes template tags (placeholders) with the corresponding
Expand Down Expand Up @@ -285,6 +309,26 @@ func (t *Template) ExecuteFuncString(f TagFunc) string {
return s
}

// ExecuteFuncString calls f on each template tag (placeholder) occurrence
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExecuteFuncString -> ExecuteFuncStringWithErr

// 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 {
bb.Reset()
t.byteBufferPool.Put(bb)
return "", err
}
s := string(bb.Bytes())
bb.Reset()
t.byteBufferPool.Put(bb)
return s, nil
}

// ExecuteString substitutes template tags (placeholders) with the corresponding
// values from the map m and returns the result.
//
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)
}
}