ref: 5b0245ca59e22d90add28b11898ecfd602429e43
parent: beb423a2d9683dfc07b2f69766e17c32589d489b
author: Anthony Fok <foka@debian.org>
date: Mon Mar 23 07:23:13 EDT 2015
Implement substr template function Its behavior is similar to that in JavaScript with special handling of negative length as found in in PHP. Fixes #991
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -210,6 +210,69 @@
}
+// Substr extracts parts of a string, beginning at the character at the specified
+// position, and returns the specified number of characters.
+//
+// It normally takes two parameters: start and length.
+// It can also take one parameter: start, i.e. length is omitted, in which case
+// the substring starting from start until the end of the string will be returned.
+//
+// To extract characters from the end of the string, use a negative start number.
+//
+// In addition, borrowing from the extended behavior described at http://php.net/substr,
+// if length is given and is negative, then that many characters will be omitted from
+// the end of string.
+func Substr(a interface{}, nums ...int) (string, error) {+ aStr, err := cast.ToStringE(a)
+ if err != nil {+ return "", err
+ }
+
+ var start, length int
+ switch len(nums) {+ case 1:
+ start = nums[0]
+ length = len(aStr)
+ case 2:
+ start = nums[0]
+ length = nums[1]
+ default:
+ return "", errors.New("too many arguments")+ }
+
+ if start < -len(aStr) {+ start = 0
+ }
+ if start > len(aStr) {+ return "", errors.New(fmt.Sprintf("start position out of bounds for %d-byte string", len(aStr)))+ }
+
+ var s, e int
+ if start >= 0 && length >= 0 {+ s = start
+ e = start + length
+ } else if start < 0 && length >= 0 {+ s = len(aStr) + start - length + 1
+ e = len(aStr) + start + 1
+ } else if start >= 0 && length < 0 {+ s = start
+ e = len(aStr) + length
+ } else {+ s = len(aStr) + start
+ e = len(aStr) + length
+ }
+
+ if s > e {+ return "", errors.New(fmt.Sprintf("calculated start position greater than end position: %d > %d", s, e))+ }
+ if e > len(aStr) {+ e = len(aStr)
+ }
+
+ return aStr[s:e], nil
+
+}
+
func Split(a interface{}, delimiter string) ([]string, error) {aStr, err := cast.ToStringE(a)
if err != nil {@@ -1339,6 +1402,7 @@
"le": Le,
"in": In,
"slicestr": Slicestr,
+ "substr": Substr,
"split": Split,
"intersect": Intersect,
"isSet": IsSet,
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -310,6 +310,46 @@
}
}
+func TestSubstr(t *testing.T) {+ for i, this := range []struct {+ v1 interface{}+ v2 int
+ v3 int
+ expect interface{}+ }{+ {"abc", 1, 2, "bc"},+ {"abc", 0, 1, "a"},+ {"abcdef", -1, 2, "ef"},+ {"abcdef", -3, 3, "bcd"},+ {"abcdef", 0, -1, "abcde"},+ {"abcdef", 2, -1, "cde"},+ {"abcdef", 4, -4, false},+ {"abcdef", 7, 1, false},+ {"abcdef", 1, 100, "bcdef"},+ {"abcdef", -100, 3, "abc"},+ {"abcdef", -3, -1, "de"},+ {123, 1, 3, "23"},+ {1.2e3, 0, 4, "1200"},+ {tstNoStringer{}, 0, 1, false},+ } {+ result, err := Substr(this.v1, this.v2, this.v3)
+
+ if b, ok := this.expect.(bool); ok && !b {+ if err == nil {+ t.Errorf("[%d] Substr didn't return an expected error", i)+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {+ t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)+ }
+ }
+ }
+}
+
func TestSplit(t *testing.T) { for i, this := range []struct { v1 interface{}--
⑨