Source file src/pkg/text/template/funcs.go
1
2
3
4
5 package template
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "net/url"
12 "reflect"
13 "strings"
14 "unicode"
15 "unicode/utf8"
16 )
17
18
19
20
21
22
23 type FuncMap map[string]interface{}
24
25 var builtins = FuncMap{
26 "and": and,
27 "call": call,
28 "html": HTMLEscaper,
29 "index": index,
30 "js": JSEscaper,
31 "len": length,
32 "not": not,
33 "or": or,
34 "print": fmt.Sprint,
35 "printf": fmt.Sprintf,
36 "println": fmt.Sprintln,
37 "urlquery": URLQueryEscaper,
38 }
39
40 var builtinFuncs = createValueFuncs(builtins)
41
42
43 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
44 m := make(map[string]reflect.Value)
45 addValueFuncs(m, funcMap)
46 return m
47 }
48
49
50 func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
51 for name, fn := range in {
52 v := reflect.ValueOf(fn)
53 if v.Kind() != reflect.Func {
54 panic("value for " + name + " not a function")
55 }
56 if !goodFunc(v.Type()) {
57 panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
58 }
59 out[name] = v
60 }
61 }
62
63
64
65 func addFuncs(out, in FuncMap) {
66 for name, fn := range in {
67 out[name] = fn
68 }
69 }
70
71
72 func goodFunc(typ reflect.Type) bool {
73
74 switch {
75 case typ.NumOut() == 1:
76 return true
77 case typ.NumOut() == 2 && typ.Out(1) == errorType:
78 return true
79 }
80 return false
81 }
82
83
84 func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
85 if tmpl != nil && tmpl.common != nil {
86 if fn := tmpl.execFuncs[name]; fn.IsValid() {
87 return fn, true
88 }
89 }
90 if fn := builtinFuncs[name]; fn.IsValid() {
91 return fn, true
92 }
93 return reflect.Value{}, false
94 }
95
96
97
98
99
100
101 func index(item interface{}, indices ...interface{}) (interface{}, error) {
102 v := reflect.ValueOf(item)
103 for _, i := range indices {
104 index := reflect.ValueOf(i)
105 var isNil bool
106 if v, isNil = indirect(v); isNil {
107 return nil, fmt.Errorf("index of nil pointer")
108 }
109 switch v.Kind() {
110 case reflect.Array, reflect.Slice, reflect.String:
111 var x int64
112 switch index.Kind() {
113 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
114 x = index.Int()
115 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
116 x = int64(index.Uint())
117 default:
118 return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
119 }
120 if x < 0 || x >= int64(v.Len()) {
121 return nil, fmt.Errorf("index out of range: %d", x)
122 }
123 v = v.Index(int(x))
124 case reflect.Map:
125 if !index.IsValid() {
126 index = reflect.Zero(v.Type().Key())
127 }
128 if !index.Type().AssignableTo(v.Type().Key()) {
129 return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
130 }
131 if x := v.MapIndex(index); x.IsValid() {
132 v = x
133 } else {
134 v = reflect.Zero(v.Type().Elem())
135 }
136 default:
137 return nil, fmt.Errorf("can't index item of type %s", v.Type())
138 }
139 }
140 return v.Interface(), nil
141 }
142
143
144
145
146 func length(item interface{}) (int, error) {
147 v, isNil := indirect(reflect.ValueOf(item))
148 if isNil {
149 return 0, fmt.Errorf("len of nil pointer")
150 }
151 switch v.Kind() {
152 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
153 return v.Len(), nil
154 }
155 return 0, fmt.Errorf("len of type %s", v.Type())
156 }
157
158
159
160
161
162 func call(fn interface{}, args ...interface{}) (interface{}, error) {
163 v := reflect.ValueOf(fn)
164 typ := v.Type()
165 if typ.Kind() != reflect.Func {
166 return nil, fmt.Errorf("non-function of type %s", typ)
167 }
168 if !goodFunc(typ) {
169 return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
170 }
171 numIn := typ.NumIn()
172 var dddType reflect.Type
173 if typ.IsVariadic() {
174 if len(args) < numIn-1 {
175 return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
176 }
177 dddType = typ.In(numIn - 1).Elem()
178 } else {
179 if len(args) != numIn {
180 return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
181 }
182 }
183 argv := make([]reflect.Value, len(args))
184 for i, arg := range args {
185 value := reflect.ValueOf(arg)
186
187 var argType reflect.Type
188 if !typ.IsVariadic() || i < numIn-1 {
189 argType = typ.In(i)
190 } else {
191 argType = dddType
192 }
193 if !value.IsValid() && canBeNil(argType) {
194 value = reflect.Zero(argType)
195 }
196 if !value.Type().AssignableTo(argType) {
197 return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
198 }
199 argv[i] = value
200 }
201 result := v.Call(argv)
202 if len(result) == 2 {
203 return result[0].Interface(), result[1].Interface().(error)
204 }
205 return result[0].Interface(), nil
206 }
207
208
209
210 func truth(a interface{}) bool {
211 t, _ := isTrue(reflect.ValueOf(a))
212 return t
213 }
214
215
216
217 func and(arg0 interface{}, args ...interface{}) interface{} {
218 if !truth(arg0) {
219 return arg0
220 }
221 for i := range args {
222 arg0 = args[i]
223 if !truth(arg0) {
224 break
225 }
226 }
227 return arg0
228 }
229
230
231
232 func or(arg0 interface{}, args ...interface{}) interface{} {
233 if truth(arg0) {
234 return arg0
235 }
236 for i := range args {
237 arg0 = args[i]
238 if truth(arg0) {
239 break
240 }
241 }
242 return arg0
243 }
244
245
246 func not(arg interface{}) (truth bool) {
247 truth, _ = isTrue(reflect.ValueOf(arg))
248 return !truth
249 }
250
251
252
253 var (
254 htmlQuot = []byte(""")
255 htmlApos = []byte("'")
256 htmlAmp = []byte("&")
257 htmlLt = []byte("<")
258 htmlGt = []byte(">")
259 )
260
261
262 func HTMLEscape(w io.Writer, b []byte) {
263 last := 0
264 for i, c := range b {
265 var html []byte
266 switch c {
267 case '"':
268 html = htmlQuot
269 case '\'':
270 html = htmlApos
271 case '&':
272 html = htmlAmp
273 case '<':
274 html = htmlLt
275 case '>':
276 html = htmlGt
277 default:
278 continue
279 }
280 w.Write(b[last:i])
281 w.Write(html)
282 last = i + 1
283 }
284 w.Write(b[last:])
285 }
286
287
288 func HTMLEscapeString(s string) string {
289
290 if strings.IndexAny(s, `'"&<>`) < 0 {
291 return s
292 }
293 var b bytes.Buffer
294 HTMLEscape(&b, []byte(s))
295 return b.String()
296 }
297
298
299
300 func HTMLEscaper(args ...interface{}) string {
301 ok := false
302 var s string
303 if len(args) == 1 {
304 s, ok = args[0].(string)
305 }
306 if !ok {
307 s = fmt.Sprint(args...)
308 }
309 return HTMLEscapeString(s)
310 }
311
312
313
314 var (
315 jsLowUni = []byte(`\u00`)
316 hex = []byte("0123456789ABCDEF")
317
318 jsBackslash = []byte(`\\`)
319 jsApos = []byte(`\'`)
320 jsQuot = []byte(`\"`)
321 jsLt = []byte(`\x3C`)
322 jsGt = []byte(`\x3E`)
323 )
324
325
326 func JSEscape(w io.Writer, b []byte) {
327 last := 0
328 for i := 0; i < len(b); i++ {
329 c := b[i]
330
331 if !jsIsSpecial(rune(c)) {
332
333 continue
334 }
335 w.Write(b[last:i])
336
337 if c < utf8.RuneSelf {
338
339
340 switch c {
341 case '\\':
342 w.Write(jsBackslash)
343 case '\'':
344 w.Write(jsApos)
345 case '"':
346 w.Write(jsQuot)
347 case '<':
348 w.Write(jsLt)
349 case '>':
350 w.Write(jsGt)
351 default:
352 w.Write(jsLowUni)
353 t, b := c>>4, c&0x0f
354 w.Write(hex[t : t+1])
355 w.Write(hex[b : b+1])
356 }
357 } else {
358
359 r, size := utf8.DecodeRune(b[i:])
360 if unicode.IsPrint(r) {
361 w.Write(b[i : i+size])
362 } else {
363 fmt.Fprintf(w, "\\u%04X", r)
364 }
365 i += size - 1
366 }
367 last = i + 1
368 }
369 w.Write(b[last:])
370 }
371
372
373 func JSEscapeString(s string) string {
374
375 if strings.IndexFunc(s, jsIsSpecial) < 0 {
376 return s
377 }
378 var b bytes.Buffer
379 JSEscape(&b, []byte(s))
380 return b.String()
381 }
382
383 func jsIsSpecial(r rune) bool {
384 switch r {
385 case '\\', '\'', '"', '<', '>':
386 return true
387 }
388 return r < ' ' || utf8.RuneSelf <= r
389 }
390
391
392
393 func JSEscaper(args ...interface{}) string {
394 ok := false
395 var s string
396 if len(args) == 1 {
397 s, ok = args[0].(string)
398 }
399 if !ok {
400 s = fmt.Sprint(args...)
401 }
402 return JSEscapeString(s)
403 }
404
405
406
407 func URLQueryEscaper(args ...interface{}) string {
408 s, ok := "", false
409 if len(args) == 1 {
410 s, ok = args[0].(string)
411 }
412 if !ok {
413 s = fmt.Sprint(args...)
414 }
415 return url.QueryEscape(s)
416 }
View as plain text