123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- package pongo2
- import (
- "bytes"
- "io"
- "github.com/juju/errors"
- )
- type TemplateWriter interface {
- io.Writer
- WriteString(string) (int, error)
- }
- type templateWriter struct {
- w io.Writer
- }
- func (tw *templateWriter) WriteString(s string) (int, error) {
- return tw.w.Write([]byte(s))
- }
- func (tw *templateWriter) Write(b []byte) (int, error) {
- return tw.w.Write(b)
- }
- type Template struct {
- set *TemplateSet
- // Input
- isTplString 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
- exportedMacros map[string]*tagMacroNode
- // Output
- root *nodeDocument
- }
- func newTemplateString(set *TemplateSet, tpl []byte) (*Template, error) {
- return newTemplate(set, "<string>", true, tpl)
- }
- func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*Template, error) {
- strTpl := string(tpl)
- // Create the template
- t := &Template{
- set: set,
- isTplString: isTplString,
- name: name,
- tpl: strTpl,
- size: len(strTpl),
- blocks: make(map[string]*NodeWrapper),
- exportedMacros: make(map[string]*tagMacroNode),
- }
- // Tokenize it
- tokens, err := lex(name, strTpl)
- 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, writer TemplateWriter) error {
- // 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 err
- }
- // Check for clashes with macro names
- for k := range newContext {
- _, has := tpl.exportedMacros[k]
- if has {
- return &Error{
- Filename: tpl.name,
- Sender: "execution",
- OrigError: errors.Errorf("context key name '%s' clashes with macro '%s'", k, k),
- }
- }
- }
- }
- }
- // Create operational context
- ctx := newExecutionContext(parent, newContext)
- // Run the selected document
- if err := parent.root.Execute(ctx, writer); err != nil {
- return err
- }
- return nil
- }
- func (tpl *Template) newTemplateWriterAndExecute(context Context, writer io.Writer) error {
- return tpl.execute(context, &templateWriter{w: writer})
- }
- func (tpl *Template) newBufferAndExecute(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)))
- if err := tpl.execute(context, buffer); 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 {
- buf, err := tpl.newBufferAndExecute(context)
- if err != nil {
- return err
- }
- _, err = buf.WriteTo(writer)
- if err != nil {
- return err
- }
- return nil
- }
- // Same as ExecuteWriter. The only difference between both functions is that
- // this function might already have written parts of the generated template in the
- // case of an execution error because there's no intermediate buffer involved for
- // performance reasons. This is handy if you need high performance template
- // generation or if you want to manage your own pool of buffers.
- func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error {
- return tpl.newTemplateWriterAndExecute(context, writer)
- }
- // Executes the template and returns the rendered template as a []byte
- func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
- // Execute template
- buffer, err := tpl.newBufferAndExecute(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.newBufferAndExecute(context)
- if err != nil {
- return "", err
- }
- return buffer.String(), nil
- }
|