1 // Package fasttemplate implements simple and fast template library.
3 // Fasttemplate is faster than text/template, strings.Replace
4 // and strings.Replacer.
6 // Fasttemplate ideally fits for fast and simple placeholders' substitutions.
12 "github.com/valyala/bytebufferpool"
16 // ExecuteFunc calls f on each template tag (placeholder) occurrence.
18 // Returns the number of bytes written to w.
20 // This function is optimized for constantly changing templates.
21 // Use Template.ExecuteFunc for frozen templates.
22 func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {
23 s := unsafeString2Bytes(template)
24 a := unsafeString2Bytes(startTag)
25 b := unsafeString2Bytes(endTag)
31 n := bytes.Index(s, a)
35 ni, err = w.Write(s[:n])
44 // cannot find end tag - just write it to the output.
50 ni, err = f(w, unsafeBytes2String(s[:n]))
60 // Execute substitutes template tags (placeholders) with the corresponding
61 // values from the map m and writes the result to the given writer w.
63 // Substitution map m may contain values with the following types:
64 // * []byte - the fastest value type
65 // * string - convenient value type
66 // * TagFunc - flexible value type
68 // Returns the number of bytes written to w.
70 // This function is optimized for constantly changing templates.
71 // Use Template.Execute for frozen templates.
72 func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
73 return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
76 // ExecuteFuncString calls f on each template tag (placeholder) occurrence
77 // and substitutes it with the data written to TagFunc's w.
79 // Returns the resulting string.
81 // This function is optimized for constantly changing templates.
82 // Use Template.ExecuteFuncString for frozen templates.
83 func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
84 tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
89 bb := byteBufferPool.Get()
90 if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
91 panic(fmt.Sprintf("unexpected error: %s", err))
95 byteBufferPool.Put(bb)
99 var byteBufferPool bytebufferpool.Pool
101 // ExecuteString substitutes template tags (placeholders) with the corresponding
102 // values from the map m and returns the result.
104 // Substitution map m may contain values with the following types:
105 // * []byte - the fastest value type
106 // * string - convenient value type
107 // * TagFunc - flexible value type
109 // This function is optimized for constantly changing templates.
110 // Use Template.ExecuteString for frozen templates.
111 func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {
112 return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
115 // Template implements simple template engine, which can be used for fast
116 // tags' (aka placeholders) substitution.
117 type Template struct {
124 byteBufferPool bytebufferpool.Pool
127 // New parses the given template using the given startTag and endTag
128 // as tag start and tag end.
130 // The returned template can be executed by concurrently running goroutines
131 // using Execute* methods.
133 // New panics if the given template cannot be parsed. Use NewTemplate instead
134 // if template may contain errors.
135 func New(template, startTag, endTag string) *Template {
136 t, err := NewTemplate(template, startTag, endTag)
143 // NewTemplate parses the given template using the given startTag and endTag
144 // as tag start and tag end.
146 // The returned template can be executed by concurrently running goroutines
147 // using Execute* methods.
148 func NewTemplate(template, startTag, endTag string) (*Template, error) {
150 err := t.Reset(template, startTag, endTag)
157 // TagFunc can be used as a substitution value in the map passed to Execute*.
158 // Execute* functions pass tag (placeholder) name in 'tag' argument.
160 // TagFunc must be safe to call from concurrently running goroutines.
162 // TagFunc must write contents to w and return the number of bytes written.
163 type TagFunc func(w io.Writer, tag string) (int, error)
165 // Reset resets the template t to new one defined by
166 // template, startTag and endTag.
168 // Reset allows Template object re-use.
170 // Reset may be called only if no other goroutines call t methods at the moment.
171 func (t *Template) Reset(template, startTag, endTag string) error {
172 // Keep these vars in t, so GC won't collect them and won't break
173 // vars derived via unsafe*
174 t.template = template
175 t.startTag = startTag
177 t.texts = t.texts[:0]
180 if len(startTag) == 0 {
181 panic("startTag cannot be empty")
183 if len(endTag) == 0 {
184 panic("endTag cannot be empty")
187 s := unsafeString2Bytes(template)
188 a := unsafeString2Bytes(startTag)
189 b := unsafeString2Bytes(endTag)
191 tagsCount := bytes.Count(s, a)
196 if tagsCount+1 > cap(t.texts) {
197 t.texts = make([][]byte, 0, tagsCount+1)
199 if tagsCount > cap(t.tags) {
200 t.tags = make([]string, 0, tagsCount)
204 n := bytes.Index(s, a)
206 t.texts = append(t.texts, s)
209 t.texts = append(t.texts, s[:n])
212 n = bytes.Index(s, b)
214 return fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
217 t.tags = append(t.tags, unsafeBytes2String(s[:n]))
224 // ExecuteFunc calls f on each template tag (placeholder) occurrence.
226 // Returns the number of bytes written to w.
228 // This function is optimized for frozen templates.
229 // Use ExecuteFunc for constantly changing templates.
230 func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
233 n := len(t.texts) - 1
235 ni, err := w.Write(unsafeString2Bytes(t.template))
236 return int64(ni), err
239 for i := 0; i < n; i++ {
240 ni, err := w.Write(t.texts[i])
246 ni, err = f(w, t.tags[i])
252 ni, err := w.Write(t.texts[n])
257 // Execute substitutes template tags (placeholders) with the corresponding
258 // values from the map m and writes the result to the given writer w.
260 // Substitution map m may contain values with the following types:
261 // * []byte - the fastest value type
262 // * string - convenient value type
263 // * TagFunc - flexible value type
265 // Returns the number of bytes written to w.
266 func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
267 return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
270 // ExecuteFuncString calls f on each template tag (placeholder) occurrence
271 // and substitutes it with the data written to TagFunc's w.
273 // Returns the resulting string.
275 // This function is optimized for frozen templates.
276 // Use ExecuteFuncString for constantly changing templates.
277 func (t *Template) ExecuteFuncString(f TagFunc) string {
278 bb := t.byteBufferPool.Get()
279 if _, err := t.ExecuteFunc(bb, f); err != nil {
280 panic(fmt.Sprintf("unexpected error: %s", err))
282 s := string(bb.Bytes())
284 t.byteBufferPool.Put(bb)
288 // ExecuteString substitutes template tags (placeholders) with the corresponding
289 // values from the map m and returns the result.
291 // Substitution map m may contain values with the following types:
292 // * []byte - the fastest value type
293 // * string - convenient value type
294 // * TagFunc - flexible value type
296 // This function is optimized for frozen templates.
297 // Use ExecuteString for constantly changing templates.
298 func (t *Template) ExecuteString(m map[string]interface{}) string {
299 return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
302 func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
307 switch value := v.(type) {
309 return w.Write(value)
311 return w.Write([]byte(value))
315 panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))