-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmustache.go
343 lines (302 loc) · 8.15 KB
/
mustache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// Copyright (c) 2014 Alex Kalyvitis
package mustache
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"reflect"
"strings"
)
// The node type is the base type that represents a node in the parse tree.
type node interface {
// The render function should be defined by any type wishing to satisfy the
// node interface. Implementations should be able to render itself to the
// w Writer with c given as context.
render(t *Template, w *writer, c ...interface{}) error
}
// The textNode type represents a part of the template that is made up solely of
// text. It's an alias to string and it ignores c when rendering.
type textNode string
func (n textNode) render(t *Template, w *writer, c ...interface{}) error {
for _, r := range n {
if !whitespace(r) {
w.text()
}
err := w.write(r)
if err != nil {
return err
}
}
return nil
}
func (n textNode) String() string {
return fmt.Sprintf("[text: %q]", string(n))
}
// The varNode type represents a part of the template that needs to be replaced
// by a variable that exists within c.
type varNode struct {
name string
escape bool
}
func (n *varNode) render(t *Template, w *writer, c ...interface{}) error {
w.text()
v, _ := lookup(n.name, c...)
// If the value is present but 'falsy', such as a false bool, or a zero int,
// we still want to render that value.
if v != nil {
if n.escape {
v = escape(fmt.Sprintf("%v", v))
}
print(w, v)
return nil
}
return fmt.Errorf("failed to lookup %s", n.name)
}
func (n *varNode) String() string {
return fmt.Sprintf("[var: %q escaped: %t]", n.name, n.escape)
}
// The sectionNode type is a complex node which recursively renders its child
// elements while passing along its context along with the global context.
type sectionNode struct {
name string
inverted bool
elems []node
}
func (n *sectionNode) render(t *Template, w *writer, c ...interface{}) error {
w.tag()
defer w.tag()
elemFn := func(v ...interface{}) {
for _, elem := range n.elems {
elem.render(t, w, append(v, c...)...)
}
}
v, ok := lookup(n.name, c...)
if ok != n.inverted {
r := reflect.ValueOf(v)
switch r.Kind() {
case reflect.Slice, reflect.Array:
if r.Len() > 0 {
for i := 0; i < r.Len(); i++ {
elemFn(r.Index(i).Interface())
}
} else {
elemFn(v)
}
default:
elemFn(v)
}
return nil
}
return fmt.Errorf("failed to lookup %s", n.name)
}
func (n *sectionNode) String() string {
return fmt.Sprintf("[section: %q inv: %t elems: %s]", n.name, n.inverted, n.elems)
}
// The commentNode type is a part of the template wich gets ignored. Perhaps it
// can be optionally enabled to print comments.
type commentNode string
func (n commentNode) render(t *Template, w *writer, c ...interface{}) error {
w.tag()
return nil
}
func (n commentNode) String() string {
return fmt.Sprintf("[comment: %q]", string(n))
}
// The partialNode type represents a named partial template.
type partialNode struct {
name string
}
func (p *partialNode) render(t *Template, w *writer, c ...interface{}) error {
w.tag()
if template, ok := t.partials[p.name]; ok {
template.partials = t.partials
template.render(w, c...)
}
return nil
}
func (p *partialNode) String() string {
return fmt.Sprintf("[partial: %s]", p.name)
}
type delimNode string
func (n delimNode) String() string {
return "[delim]"
}
func (n delimNode) render(t *Template, w *writer, c ...interface{}) error {
w.tag()
return nil
}
// The print function is able to format the interface v and write it to w using
// the best possible formatting flags.
func print(w io.Writer, v interface{}) {
if s, ok := v.(fmt.Stringer); ok {
fmt.Fprint(w, s.String())
} else {
switch v.(type) {
case string:
fmt.Fprintf(w, "%s", v)
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
fmt.Fprintf(w, "%d", v)
case float32, float64:
fmt.Fprintf(w, "%g", v)
default:
fmt.Fprintf(w, "%v", v)
}
}
}
// The escape function replicates the text/template.HTMLEscapeString but keeps
// "'" and """ for compatibility with the mustache spec.
func escape(s string) string {
if strings.IndexAny(s, `'"&<>`) < 0 {
return s
}
var b bytes.Buffer
for _, r := range s {
switch r {
case '"':
b.WriteString(""")
case '\'':
b.WriteString("'")
case '&':
b.WriteString("&")
case '<':
b.WriteString("<")
case '>':
b.WriteString(">")
default:
b.WriteRune(r)
}
}
return b.String()
}
// The Option type describes functional options used with Templates. Check out
// Dave Cheney's talk on functional options http://bit.ly/1x9WWPi.
type Option func(*Template)
// Name sets the name of the template.
func Name(n string) Option {
return func(t *Template) {
t.name = n
}
}
// Delimiters sets the start and end delimiters of the template.
func Delimiters(start, end string) Option {
return func(t *Template) {
t.startDelim = start
t.endDelim = end
}
}
// Partial sets p as a partial to the template. It is important to set the name
// of p so that it may be looked up by the parent template.
func Partial(p *Template) Option {
return func(t *Template) {
t.partials[p.name] = p
}
}
// Errors enables missing variable errors. This option is deprecated. Please
// use SilentMiss instead.
func Errors() Option {
return func(t *Template) {
t.silentMiss = false
}
}
// SilentMiss sets the silent miss behavior of variable lookups when rendering.
// If true, missed lookups will not produce any errors. Otherwise a missed
// variable lookup will stop the rendering and return an error.
func SilentMiss(silent bool) Option {
return func(t *Template) {
t.silentMiss = silent
}
}
// The Template type represents a template and its components.
type Template struct {
name string
elems []node
partials map[string]*Template
startDelim string
endDelim string
silentMiss bool
}
// New returns a new Template instance.
func New(options ...Option) *Template {
t := &Template{
elems: make([]node, 0),
partials: make(map[string]*Template),
startDelim: "{{",
endDelim: "}}",
silentMiss: true,
}
t.Option(options...)
return t
}
// Option applies options to the currrent template t.
func (t *Template) Option(options ...Option) {
for _, optionFn := range options {
optionFn(t)
}
}
// Parse parses a stream of bytes read from r and creates a parse tree that
// represents the template.
func (t *Template) Parse(r io.Reader) error {
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
l := newLexer(string(b), t.startDelim, t.endDelim)
p := newParser(l)
elems, err := p.parse()
if err != nil {
return err
}
t.elems = elems
return nil
}
// ParseString is a helper function that uses a string as input.
func (t *Template) ParseString(s string) error {
return t.Parse(strings.NewReader(s))
}
// ParseBytes is a helper function that uses a byte array as input.
func (t *Template) ParseBytes(b []byte) error {
return t.Parse(bytes.NewReader(b))
}
func (t *Template) render(w *writer, context ...interface{}) error {
for _, elem := range t.elems {
err := elem.render(t, w, context...)
if err != nil {
if !t.silentMiss {
return err
}
}
}
return w.flush()
}
// Render walks through the template's parse tree and writes the output to w
// replacing the values found in context.
func (t *Template) Render(w io.Writer, context ...interface{}) error {
return t.render(newWriter(w), context...)
}
// RenderString is a helper function that renders the template as a string.
func (t *Template) RenderString(context ...interface{}) (string, error) {
b := &bytes.Buffer{}
err := t.Render(b, context...)
return b.String(), err
}
// RenderBytes is a helper function that renders the template as a byte slice.
func (t *Template) RenderBytes(context ...interface{}) ([]byte, error) {
var b *bytes.Buffer
err := t.Render(b, context...)
return b.Bytes(), err
}
// Parse wraps the creation of a new template and parsing from r in one go.
func Parse(r io.Reader) (*Template, error) {
t := New()
err := t.Parse(r)
return t, err
}
// Render wraps the parsing and rendering into a single function.
func Render(r io.Reader, w io.Writer, context ...interface{}) error {
t, err := Parse(r)
if err != nil {
return err
}
return t.Render(w, context...)
}