template.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. package pongo2
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "strings"
  7. )
  8. type TemplateWriter interface {
  9. io.Writer
  10. WriteString(string) (int, error)
  11. }
  12. type templateWriter struct {
  13. w io.Writer
  14. }
  15. func (tw *templateWriter) WriteString(s string) (int, error) {
  16. return tw.w.Write([]byte(s))
  17. }
  18. func (tw *templateWriter) Write(b []byte) (int, error) {
  19. return tw.w.Write(b)
  20. }
  21. type Template struct {
  22. set *TemplateSet
  23. // Input
  24. isTplString bool
  25. name string
  26. tpl string
  27. size int
  28. // Calculation
  29. tokens []*Token
  30. parser *Parser
  31. // first come, first serve (it's important to not override existing entries in here)
  32. level int
  33. parent *Template
  34. child *Template
  35. blocks map[string]*NodeWrapper
  36. exportedMacros map[string]*tagMacroNode
  37. // Output
  38. root *nodeDocument
  39. // Options allow you to change the behavior of template-engine.
  40. // You can change the options before calling the Execute method.
  41. Options *Options
  42. }
  43. func newTemplateString(set *TemplateSet, tpl []byte) (*Template, error) {
  44. return newTemplate(set, "<string>", true, tpl)
  45. }
  46. func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*Template, error) {
  47. strTpl := string(tpl)
  48. // Create the template
  49. t := &Template{
  50. set: set,
  51. isTplString: isTplString,
  52. name: name,
  53. tpl: strTpl,
  54. size: len(strTpl),
  55. blocks: make(map[string]*NodeWrapper),
  56. exportedMacros: make(map[string]*tagMacroNode),
  57. Options: newOptions(),
  58. }
  59. // Copy all settings from another Options.
  60. t.Options.Update(set.Options)
  61. // Tokenize it
  62. tokens, err := lex(name, strTpl)
  63. if err != nil {
  64. return nil, err
  65. }
  66. t.tokens = tokens
  67. // For debugging purposes, show all tokens:
  68. /*for i, t := range tokens {
  69. fmt.Printf("%3d. %s\n", i, t)
  70. }*/
  71. // Parse it
  72. err = t.parse()
  73. if err != nil {
  74. return nil, err
  75. }
  76. return t, nil
  77. }
  78. func (tpl *Template) newContextForExecution(context Context) (*Template, *ExecutionContext, error) {
  79. if tpl.Options.TrimBlocks || tpl.Options.LStripBlocks {
  80. // If an application configures pongo2 template to trim_blocks,
  81. // the first newline after a template tag is removed automatically (like in PHP).
  82. prev := &Token{
  83. Typ: TokenHTML,
  84. Val: "\n",
  85. }
  86. for _, t := range tpl.tokens {
  87. if tpl.Options.LStripBlocks {
  88. if prev.Typ == TokenHTML && t.Typ != TokenHTML && t.Val == "{%" {
  89. prev.Val = strings.TrimRight(prev.Val, "\t ")
  90. }
  91. }
  92. if tpl.Options.TrimBlocks {
  93. if prev.Typ != TokenHTML && t.Typ == TokenHTML && prev.Val == "%}" {
  94. if len(t.Val) > 0 && t.Val[0] == '\n' {
  95. t.Val = t.Val[1:len(t.Val)]
  96. }
  97. }
  98. }
  99. prev = t
  100. }
  101. }
  102. // Determine the parent to be executed (for template inheritance)
  103. parent := tpl
  104. for parent.parent != nil {
  105. parent = parent.parent
  106. }
  107. // Create context if none is given
  108. newContext := make(Context)
  109. newContext.Update(tpl.set.Globals)
  110. if context != nil {
  111. newContext.Update(context)
  112. if len(newContext) > 0 {
  113. // Check for context name syntax
  114. err := newContext.checkForValidIdentifiers()
  115. if err != nil {
  116. return parent, nil, err
  117. }
  118. // Check for clashes with macro names
  119. for k := range newContext {
  120. _, has := tpl.exportedMacros[k]
  121. if has {
  122. return parent, nil, &Error{
  123. Filename: tpl.name,
  124. Sender: "execution",
  125. OrigError: fmt.Errorf("context key name '%s' clashes with macro '%s'", k, k),
  126. }
  127. }
  128. }
  129. }
  130. }
  131. // Create operational context
  132. ctx := newExecutionContext(parent, newContext)
  133. return parent, ctx, nil
  134. }
  135. func (tpl *Template) execute(context Context, writer TemplateWriter) error {
  136. parent, ctx, err := tpl.newContextForExecution(context)
  137. if err != nil {
  138. return err
  139. }
  140. // Run the selected document
  141. if err := parent.root.Execute(ctx, writer); err != nil {
  142. return err
  143. }
  144. return nil
  145. }
  146. func (tpl *Template) newTemplateWriterAndExecute(context Context, writer io.Writer) error {
  147. return tpl.execute(context, &templateWriter{w: writer})
  148. }
  149. func (tpl *Template) newBufferAndExecute(context Context) (*bytes.Buffer, error) {
  150. // Create output buffer
  151. // We assume that the rendered template will be 30% larger
  152. buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
  153. if err := tpl.execute(context, buffer); err != nil {
  154. return nil, err
  155. }
  156. return buffer, nil
  157. }
  158. // Executes the template with the given context and writes to writer (io.Writer)
  159. // on success. Context can be nil. Nothing is written on error; instead the error
  160. // is being returned.
  161. func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
  162. buf, err := tpl.newBufferAndExecute(context)
  163. if err != nil {
  164. return err
  165. }
  166. _, err = buf.WriteTo(writer)
  167. if err != nil {
  168. return err
  169. }
  170. return nil
  171. }
  172. // Same as ExecuteWriter. The only difference between both functions is that
  173. // this function might already have written parts of the generated template in the
  174. // case of an execution error because there's no intermediate buffer involved for
  175. // performance reasons. This is handy if you need high performance template
  176. // generation or if you want to manage your own pool of buffers.
  177. func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error {
  178. return tpl.newTemplateWriterAndExecute(context, writer)
  179. }
  180. // Executes the template and returns the rendered template as a []byte
  181. func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
  182. // Execute template
  183. buffer, err := tpl.newBufferAndExecute(context)
  184. if err != nil {
  185. return nil, err
  186. }
  187. return buffer.Bytes(), nil
  188. }
  189. // Executes the template and returns the rendered template as a string
  190. func (tpl *Template) Execute(context Context) (string, error) {
  191. // Execute template
  192. buffer, err := tpl.newBufferAndExecute(context)
  193. if err != nil {
  194. return "", err
  195. }
  196. return buffer.String(), nil
  197. }
  198. func (tpl *Template) ExecuteBlocks(context Context, blocks []string) (map[string]string, error) {
  199. var parents []*Template
  200. result := make(map[string]string)
  201. parent := tpl
  202. for parent != nil {
  203. parents = append(parents, parent)
  204. parent = parent.parent
  205. }
  206. for _, t := range parents {
  207. buffer := bytes.NewBuffer(make([]byte, 0, int(float64(t.size)*1.3)))
  208. _, ctx, err := t.newContextForExecution(context)
  209. if err != nil {
  210. return nil, err
  211. }
  212. for _, blockName := range blocks {
  213. if _, ok := result[blockName]; ok {
  214. continue
  215. }
  216. if blockWrapper, ok := t.blocks[blockName]; ok {
  217. bErr := blockWrapper.Execute(ctx, buffer)
  218. if bErr != nil {
  219. return nil, bErr
  220. }
  221. result[blockName] = buffer.String()
  222. buffer.Reset()
  223. }
  224. }
  225. // We have found all blocks
  226. if len(blocks) == len(result) {
  227. break
  228. }
  229. }
  230. return result, nil
  231. }