mirror of
https://github.com/golang/go
synced 2024-11-22 02:44:39 -07:00
exp/template: index function for arrays, slices, and maps.
R=golang-dev, adg, r CC=golang-dev https://golang.org/cl/4643072
This commit is contained in:
parent
64228e36a4
commit
6732bd3526
@ -31,6 +31,7 @@ type T struct {
|
|||||||
MSI map[string]int
|
MSI map[string]int
|
||||||
MSIone map[string]int // one element, for deterministic output
|
MSIone map[string]int // one element, for deterministic output
|
||||||
MSIEmpty map[string]int
|
MSIEmpty map[string]int
|
||||||
|
SMSI []map[string]int
|
||||||
// Empty interface; used to see if we can dig inside one.
|
// Empty interface; used to see if we can dig inside one.
|
||||||
EmptyInterface interface{}
|
EmptyInterface interface{}
|
||||||
}
|
}
|
||||||
@ -89,6 +90,10 @@ var tVal = &T{
|
|||||||
SB: []bool{true, false},
|
SB: []bool{true, false},
|
||||||
MSI: map[string]int{"one": 1, "two": 2, "three": 3},
|
MSI: map[string]int{"one": 1, "two": 2, "three": 3},
|
||||||
MSIone: map[string]int{"one": 1},
|
MSIone: map[string]int{"one": 1},
|
||||||
|
SMSI: []map[string]int{
|
||||||
|
{"one": 1, "two": 2},
|
||||||
|
{"eleven": 11, "twelve": 12},
|
||||||
|
},
|
||||||
EmptyInterface: []int{7, 8},
|
EmptyInterface: []int{7, 8},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +171,16 @@ var execTests = []execTest{
|
|||||||
{"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true},
|
{"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true},
|
||||||
{"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true},
|
{"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true},
|
||||||
{"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
|
{"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
|
||||||
|
// Indexing.
|
||||||
|
{"slice[0]", "{{index .SI 0}}", "3", tVal, true},
|
||||||
|
{"slice[1]", "{{index .SI 1}}", "4", tVal, true},
|
||||||
|
{"slice[HUGE]", "{{index .SI 10}}", "", tVal, false},
|
||||||
|
{"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
|
||||||
|
{"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
|
||||||
|
{"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
|
||||||
|
{"map[NO]", "{{index .MSI `XXX`}}", "", tVal, false},
|
||||||
|
{"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
|
||||||
|
{"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
|
||||||
// With.
|
// With.
|
||||||
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
|
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
|
||||||
{"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
|
{"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
|
||||||
@ -222,7 +237,10 @@ func testExecute(execTests []execTest, set *Set, t *testing.T) {
|
|||||||
t.Errorf("%s: unexpected execute error: %s", test.name, err)
|
t.Errorf("%s: unexpected execute error: %s", test.name, err)
|
||||||
continue
|
continue
|
||||||
case !test.ok && err != nil:
|
case !test.ok && err != nil:
|
||||||
continue
|
// expected error, got one
|
||||||
|
if *debug {
|
||||||
|
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result := b.String()
|
result := b.String()
|
||||||
if result != test.output {
|
if result != test.output {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -20,12 +21,13 @@ import (
|
|||||||
type FuncMap map[string]interface{}
|
type FuncMap map[string]interface{}
|
||||||
|
|
||||||
var funcs = map[string]reflect.Value{
|
var funcs = map[string]reflect.Value{
|
||||||
"printf": reflect.ValueOf(fmt.Sprintf),
|
|
||||||
"html": reflect.ValueOf(HTMLEscaper),
|
|
||||||
"js": reflect.ValueOf(JSEscaper),
|
|
||||||
"and": reflect.ValueOf(and),
|
"and": reflect.ValueOf(and),
|
||||||
"or": reflect.ValueOf(or),
|
"html": reflect.ValueOf(HTMLEscaper),
|
||||||
|
"index": reflect.ValueOf(index),
|
||||||
|
"js": reflect.ValueOf(JSEscaper),
|
||||||
"not": reflect.ValueOf(not),
|
"not": reflect.ValueOf(not),
|
||||||
|
"or": reflect.ValueOf(or),
|
||||||
|
"printf": reflect.ValueOf(fmt.Sprintf),
|
||||||
}
|
}
|
||||||
|
|
||||||
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
||||||
@ -72,6 +74,45 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
|
|||||||
return reflect.Value{}, false
|
return reflect.Value{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indexing.
|
||||||
|
|
||||||
|
// index returns the result of indexing its first argument by the following
|
||||||
|
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
|
||||||
|
// indexed item must be a map, slice, or array.
|
||||||
|
func index(item interface{}, indices ...interface{}) (interface{}, os.Error) {
|
||||||
|
v := reflect.ValueOf(item)
|
||||||
|
for _, i := range indices {
|
||||||
|
index := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
var x int64
|
||||||
|
switch index.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
x = index.Int()
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
x = int64(index.Uint())
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
|
||||||
|
}
|
||||||
|
if x < 0 || x >= int64(v.Len()) {
|
||||||
|
return nil, fmt.Errorf("index out of range: %d", x)
|
||||||
|
}
|
||||||
|
v = v.Index(int(x))
|
||||||
|
case reflect.Map:
|
||||||
|
if !index.Type().AssignableTo(v.Type().Key()) {
|
||||||
|
return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
|
||||||
|
}
|
||||||
|
v = v.MapIndex(index)
|
||||||
|
if !v.IsValid() {
|
||||||
|
return nil, fmt.Errorf("index %v not present in map", index.Interface())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("can't index item of type %s", index.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Boolean logic.
|
// Boolean logic.
|
||||||
|
|
||||||
// and returns the Boolean AND of its arguments.
|
// and returns the Boolean AND of its arguments.
|
||||||
|
Loading…
Reference in New Issue
Block a user