2009-04-08 23:08:55 -06:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package template
|
|
|
|
|
|
|
|
import (
|
2009-12-15 16:41:46 -07:00
|
|
|
"bytes"
|
|
|
|
"container/vector"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
2009-04-08 23:08:55 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type Test struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
in, out, err string
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type T struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
item string
|
|
|
|
value string
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
type U struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
mp map[string]int
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
}
|
|
|
|
|
2009-04-08 23:08:55 -06:00
|
|
|
type S struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
header string
|
|
|
|
integer int
|
|
|
|
raw string
|
|
|
|
innerT T
|
|
|
|
innerPointerT *T
|
|
|
|
data []T
|
|
|
|
pdata []*T
|
|
|
|
empty []*T
|
|
|
|
emptystring string
|
|
|
|
null []*T
|
|
|
|
vec *vector.Vector
|
|
|
|
true bool
|
|
|
|
false bool
|
|
|
|
mp map[string]string
|
|
|
|
innermap U
|
|
|
|
bytes []byte
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
|
2009-12-16 04:10:50 -07:00
|
|
|
func (s *S) pointerMethod() string { return "ptrmethod!" }
|
|
|
|
|
|
|
|
func (s S) valueMethod() string { return "valmethod!" }
|
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
var t1 = T{"ItemNumber1", "ValueNumber1"}
|
|
|
|
var t2 = T{"ItemNumber2", "ValueNumber2"}
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-04-13 20:29:38 -06:00
|
|
|
func uppercase(v interface{}) string {
|
2009-12-15 16:41:46 -07:00
|
|
|
s := v.(string)
|
|
|
|
t := ""
|
2009-04-09 00:33:31 -06:00
|
|
|
for i := 0; i < len(s); i++ {
|
2009-12-15 16:41:46 -07:00
|
|
|
c := s[i]
|
2009-04-09 00:33:31 -06:00
|
|
|
if 'a' <= c && c <= 'z' {
|
2009-11-09 22:23:52 -07:00
|
|
|
c = c + 'A' - 'a'
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
t += string(c)
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return t
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
|
|
|
|
2009-04-13 20:29:38 -06:00
|
|
|
func plus1(v interface{}) string {
|
2009-12-15 16:41:46 -07:00
|
|
|
i := v.(int)
|
|
|
|
return fmt.Sprint(i + 1)
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
|
|
|
|
2009-05-08 12:22:57 -06:00
|
|
|
func writer(f func(interface{}) string) (func(io.Writer, interface{}, string)) {
|
2009-11-06 15:24:38 -07:00
|
|
|
return func(w io.Writer, v interface{}, format string) {
|
2009-11-09 13:07:39 -07:00
|
|
|
io.WriteString(w, f(v))
|
|
|
|
}
|
2009-04-13 20:29:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
var formatters = FormatterMap{
|
|
|
|
"uppercase": writer(uppercase),
|
|
|
|
"+1": writer(plus1),
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
var tests = []*Test{
|
2009-04-08 23:08:55 -06:00
|
|
|
// Simple
|
2009-10-08 16:14:54 -06:00
|
|
|
&Test{"", "", ""},
|
|
|
|
&Test{"abc\ndef\n", "abc\ndef\n", ""},
|
|
|
|
&Test{" {.meta-left} \n", "{", ""},
|
|
|
|
&Test{" {.meta-right} \n", "}", ""},
|
|
|
|
&Test{" {.space} \n", " ", ""},
|
|
|
|
&Test{" {.tab} \n", "\t", ""},
|
|
|
|
&Test{" {#comment} \n", "", ""},
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-04-20 19:51:13 -06:00
|
|
|
// Variables at top level
|
|
|
|
&Test{
|
2009-08-26 19:30:13 -06:00
|
|
|
in: "{header}={integer}\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "Header=77\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
},
|
|
|
|
|
2009-12-16 04:10:50 -07:00
|
|
|
// Method at top level
|
|
|
|
&Test{
|
|
|
|
in: "ptrmethod={pointerMethod}\n",
|
|
|
|
|
|
|
|
out: "ptrmethod=ptrmethod!\n",
|
|
|
|
},
|
|
|
|
|
|
|
|
&Test{
|
|
|
|
in: "valmethod={valueMethod}\n",
|
|
|
|
|
|
|
|
out: "valmethod=valmethod!\n",
|
|
|
|
},
|
|
|
|
|
2009-04-08 23:08:55 -06:00
|
|
|
// Section
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section data }\n" +
|
|
|
|
"some text for the section\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "some text for the section\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section data }\n" +
|
|
|
|
"{header}={integer}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "Header=77\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{header}={integer}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "Header=77\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"data present\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"data not present\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "data present\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section empty }\n" +
|
|
|
|
"data present\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"data not present\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "data not present\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section null }\n" +
|
|
|
|
"data present\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"data not present\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "data not present\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{header}={integer}\n" +
|
|
|
|
"{.section @ }\n" +
|
|
|
|
"{header}={integer}\n" +
|
|
|
|
"{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "Header=77\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"Header=77\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
2009-04-14 01:06:49 -06:00
|
|
|
&Test{
|
2009-08-26 19:30:13 -06:00
|
|
|
in: "{.section data}{.end} {header}\n",
|
2009-04-14 01:06:49 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: " Header\n",
|
2009-04-14 01:06:49 -06:00
|
|
|
},
|
2009-04-08 23:08:55 -06:00
|
|
|
|
|
|
|
// Repeated
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{.repeated section @ }\n" +
|
|
|
|
"{item}={value}\n" +
|
|
|
|
"{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "ItemNumber1=ValueNumber1\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"ItemNumber2=ValueNumber2\n",
|
2009-04-08 23:08:55 -06:00
|
|
|
},
|
2009-04-20 19:51:13 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{.repeated section @ }\n" +
|
|
|
|
"{item}={value}\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"this should not appear\n" +
|
|
|
|
"{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "ItemNumber1=ValueNumber1\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"ItemNumber2=ValueNumber2\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
},
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section @ }\n" +
|
|
|
|
"{.repeated section empty }\n" +
|
|
|
|
"{item}={value}\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"this should appear: empty field\n" +
|
|
|
|
"{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "this should appear: empty field\n",
|
2009-04-20 19:51:13 -06:00
|
|
|
},
|
2009-08-29 22:13:32 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.repeated section pdata }\n" +
|
|
|
|
"{item}\n" +
|
|
|
|
"{.alternates with}\n" +
|
|
|
|
"is\nover\nmultiple\nlines\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-08-29 22:13:32 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "ItemNumber1\n" +
|
|
|
|
"is\nover\nmultiple\nlines\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"ItemNumber2\n",
|
2009-08-29 22:13:32 -06:00
|
|
|
},
|
2009-04-27 22:04:46 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{.repeated section @ }\n" +
|
|
|
|
"{item}={value}\n" +
|
|
|
|
"{.alternates with}DIVIDER\n" +
|
|
|
|
"{.or}\n" +
|
|
|
|
"this should not appear\n" +
|
|
|
|
"{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-27 22:04:46 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "ItemNumber1=ValueNumber1\n" +
|
|
|
|
"DIVIDER\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"ItemNumber2=ValueNumber2\n",
|
2009-04-27 22:04:46 -06:00
|
|
|
},
|
2009-08-26 19:30:13 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.repeated section vec }\n" +
|
|
|
|
"{@}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-08-26 19:30:13 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "elt1\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"elt2\n",
|
2009-08-26 19:30:13 -06:00
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
in: "{.repeated section integer}{.end}",
|
|
|
|
|
2009-11-03 00:06:48 -07:00
|
|
|
err: "line 1: .repeated: cannot repeat integer (type int)",
|
2009-08-26 19:30:13 -06:00
|
|
|
},
|
2009-04-09 00:33:31 -06:00
|
|
|
|
2009-07-30 19:17:07 -06:00
|
|
|
// Nested names
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section @ }\n" +
|
|
|
|
"{innerT.item}={innerT.value}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}",
|
2009-07-30 19:17:07 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "ItemNumber1=ValueNumber1\n",
|
2009-07-30 19:17:07 -06:00
|
|
|
},
|
2009-09-01 17:31:49 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section @ }\n" +
|
|
|
|
"{innerT.item}={.section innerT}{.section value}{@}{.end}{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}",
|
2009-09-01 17:31:49 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "ItemNumber1=ValueNumber1\n",
|
2009-09-01 17:31:49 -06:00
|
|
|
},
|
|
|
|
|
2009-07-30 19:17:07 -06:00
|
|
|
|
2009-04-09 00:33:31 -06:00
|
|
|
// Formatters
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section pdata }\n" +
|
|
|
|
"{header|uppercase}={integer|+1}\n" +
|
|
|
|
"{header|html}={integer|str}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.end}\n",
|
2009-04-09 00:33:31 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "HEADER=78\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"Header=77\n",
|
2009-04-09 00:33:31 -06:00
|
|
|
},
|
2009-04-15 01:26:49 -06:00
|
|
|
|
2009-04-15 01:05:47 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{raw}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{raw|html}\n",
|
2009-04-15 01:26:49 -06:00
|
|
|
|
2009-12-09 17:54:07 -07:00
|
|
|
out: "&<>!@ #$%^\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"&<>!@ #$%^\n",
|
2009-04-15 01:05:47 -06:00
|
|
|
},
|
2009-04-15 01:26:49 -06:00
|
|
|
|
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section emptystring}emptystring{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.section header}header{.end}\n",
|
2009-04-15 01:26:49 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "\nheader\n",
|
2009-04-15 01:26:49 -06:00
|
|
|
},
|
2009-09-01 17:31:49 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
&Test{
|
2009-12-09 17:54:07 -07:00
|
|
|
in: "{.section true}1{.or}2{.end}\n" +
|
2009-11-05 16:12:37 -07:00
|
|
|
"{.section false}3{.or}4{.end}\n",
|
2009-09-01 17:31:49 -06:00
|
|
|
|
2009-10-08 16:14:54 -06:00
|
|
|
out: "1\n4\n",
|
2009-09-01 17:31:49 -06:00
|
|
|
},
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
|
2009-11-30 11:29:14 -07:00
|
|
|
&Test{
|
|
|
|
in: "{bytes}",
|
|
|
|
|
|
|
|
out: "hello",
|
|
|
|
},
|
|
|
|
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
// Maps
|
|
|
|
|
|
|
|
&Test{
|
|
|
|
in: "{mp.mapkey}\n",
|
|
|
|
|
|
|
|
out: "Ahoy!\n",
|
|
|
|
},
|
|
|
|
|
|
|
|
&Test{
|
|
|
|
in: "{innermap.mp.innerkey}\n",
|
|
|
|
|
|
|
|
out: "55\n",
|
|
|
|
},
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestAll(t *testing.T) {
|
2009-12-15 16:41:46 -07:00
|
|
|
s := new(S)
|
2009-04-08 23:08:55 -06:00
|
|
|
// initialized by hand for clarity.
|
2009-12-15 16:41:46 -07:00
|
|
|
s.header = "Header"
|
|
|
|
s.integer = 77
|
|
|
|
s.raw = "&<>!@ #$%^"
|
|
|
|
s.innerT = t1
|
|
|
|
s.data = []T{t1, t2}
|
|
|
|
s.pdata = []*T{&t1, &t2}
|
|
|
|
s.empty = []*T{}
|
|
|
|
s.null = nil
|
|
|
|
s.vec = new(vector.Vector)
|
|
|
|
s.vec.Push("elt1")
|
|
|
|
s.vec.Push("elt2")
|
|
|
|
s.true = true
|
|
|
|
s.false = false
|
|
|
|
s.mp = make(map[string]string)
|
|
|
|
s.mp["mapkey"] = "Ahoy!"
|
|
|
|
s.innermap.mp = make(map[string]int)
|
|
|
|
s.innermap.mp["innerkey"] = 55
|
|
|
|
s.bytes = strings.Bytes("hello")
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2009-09-15 10:41:59 -06:00
|
|
|
for _, test := range tests {
|
2009-12-15 16:41:46 -07:00
|
|
|
buf.Reset()
|
|
|
|
tmpl, err := Parse(test.in, formatters)
|
2009-04-14 01:06:49 -06:00
|
|
|
if err != nil {
|
2009-12-15 16:41:46 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
continue
|
2009-04-14 01:06:49 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
err = tmpl.Execute(s, &buf)
|
2009-08-26 19:30:13 -06:00
|
|
|
if test.err == "" {
|
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected execute error:", err)
|
2009-08-26 19:30:13 -06:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err == nil || err.String() != test.err {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("expected execute error %q, got %q", test.err, err.String())
|
2009-08-26 19:30:13 -06:00
|
|
|
}
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
2009-09-18 00:51:06 -06:00
|
|
|
if buf.String() != test.out {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String())
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
func TestMapDriverType(t *testing.T) {
|
2009-12-15 16:41:46 -07:00
|
|
|
mp := map[string]string{"footer": "Ahoy!"}
|
|
|
|
tmpl, err := Parse("template: {footer}", nil)
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
var b bytes.Buffer
|
|
|
|
err = tmpl.Execute(mp, &b)
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected execute error:", err)
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
s := b.String()
|
|
|
|
expected := "template: Ahoy!"
|
Map support for template.Execute().
Allows the developer to pass a map either by itself for
evaluation, or inside a struct. Access to data inside
maps is identical to the current system for structs, ie.
-Psuedocode-
mp map[string]string = {
"header" : "A fantastic header!",
"footer" : "A not-so-fantastic footer!",
}
template.Execute(mp)
...can be accessed using {header} and {footer} in
the template. Similarly, for maps inside structs:
type s struct {
mp map[string]string,
}
s1 = new s
s1.mp["header"] = "A fantastic header!";
template.Execute(s1)
...is accessed using {mp.header}. Multi-maps, ie.
map[string](map[string]string) and maps of structs
containing more maps are unsupported, but then, I'm
not even sure if that's supported by the language.
Map elements can be of any type that can be written
by the formatters. Keys should really only be strings.
Fixes #259.
R=r, rsc
https://golang.org/cl/157088
2009-11-19 22:08:05 -07:00
|
|
|
if s != expected {
|
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", "template: Ahoy!", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 02:12:20 -06:00
|
|
|
func TestStringDriverType(t *testing.T) {
|
2009-12-15 16:41:46 -07:00
|
|
|
tmpl, err := Parse("template: {@}", nil)
|
2009-04-14 01:06:49 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
2009-04-14 01:06:49 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
var b bytes.Buffer
|
|
|
|
err = tmpl.Execute("hello", &b)
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected execute error:", err)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
s := b.String()
|
2009-04-14 02:12:20 -06:00
|
|
|
if s != "template: hello" {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
}
|
2009-04-14 22:25:33 -06:00
|
|
|
|
|
|
|
func TestTwice(t *testing.T) {
|
2009-12-15 16:41:46 -07:00
|
|
|
tmpl, err := Parse("template: {@}", nil)
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
var b bytes.Buffer
|
|
|
|
err = tmpl.Execute("hello", &b)
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
s := b.String()
|
|
|
|
text := "template: hello"
|
2009-04-14 22:25:33 -06:00
|
|
|
if s != text {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", text, s)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
err = tmpl.Execute("hello", &b)
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
s = b.String()
|
|
|
|
text += text
|
2009-04-14 22:25:33 -06:00
|
|
|
if s != text {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", text, s)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
|
|
|
}
|
2009-04-14 23:35:18 -06:00
|
|
|
|
|
|
|
func TestCustomDelims(t *testing.T) {
|
|
|
|
// try various lengths. zero should catch error.
|
|
|
|
for i := 0; i < 7; i++ {
|
|
|
|
for j := 0; j < 7; j++ {
|
2009-12-15 16:41:46 -07:00
|
|
|
tmpl := New(nil)
|
2009-04-14 23:35:18 -06:00
|
|
|
// first two chars deliberately the same to test equal left and right delims
|
2009-12-15 16:41:46 -07:00
|
|
|
ldelim := "$!#$%^&"[0:i]
|
|
|
|
rdelim := "$*&^%$!"[0:j]
|
|
|
|
tmpl.SetDelims(ldelim, rdelim)
|
2009-04-14 23:35:18 -06:00
|
|
|
// if braces, this would be template: {@}{.meta-left}{.meta-right}
|
|
|
|
text := "template: " +
|
|
|
|
ldelim + "@" + rdelim +
|
|
|
|
ldelim + ".meta-left" + rdelim +
|
2009-12-15 16:41:46 -07:00
|
|
|
ldelim + ".meta-right" + rdelim
|
|
|
|
err := tmpl.Parse(text)
|
2009-04-14 23:35:18 -06:00
|
|
|
if err != nil {
|
2009-12-15 16:41:46 -07:00
|
|
|
if i == 0 || j == 0 { // expected
|
2009-11-09 13:07:39 -07:00
|
|
|
continue
|
2009-04-14 23:35:18 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
t.Error("unexpected parse error:", err)
|
2009-04-14 23:35:18 -06:00
|
|
|
} else if i == 0 || j == 0 {
|
2009-12-15 16:41:46 -07:00
|
|
|
t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim)
|
|
|
|
continue
|
2009-04-14 23:35:18 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
var b bytes.Buffer
|
|
|
|
err = tmpl.Execute("hello", &b)
|
|
|
|
s := b.String()
|
2009-11-09 22:23:52 -07:00
|
|
|
if s != "template: hello"+ldelim+rdelim {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
|
2009-04-14 23:35:18 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-31 13:52:03 -06:00
|
|
|
|
|
|
|
// Test that a variable evaluates to the field itself and does not further indirection
|
|
|
|
func TestVarIndirection(t *testing.T) {
|
2009-12-15 16:41:46 -07:00
|
|
|
s := new(S)
|
2009-07-31 13:52:03 -06:00
|
|
|
// initialized by hand for clarity.
|
2009-12-15 16:41:46 -07:00
|
|
|
s.innerPointerT = &t1
|
2009-07-31 13:52:03 -06:00
|
|
|
|
2009-12-15 16:41:46 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
input := "{.section @}{innerPointerT}{.end}"
|
|
|
|
tmpl, err := Parse(input, nil)
|
2009-07-31 13:52:03 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Fatal("unexpected parse error:", err)
|
2009-07-31 13:52:03 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
err = tmpl.Execute(s, &buf)
|
2009-07-31 13:52:03 -06:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Fatal("unexpected execute error:", err)
|
2009-07-31 13:52:03 -06:00
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
|
2009-09-18 00:51:06 -06:00
|
|
|
if buf.String() != expect {
|
2009-11-09 13:07:39 -07:00
|
|
|
t.Errorf("for %q: expected %q got %q", input, expect, buf.String())
|
2009-07-31 13:52:03 -06:00
|
|
|
}
|
|
|
|
}
|