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 }