summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Meneghello <rawrz0r@gmail.com>2009-11-19 21:08:05 -0800
committerJames Meneghello <rawrz0r@gmail.com>2009-11-19 21:08:05 -0800
commit685eac5e26250e4d9b795d1b2baf4bdcbd4939b9 (patch)
treeabe1a7cdfa2aa0e1ee5bd4ad479520c8f39148ed
parent0e9a31a3439b63820d967ae09229ddc9b45ed540 (diff)
downloadgolang-685eac5e26250e4d9b795d1b2baf4bdcbd4939b9.tar.gz
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 issue 259. R=r, rsc http://codereview.appspot.com/157088 Committer: Rob Pike <r@golang.org>
-rw-r--r--src/pkg/template/template.go29
-rw-r--r--src/pkg/template/template_test.go42
2 files changed, 59 insertions, 12 deletions
diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go
index 9a819db61..6964d67f4 100644
--- a/src/pkg/template/template.go
+++ b/src/pkg/template/template.go
@@ -10,10 +10,11 @@
Templates are executed by applying them to a data structure.
Annotations in the template refer to elements of the data
- structure (typically a field of a struct) to control execution
- and derive values to be displayed. The template walks the
- structure as it executes and the "cursor" @ represents the
- value at the current location in the structure.
+ structure (typically a field of a struct or a key in a map)
+ to control execution and derive values to be displayed.
+ The template walks the structure as it executes and the
+ "cursor" @ represents the value at the current location
+ in the structure.
Data items may be values or pointers; the interface hides the
indirection.
@@ -605,20 +606,24 @@ func (st *state) findVar(s string) reflect.Value {
data := st.data;
elems := strings.Split(s, ".", 0);
for i := 0; i < len(elems); i++ {
- // Look up field; data must be a struct.
+ // Look up field; data must be a struct or map.
data = reflect.Indirect(data);
if data == nil {
return nil
}
- typ, ok := data.Type().(*reflect.StructType);
- if !ok {
- return nil
- }
- field, ok := typ.FieldByName(elems[i]);
- if !ok {
+
+ switch typ := data.Type().(type) {
+ case *reflect.StructType:
+ field, ok := typ.FieldByName(elems[i]);
+ if !ok {
+ return nil
+ }
+ data = data.(*reflect.StructValue).FieldByIndex(field.Index);
+ case *reflect.MapType:
+ data = data.(*reflect.MapValue).Elem(reflect.NewValue(elems[i]))
+ default:
return nil
}
- data = data.(*reflect.StructValue).FieldByIndex(field.Index);
}
return data;
}
diff --git a/src/pkg/template/template_test.go b/src/pkg/template/template_test.go
index 0b95fcff4..8dadd27f7 100644
--- a/src/pkg/template/template_test.go
+++ b/src/pkg/template/template_test.go
@@ -21,6 +21,10 @@ type T struct {
value string;
}
+type U struct {
+ mp map[string]int;
+}
+
type S struct {
header string;
integer int;
@@ -35,6 +39,8 @@ type S struct {
vec *vector.Vector;
true bool;
false bool;
+ mp map[string]string;
+ innermap U;
}
var t1 = T{"ItemNumber1", "ValueNumber1"}
@@ -275,6 +281,20 @@ var tests = []*Test{
out: "1\n4\n",
},
+
+ // Maps
+
+ &Test{
+ in: "{mp.mapkey}\n",
+
+ out: "Ahoy!\n",
+ },
+
+ &Test{
+ in: "{innermap.mp.innerkey}\n",
+
+ out: "55\n",
+ },
}
func TestAll(t *testing.T) {
@@ -293,6 +313,10 @@ func TestAll(t *testing.T) {
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;
var buf bytes.Buffer;
for _, test := range tests {
@@ -318,6 +342,24 @@ func TestAll(t *testing.T) {
}
}
+func TestMapDriverType(t *testing.T) {
+ mp := map[string]string{"footer": "Ahoy!"};
+ tmpl, err := Parse("template: {footer}", nil);
+ if err != nil {
+ t.Error("unexpected parse error:", err)
+ }
+ var b bytes.Buffer;
+ err = tmpl.Execute(mp, &b);
+ if err != nil {
+ t.Error("unexpected execute error:", err)
+ }
+ s := b.String();
+ expected := "template: Ahoy!";
+ if s != expected {
+ t.Errorf("failed passing string as data: expected %q got %q", "template: Ahoy!", s)
+ }
+}
+
func TestStringDriverType(t *testing.T) {
tmpl, err := Parse("template: {@}", nil);
if err != nil {