If it is not found, the search continues in outer sections
until the top level is reached.
+ If the field value is a pointer, leading asterisks indicate
+ that the value to be inserted should be evaluated through the
+ pointer. For example, if x.p is of type *int, {x.p} will
+ insert the value of the pointer but {*x.p} will insert the
+ value of the underlying integer. If the value is nil or not a
+ pointer, asterisks have no effect.
+
If a formatter is specified, it must be named in the formatter
map passed to the template set up routines or in the default
set ("html","str","") and is used to process the data for
return v
}
+// indirectPtr returns the item numLevels levels of indirection below the value.
+// It is forgiving: if the value is not a pointer, it returns it rather than giving
+// an error. If the pointer is nil, it is returned as is.
+func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
+ for i := numLevels; v != nil && i > 0; i++ {
+ if p, ok := v.(*reflect.PtrValue); ok {
+ if p.IsNil() {
+ return v
+ }
+ v = p.Elem()
+ } else {
+ break
+ }
+ }
+ return v
+}
+
// Walk v through pointers and interfaces, extracting the elements within.
func indirect(v reflect.Value) reflect.Value {
loop:
// The special name "@" (the "cursor") denotes the current data.
// The value coming in (st.data) might need indirecting to reach
// a struct while the return value is not indirected - that is,
-// it represents the actual named field.
+// it represents the actual named field. Leading stars indicate
+// levels of indirection to be applied to the value.
func (t *Template) findVar(st *state, s string) reflect.Value {
+ data := st.data
+ flattenedName := strings.TrimLeft(s, "*")
+ numStars := len(s) - len(flattenedName)
+ s = flattenedName
if s == "@" {
- return st.data
+ return indirectPtr(data, numStars)
}
- data := st.data
for _, elem := range strings.Split(s, ".", -1) {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
return nil
}
}
- return data
+ return indirectPtr(data, numStars)
}
// Is there no data to look at?
type S struct {
Header string
+ HeaderPtr *string
Integer int
+ IntegerPtr *int
+ NilPtr *int
Raw string
InnerT T
InnerPointerT *T
JSON interface{}
Innermap U
Stringmap map[string]string
+ Ptrmap map[string]*string
Bytes []byte
Iface interface{}
Ifaceptr interface{}
out: "Header=77\n",
},
+ &Test{
+ in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
+
+ out: "Pointers: Header=77\n",
+ },
+
+ &Test{
+ in: "Stars but not pointers: {*Header}={*Integer}\n",
+
+ out: "Stars but not pointers: Header=77\n",
+ },
+
+ &Test{
+ in: "nil pointer: {*NilPtr}={*Integer}\n",
+
+ out: "nil pointer: <nil>=77\n",
+ },
+
// Method at top level
&Test{
in: "ptrmethod={PointerMethod}\n",
out: "\tstringresult\n" +
"\tstringresult\n",
},
+ &Test{
+ in: "{*Ptrmap.stringkey1}\n",
+
+ out: "pointedToString\n",
+ },
+ &Test{
+ in: "{.repeated section Ptrmap}\n" +
+ "{*@}\n" +
+ "{.end}",
+
+ out: "pointedToString\n" +
+ "pointedToString\n",
+ },
+
// Interface values
s := new(S)
// initialized by hand for clarity.
s.Header = "Header"
+ s.HeaderPtr = &s.Header
s.Integer = 77
+ s.IntegerPtr = &s.Integer
s.Raw = "&<>!@ #$%^"
s.InnerT = t1
s.Data = []T{t1, t2}
s.Stringmap = make(map[string]string)
s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
s.Stringmap["stringkey2"] = "stringresult"
+ s.Ptrmap = make(map[string]*string)
+ x := "pointedToString"
+ s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent
+ s.Ptrmap["stringkey2"] = &x
s.Bytes = []byte("hello")
s.Iface = []int{1, 2, 3}
s.Ifaceptr = &T{"Item", "Value"}