template.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package fasttemplate
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "sync"
  7. )
  8. // Template implements simple template engine, which can be used for fast
  9. // tags (aka placeholders) substitution.
  10. type Template struct {
  11. texts [][]byte
  12. tags []string
  13. bytesBufferPool sync.Pool
  14. }
  15. // NewTemplate parses the given template using the given startTag and endTag
  16. // as tag start and tag end.
  17. //
  18. // The returned template can be executed by concurrently running goroutines
  19. // using Execute* methods.
  20. func NewTemplate(template, startTag, endTag string) (*Template, error) {
  21. var t Template
  22. if len(startTag) == 0 {
  23. panic("startTag cannot be empty")
  24. }
  25. if len(endTag) == 0 {
  26. panic("endTag cannot be empty")
  27. }
  28. s := []byte(template)
  29. a := []byte(startTag)
  30. b := []byte(endTag)
  31. for {
  32. n := bytes.Index(s, a)
  33. if n < 0 {
  34. t.texts = append(t.texts, s)
  35. break
  36. }
  37. t.texts = append(t.texts, s[:n])
  38. s = s[n+len(a):]
  39. n = bytes.Index(s, b)
  40. if n < 0 {
  41. return nil, fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
  42. }
  43. t.tags = append(t.tags, string(s[:n]))
  44. s = s[n+len(b):]
  45. }
  46. return &t, nil
  47. }
  48. // TagFunc can be used as substitution value in the map passed to Execute*.
  49. //
  50. // It must write contents to w and return the number of bytes written.
  51. type TagFunc func(w io.Writer) (int, error)
  52. // Execute substitutes template tags (placeholders) with the corresponding
  53. // values from the map m and writes the result to the given writer w.
  54. //
  55. // Substitution map m may contain values with the following types:
  56. // * []byte - the fastest value type
  57. // * string - convenient value type
  58. // * TagFunc - flexible value type
  59. //
  60. // Returns the number of bytes written to w.
  61. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
  62. var nn int64
  63. n := len(t.texts) - 1
  64. for i := 0; i < n; i++ {
  65. ni, err := w.Write(t.texts[i])
  66. if err != nil {
  67. return nn, err
  68. }
  69. nn += int64(ni)
  70. k := t.tags[i]
  71. v := m[k]
  72. if v == nil {
  73. continue
  74. }
  75. switch value := v.(type) {
  76. case []byte:
  77. ni, err = w.Write(value)
  78. case string:
  79. ni, err = w.Write([]byte(value))
  80. case TagFunc:
  81. ni, err = value(w)
  82. default:
  83. panic(fmt.Sprintf("key=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", k, v))
  84. }
  85. if err != nil {
  86. return nn, err
  87. }
  88. nn += int64(ni)
  89. }
  90. ni, err := w.Write(t.texts[n])
  91. if err != nil {
  92. return nn, err
  93. }
  94. nn += int64(ni)
  95. return nn, nil
  96. }
  97. // ExecuteString substitutes template tags (placeholders) with the corresponding
  98. // values from the map m and returns the result.
  99. //
  100. // Substitution map m may contain values with the following types:
  101. // * []byte - the fastest value type
  102. // * string - convenient value type
  103. // * TagFunc - flexible value type
  104. //
  105. func (t *Template) ExecuteString(m map[string]interface{}) string {
  106. var w *bytes.Buffer
  107. wv := t.bytesBufferPool.Get()
  108. if wv == nil {
  109. w = &bytes.Buffer{}
  110. } else {
  111. w = wv.(*bytes.Buffer)
  112. }
  113. _, err := t.Execute(w, m)
  114. if err != nil {
  115. panic(fmt.Sprintf("unexpected error: %s", err))
  116. }
  117. s := string(w.Bytes())
  118. w.Reset()
  119. t.bytesBufferPool.Put(w)
  120. return s
  121. }