// Taken and adapted from the stdlib text/template/funcs.go. // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package texttemplate import ( "fmt" "math" "reflect" ) // indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible. // //nolint:revive func indexArg(index reflect.Value, cap int) (int, error) { 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, reflect.Uintptr: val := index.Uint() if val > math.MaxInt64 { return -1, fmt.Errorf("index too large: %d", val) } x = int64(val) case reflect.Invalid: return 0, fmt.Errorf("cannot index slice/array with nil") default: return 0, fmt.Errorf("cannot index slice/array with type %s", index.Type()) } if x < 0 || int(x) < 0 || int(x) > cap { return 0, fmt.Errorf("index out of range: %d", x) } return int(x), nil } // Slicing. // slice returns the result of slicing its first argument by the remaining // arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2], while "slice x" // is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first // argument must be a string, slice, or array. func GoSlice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { item = indirectInterface(item) if !item.IsValid() { return reflect.Value{}, fmt.Errorf("slice of untyped nil") } if len(indexes) > 3 { return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len(indexes)) } var capacity int switch item.Kind() { case reflect.String: if len(indexes) == 3 { return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string") } capacity = item.Len() case reflect.Array, reflect.Slice: capacity = item.Cap() default: return reflect.Value{}, fmt.Errorf("can't slice item of type %s", item.Type()) } // set default values for cases item[:], item[i:]. idx := [3]int{0, item.Len()} for i, index := range indexes { x, err := indexArg(index, capacity) if err != nil { return reflect.Value{}, err } idx[i] = x } // given item[i:j], make sure i <= j. if idx[0] > idx[1] { return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1]) } if len(indexes) < 3 { return item.Slice(idx[0], idx[1]), nil } // given item[i:j:k], make sure i <= j <= k. if idx[1] > idx[2] { return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2]) } return item.Slice3(idx[0], idx[1], idx[2]), nil }