| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package fasttemplate
- import (
- "bytes"
- "fmt"
- "io"
- "sync"
- )
- // Template implements simple template engine, which can be used for fast
- // tags (aka placeholders) substitution.
- type Template struct {
- texts [][]byte
- tags []string
- bytesBufferPool sync.Pool
- }
- // NewTemplate parses the given template using the given startTag and endTag
- // as tag start and tag end.
- //
- // The returned template can be executed by concurrently running goroutines
- // using Execute* methods.
- func NewTemplate(template, startTag, endTag string) (*Template, error) {
- var t Template
- if len(startTag) == 0 {
- panic("startTag cannot be empty")
- }
- if len(endTag) == 0 {
- panic("endTag cannot be empty")
- }
- s := []byte(template)
- a := []byte(startTag)
- b := []byte(endTag)
- for {
- n := bytes.Index(s, a)
- if n < 0 {
- t.texts = append(t.texts, s)
- break
- }
- t.texts = append(t.texts, s[:n])
- s = s[n+len(a):]
- n = bytes.Index(s, b)
- if n < 0 {
- return nil, fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
- }
- t.tags = append(t.tags, string(s[:n]))
- s = s[n+len(b):]
- }
- return &t, nil
- }
- // TagFunc can be used as substitution value in the map passed to Execute*.
- //
- // It must write contents to w and return the number of bytes written.
- type TagFunc func(w io.Writer) (int, error)
- // Execute substitutes template tags (placeholders) with the corresponding
- // values from the map m and writes the result to the given writer w.
- //
- // Substitution map m may contain values with the following types:
- // * []byte - the fastest value type
- // * string - convenient value type
- // * TagFunc - flexible value type
- //
- // Returns the number of bytes written to w.
- func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
- var nn int64
- n := len(t.texts) - 1
- for i := 0; i < n; i++ {
- ni, err := w.Write(t.texts[i])
- if err != nil {
- return nn, err
- }
- nn += int64(ni)
- k := t.tags[i]
- v := m[k]
- if v == nil {
- continue
- }
- switch value := v.(type) {
- case []byte:
- ni, err = w.Write(value)
- case string:
- ni, err = w.Write([]byte(value))
- case TagFunc:
- ni, err = value(w)
- default:
- panic(fmt.Sprintf("key=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", k, v))
- }
- if err != nil {
- return nn, err
- }
- nn += int64(ni)
- }
- ni, err := w.Write(t.texts[n])
- if err != nil {
- return nn, err
- }
- nn += int64(ni)
- return nn, nil
- }
- // ExecuteString substitutes template tags (placeholders) with the corresponding
- // values from the map m and returns the result.
- //
- // Substitution map m may contain values with the following types:
- // * []byte - the fastest value type
- // * string - convenient value type
- // * TagFunc - flexible value type
- //
- func (t *Template) ExecuteString(m map[string]interface{}) string {
- var w *bytes.Buffer
- wv := t.bytesBufferPool.Get()
- if wv == nil {
- w = &bytes.Buffer{}
- } else {
- w = wv.(*bytes.Buffer)
- }
- _, err := t.Execute(w, m)
- if err != nil {
- panic(fmt.Sprintf("unexpected error: %s", err))
- }
- s := string(w.Bytes())
- w.Reset()
- t.bytesBufferPool.Put(w)
- return s
- }
|