123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package pongo2
- import (
- "bytes"
- "fmt"
- "io"
- )
- type Template struct {
- set *TemplateSet
- // Input
- is_tpl_string bool
- name string
- tpl string
- size int
- // Calculation
- tokens []*Token
- parser *Parser
- // first come, first serve (it's important to not override existing entries in here)
- level int
- parent *Template
- child *Template
- blocks map[string]*NodeWrapper
- exported_macros map[string]*tagMacroNode
- // Output
- root *nodeDocument
- }
- func newTemplateString(set *TemplateSet, tpl string) (*Template, error) {
- return newTemplate(set, "<string>", true, tpl)
- }
- func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) (*Template, error) {
- // Create the template
- t := &Template{
- set: set,
- is_tpl_string: is_tpl_string,
- name: name,
- tpl: tpl,
- size: len(tpl),
- blocks: make(map[string]*NodeWrapper),
- exported_macros: make(map[string]*tagMacroNode),
- }
- // Tokenize it
- tokens, err := lex(name, tpl)
- if err != nil {
- return nil, err
- }
- t.tokens = tokens
- // For debugging purposes, show all tokens:
- /*for i, t := range tokens {
- fmt.Printf("%3d. %s\n", i, t)
- }*/
- // Parse it
- err = t.parse()
- if err != nil {
- return nil, err
- }
- return t, nil
- }
- func (tpl *Template) execute(context Context) (*bytes.Buffer, error) {
- // Create output buffer
- // We assume that the rendered template will be 30% larger
- buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
- // Determine the parent to be executed (for template inheritance)
- parent := tpl
- for parent.parent != nil {
- parent = parent.parent
- }
- // Create context if none is given
- newContext := make(Context)
- newContext.Update(tpl.set.Globals)
- if context != nil {
- newContext.Update(context)
- if len(newContext) > 0 {
- // Check for context name syntax
- err := newContext.checkForValidIdentifiers()
- if err != nil {
- return nil, err
- }
- // Check for clashes with macro names
- for k, _ := range newContext {
- _, has := tpl.exported_macros[k]
- if has {
- return nil, &Error{
- Filename: tpl.name,
- Sender: "execution",
- ErrorMsg: fmt.Sprintf("Context key name '%s' clashes with macro '%s'.", k, k),
- }
- }
- }
- }
- }
- // Create operational context
- ctx := newExecutionContext(parent, newContext)
- // Run the selected document
- err := parent.root.Execute(ctx, buffer)
- if err != nil {
- return nil, err
- }
- return buffer, nil
- }
- // Executes the template with the given context and writes to writer (io.Writer)
- // on success. Context can be nil. Nothing is written on error; instead the error
- // is being returned.
- func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
- buffer, err := tpl.execute(context)
- if err != nil {
- return err
- }
- l := buffer.Len()
- n, werr := buffer.WriteTo(writer)
- if int(n) != l {
- panic(fmt.Sprintf("error on writing template: n(%d) != buffer.Len(%d)", n, l))
- }
- if werr != nil {
- return &Error{
- Filename: tpl.name,
- Sender: "execution",
- ErrorMsg: werr.Error(),
- }
- }
- return nil
- }
- // Executes the template and returns the rendered template as a []byte
- func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
- // Execute template
- buffer, err := tpl.execute(context)
- if err != nil {
- return nil, err
- }
- return buffer.Bytes(), nil
- }
- // Executes the template and returns the rendered template as a string
- func (tpl *Template) Execute(context Context) (string, error) {
- // Execute template
- buffer, err := tpl.execute(context)
- if err != nil {
- return "", err
- }
- return buffer.String(), nil
- }
|