فهرست منبع

ExecFuncStringWithErr will not panick when f call returns error (#19)

* ExecFuncWithErr will not panick when f call returns error

* fix comment name

* replace func body of ExecuteFuncString
Deen 5 سال پیش
والد
کامیت
f2edffee06
2فایلهای تغییر یافته به همراه99 افزوده شده و 5 حذف شده
  1. 38 5
      template.go
  2. 61 0
      template_test.go

+ 38 - 5
template.go

@@ -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)
@@ -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
@@ -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

+ 61 - 0
template_test.go

@@ -2,6 +2,7 @@ package fasttemplate
 
 import (
 	"bytes"
+	"errors"
 	"io"
 	"testing"
 )
@@ -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)
+	}
+}