github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/flosch/pongo2.v3/template.go (about)

     1  package pongo2
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  )
     8  
     9  type Template struct {
    10  	set *TemplateSet
    11  
    12  	// Input
    13  	is_tpl_string bool
    14  	name          string
    15  	tpl           string
    16  	size          int
    17  
    18  	// Calculation
    19  	tokens []*Token
    20  	parser *Parser
    21  
    22  	// first come, first serve (it's important to not override existing entries in here)
    23  	level           int
    24  	parent          *Template
    25  	child           *Template
    26  	blocks          map[string]*NodeWrapper
    27  	exported_macros map[string]*tagMacroNode
    28  
    29  	// Output
    30  	root *nodeDocument
    31  }
    32  
    33  func newTemplateString(set *TemplateSet, tpl string) (*Template, error) {
    34  	return newTemplate(set, "<string>", true, tpl)
    35  }
    36  
    37  func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) (*Template, error) {
    38  	// Create the template
    39  	t := &Template{
    40  		set:             set,
    41  		is_tpl_string:   is_tpl_string,
    42  		name:            name,
    43  		tpl:             tpl,
    44  		size:            len(tpl),
    45  		blocks:          make(map[string]*NodeWrapper),
    46  		exported_macros: make(map[string]*tagMacroNode),
    47  	}
    48  
    49  	// Tokenize it
    50  	tokens, err := lex(name, tpl)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	t.tokens = tokens
    55  
    56  	// For debugging purposes, show all tokens:
    57  	/*for i, t := range tokens {
    58  		fmt.Printf("%3d. %s\n", i, t)
    59  	}*/
    60  
    61  	// Parse it
    62  	err = t.parse()
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return t, nil
    68  }
    69  
    70  func (tpl *Template) execute(context Context) (*bytes.Buffer, error) {
    71  	// Create output buffer
    72  	// We assume that the rendered template will be 30% larger
    73  	buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
    74  
    75  	// Determine the parent to be executed (for template inheritance)
    76  	parent := tpl
    77  	for parent.parent != nil {
    78  		parent = parent.parent
    79  	}
    80  
    81  	// Create context if none is given
    82  	newContext := make(Context)
    83  	newContext.Update(tpl.set.Globals)
    84  
    85  	if context != nil {
    86  		newContext.Update(context)
    87  
    88  		if len(newContext) > 0 {
    89  			// Check for context name syntax
    90  			err := newContext.checkForValidIdentifiers()
    91  			if err != nil {
    92  				return nil, err
    93  			}
    94  
    95  			// Check for clashes with macro names
    96  			for k, _ := range newContext {
    97  				_, has := tpl.exported_macros[k]
    98  				if has {
    99  					return nil, &Error{
   100  						Filename: tpl.name,
   101  						Sender:   "execution",
   102  						ErrorMsg: fmt.Sprintf("Context key name '%s' clashes with macro '%s'.", k, k),
   103  					}
   104  				}
   105  			}
   106  		}
   107  	}
   108  
   109  	// Create operational context
   110  	ctx := newExecutionContext(parent, newContext)
   111  
   112  	// Run the selected document
   113  	err := parent.root.Execute(ctx, buffer)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	return buffer, nil
   118  }
   119  
   120  // Executes the template with the given context and writes to writer (io.Writer)
   121  // on success. Context can be nil. Nothing is written on error; instead the error
   122  // is being returned.
   123  func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
   124  	buffer, err := tpl.execute(context)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	l := buffer.Len()
   130  	n, werr := buffer.WriteTo(writer)
   131  	if int(n) != l {
   132  		panic(fmt.Sprintf("error on writing template: n(%d) != buffer.Len(%d)", n, l))
   133  	}
   134  	if werr != nil {
   135  		return &Error{
   136  			Filename: tpl.name,
   137  			Sender:   "execution",
   138  			ErrorMsg: werr.Error(),
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  // Executes the template and returns the rendered template as a []byte
   145  func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
   146  	// Execute template
   147  	buffer, err := tpl.execute(context)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	return buffer.Bytes(), nil
   152  }
   153  
   154  // Executes the template and returns the rendered template as a string
   155  func (tpl *Template) Execute(context Context) (string, error) {
   156  	// Execute template
   157  	buffer, err := tpl.execute(context)
   158  	if err != nil {
   159  		return "", err
   160  	}
   161  
   162  	return buffer.String(), nil
   163  
   164  }