Skip to content

Commit

Permalink
ExecFuncStringWithErr will not panick when f call returns error (#19)
Browse files Browse the repository at this point in the history
* ExecFuncWithErr will not panick when f call returns error

* fix comment name

* replace func body of ExecuteFuncString
  • Loading branch information
caibirdme authored and valyala committed Jan 28, 2020
1 parent 8fcc7a9 commit f2edffe
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 5 deletions.
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)
}
}

0 comments on commit f2edffe

Please sign in to comment.