diff --git a/mustache.go b/mustache.go index 533d533..bc7bad4 100644 --- a/mustache.go +++ b/mustache.go @@ -470,6 +470,31 @@ func (tmpl *Template) parse() error { } } +func lookupIndexedField(name string, v reflect.Value) (reflect.Value, error) { + arrayFields := strings.SplitN(name, "[", 2) + // if it is a valid index field, e.g. [1] then arrayFields length will be 2 + // for a valid index, name starts with [ and arrayFields[0] is always empty + if len(arrayFields) == 2 && len(arrayFields[0]) == 0 { + switch reflect.TypeOf(v.Interface()).Kind() { + case reflect.Array, reflect.Slice: + indexFields := strings.SplitN(arrayFields[1], "]", 2) + index, err := strconv.ParseInt(indexFields[0], 10, 64) + if err != nil { + return reflect.Value{}, nil + } + // if it is a valid index field e.g [1] then indexFields length is always 2 + if len(indexFields) == 2 { + // if there are more indexed fields then indexFields[1] length is always more than 0 + if len(indexFields[1]) != 0 { + return lookupIndexedField(indexFields[1], v.Elem().Index(int(index))) + } + return v.Elem().Index(int(index)), nil + } + } + } + return reflect.Value{}, nil +} + // Evaluate interfaces and pointers looking for a value that can look up the name, via a // struct field, method, or map key, and return the result of the lookup. func lookup(contextChain []interface{}, name string, allowMissing bool) (reflect.Value, error) { @@ -484,6 +509,16 @@ func lookup(contextChain []interface{}, name string, allowMissing bool) (reflect return lookup([]interface{}{v}, parts[1], allowMissing) } + if strings.Contains(name, "[") { + // trying to access the indexed field of the array + arrayFields := strings.SplitN(name, "[", 2) + v, err := lookup(contextChain, arrayFields[0], allowMissing) + if err != nil { + return v, err + } + return lookupIndexedField("["+arrayFields[1], v) + } + defer func() { if r := recover(); r != nil { fmt.Printf("Panic while looking up %q: %s\n", name, r) diff --git a/mustache_test.go b/mustache_test.go index 8f92320..0eab0e3 100644 --- a/mustache_test.go +++ b/mustache_test.go @@ -736,3 +736,15 @@ func TestCustomEscape(t *testing.T) { t.Errorf("expected %s, got %v", expected, value) } } + +func TestArrayIndexedFields(t *testing.T) { + value, err := RenderRaw("Hello {{value}} \n 0th index {{value[0]}} \n 1th index {{value[1]}} \n 2[1].a[0]th index {{value[2][1].a[0]}}", true, map[string]interface{}{"value": []interface{}{"world1", "world2", []interface{}{"1", map[string]interface{}{"a": []string{"b", "c"}}, "3"}}}) + if err != nil { + t.Errorf("expected to be rendered, got %v", err) + } + const expected = "Hello [\"world1\",\"world2\",[\"1\",{\"a\":[\"b\",\"c\"]},\"3\"]] \n 0th index world1 \n 1th index world2 \n 2[1].a[0]th index b" + + if value != expected { + t.Errorf("expected %s, got %v", expected, value) + } +}