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