template.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Package fasttemplate implements simple and fast template library.
  2. //
  3. // Fasttemplate is faster than text/template, strings.Replace
  4. // and strings.Replacer.
  5. //
  6. // Fasttemplate ideally fits for fast and simple placeholders' substitutions.
  7. package fasttemplate
  8. import (
  9. "bytes"
  10. "fmt"
  11. "io"
  12. "sync"
  13. )
  14. // Template implements simple template engine, which can be used for fast
  15. // tags' (aka placeholders) substitution.
  16. type Template struct {
  17. texts [][]byte
  18. tags []string
  19. bytesBufferPool sync.Pool
  20. }
  21. // New parses the given template using the given startTag and endTag
  22. // as tag start and tag end.
  23. //
  24. // The returned template can be executed by concurrently running goroutines
  25. // using Execute* methods.
  26. //
  27. // New panics if the given template cannot be parsed. Use NewTemplate instead
  28. // if template may contain errors.
  29. func New(template, startTag, endTag string) *Template {
  30. t, err := NewTemplate(template, startTag, endTag)
  31. if err != nil {
  32. panic(err)
  33. }
  34. return t
  35. }
  36. // NewTemplate parses the given template using the given startTag and endTag
  37. // as tag start and tag end.
  38. //
  39. // The returned template can be executed by concurrently running goroutines
  40. // using Execute* methods.
  41. func NewTemplate(template, startTag, endTag string) (*Template, error) {
  42. var t Template
  43. if len(startTag) == 0 {
  44. panic("startTag cannot be empty")
  45. }
  46. if len(endTag) == 0 {
  47. panic("endTag cannot be empty")
  48. }
  49. s := []byte(template)
  50. a := []byte(startTag)
  51. b := []byte(endTag)
  52. for {
  53. n := bytes.Index(s, a)
  54. if n < 0 {
  55. t.texts = append(t.texts, s)
  56. break
  57. }
  58. t.texts = append(t.texts, s[:n])
  59. s = s[n+len(a):]
  60. n = bytes.Index(s, b)
  61. if n < 0 {
  62. return nil, fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
  63. }
  64. t.tags = append(t.tags, string(s[:n]))
  65. s = s[n+len(b):]
  66. }
  67. t.bytesBufferPool.New = newBytesBuffer
  68. return &t, nil
  69. }
  70. func newBytesBuffer() interface{} {
  71. return &bytes.Buffer{}
  72. }
  73. // TagFunc can be used as a substitution value in the map passed to Execute*.
  74. // Execute* functions pass tag (placeholder) name in 'tag' argument.
  75. //
  76. // TagFunc must be safe to call from concurrently running goroutines.
  77. //
  78. // TagFunc must write contents to w and return the number of bytes written.
  79. type TagFunc func(w io.Writer, tag string) (int, error)
  80. // ExecuteFunc calls f on each template tag (placeholder) occurence.
  81. //
  82. // Returns the number of bytes written to w.
  83. func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
  84. var nn int64
  85. n := len(t.texts) - 1
  86. for i := 0; i < n; i++ {
  87. ni, err := w.Write(t.texts[i])
  88. if err != nil {
  89. return nn, err
  90. }
  91. nn += int64(ni)
  92. if ni, err = f(w, t.tags[i]); err != nil {
  93. return nn, err
  94. }
  95. nn += int64(ni)
  96. }
  97. ni, err := w.Write(t.texts[n])
  98. if err != nil {
  99. return nn, err
  100. }
  101. nn += int64(ni)
  102. return nn, nil
  103. }
  104. // Execute substitutes template tags (placeholders) with the corresponding
  105. // values from the map m and writes the result to the given writer w.
  106. //
  107. // Substitution map m may contain values with the following types:
  108. // * []byte - the fastest value type
  109. // * string - convenient value type
  110. // * TagFunc - flexible value type
  111. //
  112. // Returns the number of bytes written to w.
  113. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
  114. return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  115. }
  116. // ExecuteFuncString call f on each template tag (placeholder) occurence
  117. // and substitutes it with the data written to TagFunc's w.
  118. //
  119. // Returns the resulting string.
  120. func (t *Template) ExecuteFuncString(f TagFunc) string {
  121. w := t.bytesBufferPool.Get().(*bytes.Buffer)
  122. if _, err := t.ExecuteFunc(w, f); err != nil {
  123. panic(fmt.Sprintf("unexpected error: %s", err))
  124. }
  125. s := string(w.Bytes())
  126. w.Reset()
  127. t.bytesBufferPool.Put(w)
  128. return s
  129. }
  130. // ExecuteString substitutes template tags (placeholders) with the corresponding
  131. // values from the map m and returns the result.
  132. //
  133. // Substitution map m may contain values with the following types:
  134. // * []byte - the fastest value type
  135. // * string - convenient value type
  136. // * TagFunc - flexible value type
  137. //
  138. func (t *Template) ExecuteString(m map[string]interface{}) string {
  139. return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  140. }
  141. func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
  142. v := m[tag]
  143. if v == nil {
  144. return 0, nil
  145. }
  146. switch value := v.(type) {
  147. case []byte:
  148. return w.Write(value)
  149. case string:
  150. return w.Write([]byte(value))
  151. case TagFunc:
  152. return value(w, tag)
  153. default:
  154. panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
  155. }
  156. }