template.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. "github.com/valyala/bytebufferpool"
  12. "io"
  13. )
  14. // ExecuteFunc calls f on each template tag (placeholder) occurrence.
  15. //
  16. // Returns the number of bytes written to w.
  17. //
  18. // This function is optimized for constantly changing templates.
  19. // Use Template.ExecuteFunc for frozen templates.
  20. func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {
  21. s := unsafeString2Bytes(template)
  22. a := unsafeString2Bytes(startTag)
  23. b := unsafeString2Bytes(endTag)
  24. var nn int64
  25. var ni int
  26. var err error
  27. for {
  28. n := bytes.Index(s, a)
  29. if n < 0 {
  30. break
  31. }
  32. ni, err = w.Write(s[:n])
  33. nn += int64(ni)
  34. if err != nil {
  35. return nn, err
  36. }
  37. s = s[n+len(a):]
  38. n = bytes.Index(s, b)
  39. if n < 0 {
  40. // cannot find end tag - just write it to the output.
  41. ni, _ = w.Write(a)
  42. nn += int64(ni)
  43. break
  44. }
  45. ni, err = f(w, unsafeBytes2String(s[:n]))
  46. nn += int64(ni)
  47. if err != nil {
  48. return nn, err
  49. }
  50. s = s[n+len(b):]
  51. }
  52. ni, err = w.Write(s)
  53. nn += int64(ni)
  54. return nn, err
  55. }
  56. // Execute substitutes template tags (placeholders) with the corresponding
  57. // values from the map m and writes the result to the given writer w.
  58. //
  59. // Substitution map m may contain values with the following types:
  60. // * []byte - the fastest value type
  61. // * string - convenient value type
  62. // * TagFunc - flexible value type
  63. //
  64. // Returns the number of bytes written to w.
  65. //
  66. // This function is optimized for constantly changing templates.
  67. // Use Template.Execute for frozen templates.
  68. func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
  69. return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  70. }
  71. // ExecuteFuncString calls f on each template tag (placeholder) occurrence
  72. // and substitutes it with the data written to TagFunc's w.
  73. //
  74. // Returns the resulting string.
  75. //
  76. // This function is optimized for constantly changing templates.
  77. // Use Template.ExecuteFuncString for frozen templates.
  78. func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
  79. s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
  80. if err != nil {
  81. panic(fmt.Sprintf("unexpected error: %s", err))
  82. }
  83. return s
  84. }
  85. // ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
  86. // but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
  87. // it just returns an empty string and the error f returned
  88. func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
  89. tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
  90. if tagsCount == 0 {
  91. return template, nil
  92. }
  93. bb := byteBufferPool.Get()
  94. if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
  95. bb.Reset()
  96. byteBufferPool.Put(bb)
  97. return "", err
  98. }
  99. s := string(bb.B)
  100. bb.Reset()
  101. byteBufferPool.Put(bb)
  102. return s, nil
  103. }
  104. var byteBufferPool bytebufferpool.Pool
  105. // ExecuteString substitutes template tags (placeholders) with the corresponding
  106. // values from the map m and returns the result.
  107. //
  108. // Substitution map m may contain values with the following types:
  109. // * []byte - the fastest value type
  110. // * string - convenient value type
  111. // * TagFunc - flexible value type
  112. //
  113. // This function is optimized for constantly changing templates.
  114. // Use Template.ExecuteString for frozen templates.
  115. func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {
  116. return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  117. }
  118. // Template implements simple template engine, which can be used for fast
  119. // tags' (aka placeholders) substitution.
  120. type Template struct {
  121. template string
  122. startTag string
  123. endTag string
  124. texts [][]byte
  125. tags []string
  126. byteBufferPool bytebufferpool.Pool
  127. }
  128. // New parses the given template using the given startTag and endTag
  129. // as tag start and tag end.
  130. //
  131. // The returned template can be executed by concurrently running goroutines
  132. // using Execute* methods.
  133. //
  134. // New panics if the given template cannot be parsed. Use NewTemplate instead
  135. // if template may contain errors.
  136. func New(template, startTag, endTag string) *Template {
  137. t, err := NewTemplate(template, startTag, endTag)
  138. if err != nil {
  139. panic(err)
  140. }
  141. return t
  142. }
  143. // NewTemplate parses the given template using the given startTag and endTag
  144. // as tag start and tag end.
  145. //
  146. // The returned template can be executed by concurrently running goroutines
  147. // using Execute* methods.
  148. func NewTemplate(template, startTag, endTag string) (*Template, error) {
  149. var t Template
  150. err := t.Reset(template, startTag, endTag)
  151. if err != nil {
  152. return nil, err
  153. }
  154. return &t, nil
  155. }
  156. // TagFunc can be used as a substitution value in the map passed to Execute*.
  157. // Execute* functions pass tag (placeholder) name in 'tag' argument.
  158. //
  159. // TagFunc must be safe to call from concurrently running goroutines.
  160. //
  161. // TagFunc must write contents to w and return the number of bytes written.
  162. type TagFunc func(w io.Writer, tag string) (int, error)
  163. // Reset resets the template t to new one defined by
  164. // template, startTag and endTag.
  165. //
  166. // Reset allows Template object re-use.
  167. //
  168. // Reset may be called only if no other goroutines call t methods at the moment.
  169. func (t *Template) Reset(template, startTag, endTag string) error {
  170. // Keep these vars in t, so GC won't collect them and won't break
  171. // vars derived via unsafe*
  172. t.template = template
  173. t.startTag = startTag
  174. t.endTag = endTag
  175. t.texts = t.texts[:0]
  176. t.tags = t.tags[:0]
  177. if len(startTag) == 0 {
  178. panic("startTag cannot be empty")
  179. }
  180. if len(endTag) == 0 {
  181. panic("endTag cannot be empty")
  182. }
  183. s := unsafeString2Bytes(template)
  184. a := unsafeString2Bytes(startTag)
  185. b := unsafeString2Bytes(endTag)
  186. tagsCount := bytes.Count(s, a)
  187. if tagsCount == 0 {
  188. return nil
  189. }
  190. if tagsCount+1 > cap(t.texts) {
  191. t.texts = make([][]byte, 0, tagsCount+1)
  192. }
  193. if tagsCount > cap(t.tags) {
  194. t.tags = make([]string, 0, tagsCount)
  195. }
  196. for {
  197. n := bytes.Index(s, a)
  198. if n < 0 {
  199. t.texts = append(t.texts, s)
  200. break
  201. }
  202. t.texts = append(t.texts, s[:n])
  203. s = s[n+len(a):]
  204. n = bytes.Index(s, b)
  205. if n < 0 {
  206. return fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
  207. }
  208. t.tags = append(t.tags, unsafeBytes2String(s[:n]))
  209. s = s[n+len(b):]
  210. }
  211. return nil
  212. }
  213. // ExecuteFunc calls f on each template tag (placeholder) occurrence.
  214. //
  215. // Returns the number of bytes written to w.
  216. //
  217. // This function is optimized for frozen templates.
  218. // Use ExecuteFunc for constantly changing templates.
  219. func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
  220. var nn int64
  221. n := len(t.texts) - 1
  222. if n == -1 {
  223. ni, err := w.Write(unsafeString2Bytes(t.template))
  224. return int64(ni), err
  225. }
  226. for i := 0; i < n; i++ {
  227. ni, err := w.Write(t.texts[i])
  228. nn += int64(ni)
  229. if err != nil {
  230. return nn, err
  231. }
  232. ni, err = f(w, t.tags[i])
  233. nn += int64(ni)
  234. if err != nil {
  235. return nn, err
  236. }
  237. }
  238. ni, err := w.Write(t.texts[n])
  239. nn += int64(ni)
  240. return nn, err
  241. }
  242. // Execute substitutes template tags (placeholders) with the corresponding
  243. // values from the map m and writes the result to the given writer w.
  244. //
  245. // Substitution map m may contain values with the following types:
  246. // * []byte - the fastest value type
  247. // * string - convenient value type
  248. // * TagFunc - flexible value type
  249. //
  250. // Returns the number of bytes written to w.
  251. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
  252. return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  253. }
  254. // ExecuteFuncString calls f on each template tag (placeholder) occurrence
  255. // and substitutes it with the data written to TagFunc's w.
  256. //
  257. // Returns the resulting string.
  258. //
  259. // This function is optimized for frozen templates.
  260. // Use ExecuteFuncString for constantly changing templates.
  261. func (t *Template) ExecuteFuncString(f TagFunc) string {
  262. s, err := t.ExecuteFuncStringWithErr(f)
  263. if err != nil {
  264. panic(fmt.Sprintf("unexpected error: %s", err))
  265. }
  266. return s
  267. }
  268. // ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
  269. // and substitutes it with the data written to TagFunc's w.
  270. //
  271. // Returns the resulting string.
  272. //
  273. // This function is optimized for frozen templates.
  274. // Use ExecuteFuncString for constantly changing templates.
  275. func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
  276. bb := t.byteBufferPool.Get()
  277. if _, err := t.ExecuteFunc(bb, f); err != nil {
  278. bb.Reset()
  279. t.byteBufferPool.Put(bb)
  280. return "", err
  281. }
  282. s := string(bb.Bytes())
  283. bb.Reset()
  284. t.byteBufferPool.Put(bb)
  285. return s, nil
  286. }
  287. // ExecuteString substitutes template tags (placeholders) with the corresponding
  288. // values from the map m and returns the result.
  289. //
  290. // Substitution map m may contain values with the following types:
  291. // * []byte - the fastest value type
  292. // * string - convenient value type
  293. // * TagFunc - flexible value type
  294. //
  295. // This function is optimized for frozen templates.
  296. // Use ExecuteString for constantly changing templates.
  297. func (t *Template) ExecuteString(m map[string]interface{}) string {
  298. return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  299. }
  300. func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
  301. v := m[tag]
  302. if v == nil {
  303. return 0, nil
  304. }
  305. switch value := v.(type) {
  306. case []byte:
  307. return w.Write(value)
  308. case string:
  309. return w.Write([]byte(value))
  310. case TagFunc:
  311. return value(w, tag)
  312. default:
  313. panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
  314. }
  315. }