Skip to content

Commit

Permalink
(PL-4302): support array indexed fields
Browse files Browse the repository at this point in the history
  • Loading branch information
ajinkya-verloop committed Sep 24, 2024
1 parent d1d3420 commit b81a617
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
35 changes: 35 additions & 0 deletions mustache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions mustache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit b81a617

Please sign in to comment.