From e21d981a2fdf36b9c126ef53569a175db5513e79 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 14 Apr 2009 01:12:20 -0700 Subject: [PATCH] add type in not-found error messages. delay indirection so that values passed to formatters preserve pointer-ness. R=r OCL=27410 CL=27414 --- src/lib/template/template.go | 59 +++++++++++++------------------ src/lib/template/template_test.go | 14 ++++---- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/lib/template/template.go b/src/lib/template/template.go index 231ab37b9c..6fa8a0d633 100644 --- a/src/lib/template/template.go +++ b/src/lib/template/template.go @@ -268,17 +268,22 @@ func (t *Template) analyze(item []byte, st *state) (tok int, w []string) { } // If the data for this template is a struct, find the named variable. -func (st *state) findVar(s string) (int, int) { - typ, ok := st.data.Type().(reflect.StructType); +// The special name "@" denotes the current data. +func (st *state) findVar(s string) reflect.Value { + if s == "@" { + return st.data + } + data := reflect.Indirect(st.data); + typ, ok := data.Type().(reflect.StructType); if ok { for i := 0; i < typ.Len(); i++ { name, ftyp, tag, offset := typ.Field(i); if name == s { - return i, ftyp.Kind() + return data.(reflect.StructValue).Field(i) } } } - return -1, -1 + return nil } // Is there no data to look at? @@ -301,18 +306,13 @@ func (t *Template) executeRepeated(w []string, st *state) { if w[1] != "section" { st.error(ErrSyntax, `: .repeated must have "section"`) } + // Find driver array/struct for this section. It must be in the current struct. - // The special name "@" leaves us at this level. - var field reflect.Value; - if w[2] == "@" { - field = st.data - } else { - i, kind := st.findVar(w[1]); - if i < 0 { - st.error(ErrNoVar, ": ", w[2]); - } - field = reflect.Indirect(st.data.(reflect.StructValue).Field(i)); + field := st.findVar(w[2]); + if field == nil { + st.error(ErrNoVar, ": .repeated ", w[2], " in ", reflect.Indirect(st.data).Type()); } + // Must be an array/slice if field != nil && field.Kind() != reflect.ArrayKind { st.error(ErrBadType, " in .repeated: ", w[2], " ", field.Type().String()); @@ -349,24 +349,17 @@ Loop: array := field.(reflect.ArrayValue); for i := 0; i < array.Len(); i++ { tmp := childTemplate(t, t.buf[start:end]); - tmp.execute(&state{st, st.errorchan, reflect.Indirect(array.Elem(i)), st.wr}); + tmp.execute(&state{st, st.errorchan, array.Elem(i), st.wr}); } } } // Execute a ".section" func (t *Template) executeSection(w []string, st *state) { - // Find driver array/struct for this section. It must be in the current struct. - // The special name "@" leaves us at this level. - var field reflect.Value; - if w[1] == "@" { - field = st.data - } else { - i, kind := st.findVar(w[1]); - if i < 0 { - st.error(ErrNoVar, ": ", w[1]); - } - field = st.data.(reflect.StructValue).Field(i); + // Find driver data for this section. It must be in the current struct. + field := st.findVar(w[1]); + if field == nil { + st.error(ErrNoVar, ": .section ", w[1], " in ", reflect.Indirect(st.data).Type()); } // Scan section, remembering slice of text we must execute. orFound := false; @@ -424,14 +417,14 @@ Loop: // Look up a variable, up through the parent if necessary. func (t *Template) varValue(name string, st *state) reflect.Value { - i, kind := st.findVar(name); - if i < 0 { + field := st.findVar(name); + if field == nil { if st.parent == nil { st.error(ErrNoVar, ": ", name) } return t.varValue(name, st.parent); } - return st.data.(reflect.StructValue).Field(i); + return field; } // Evaluate a variable, looking up through the parent if necessary. @@ -517,12 +510,8 @@ func Parse(s string, fmap FormatterMap) (*Template, *os.Error, int) { } func (t *Template) Execute(data interface{}, wr io.Write) *os.Error { - // Extract the driver struct. - val := reflect.Indirect(reflect.NewValue(data)); - sval, ok1 := val.(reflect.StructValue); - if !ok1 { - return ErrNotStruct - } + // Extract the driver data. + val := reflect.NewValue(data); ch := make(chan *os.Error); go func() { t.execute(&state{nil, ch, val, wr}); diff --git a/src/lib/template/template_test.go b/src/lib/template/template_test.go index 9c4fd20cce..090a091365 100644 --- a/src/lib/template/template_test.go +++ b/src/lib/template/template_test.go @@ -161,7 +161,7 @@ var tests = []*Test { "HEADER=78\n" "Header=77\n" }, - + } func TestAll(t *testing.T) { @@ -192,13 +192,15 @@ func TestAll(t *testing.T) { } } -func TestBadDriverType(t *testing.T) { - tmpl, err, line := Parse("hi", nil); +func TestStringDriverType(t *testing.T) { + tmpl, err, line := Parse("template: {@}", nil); if err != nil { t.Error("unexpected parse error:", err) } - err = tmpl.Execute("hi", nil); - if err == nil { - t.Error("failed to detect string as driver type") + var b io.ByteBuffer; + err = tmpl.Execute("hello", &b); + s := string(b.Data()); + if s != "template: hello" { + t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s) } } -- 2.48.1