mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
tpl: Refactor and fix substr logic
Fix miscalculations when start is negative. Results should now match PHP substr. Fixes #7993
This commit is contained in:
parent
32d4bf68da
commit
64789fb5dc
2 changed files with 46 additions and 34 deletions
|
@ -279,64 +279,73 @@ func (ns *Namespace) Split(a interface{}, delimiter string) ([]string, error) {
|
||||||
// if length is given and is negative, then that many characters will be omitted from
|
// if length is given and is negative, then that many characters will be omitted from
|
||||||
// the end of string.
|
// the end of string.
|
||||||
func (ns *Namespace) Substr(a interface{}, nums ...interface{}) (string, error) {
|
func (ns *Namespace) Substr(a interface{}, nums ...interface{}) (string, error) {
|
||||||
aStr, err := cast.ToStringE(a)
|
s, err := cast.ToStringE(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var start, length int
|
asRunes := []rune(s)
|
||||||
|
rlen := len(asRunes)
|
||||||
|
|
||||||
asRunes := []rune(aStr)
|
var start, length int
|
||||||
|
|
||||||
switch len(nums) {
|
switch len(nums) {
|
||||||
case 0:
|
case 0:
|
||||||
return "", errors.New("too less arguments")
|
return "", errors.New("too few arguments")
|
||||||
case 1:
|
case 1:
|
||||||
if start, err = cast.ToIntE(nums[0]); err != nil {
|
if start, err = cast.ToIntE(nums[0]); err != nil {
|
||||||
return "", errors.New("start argument must be integer")
|
return "", errors.New("start argument must be an integer")
|
||||||
}
|
}
|
||||||
length = len(asRunes)
|
length = rlen
|
||||||
case 2:
|
case 2:
|
||||||
if start, err = cast.ToIntE(nums[0]); err != nil {
|
if start, err = cast.ToIntE(nums[0]); err != nil {
|
||||||
return "", errors.New("start argument must be integer")
|
return "", errors.New("start argument must be an integer")
|
||||||
}
|
}
|
||||||
if length, err = cast.ToIntE(nums[1]); err != nil {
|
if length, err = cast.ToIntE(nums[1]); err != nil {
|
||||||
return "", errors.New("length argument must be integer")
|
return "", errors.New("length argument must be an integer")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return "", errors.New("too many arguments")
|
return "", errors.New("too many arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
if start < -len(asRunes) {
|
if rlen == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if start < 0 {
|
||||||
|
start += rlen
|
||||||
|
}
|
||||||
|
|
||||||
|
// start was originally negative beyond rlen
|
||||||
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if start > len(asRunes) {
|
|
||||||
return "", fmt.Errorf("start position out of bounds for %d-byte string", len(aStr))
|
if start > rlen-1 {
|
||||||
|
return "", fmt.Errorf("start position out of bounds for %d-byte string", rlen)
|
||||||
}
|
}
|
||||||
|
|
||||||
var s, e int
|
end := rlen
|
||||||
if start >= 0 && length >= 0 {
|
|
||||||
s = start
|
if length < 0 {
|
||||||
e = start + length
|
end += length
|
||||||
} else if start < 0 && length >= 0 {
|
} else if length > 0 {
|
||||||
s = len(asRunes) + start - length + 1
|
end = start + length
|
||||||
e = len(asRunes) + start + 1
|
|
||||||
} else if start >= 0 && length < 0 {
|
|
||||||
s = start
|
|
||||||
e = len(asRunes) + length
|
|
||||||
} else {
|
|
||||||
s = len(asRunes) + start
|
|
||||||
e = len(asRunes) + length
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s > e {
|
if start >= end {
|
||||||
return "", fmt.Errorf("calculated start position greater than end position: %d > %d", s, e)
|
return "", fmt.Errorf("calculated start position greater than end position: %d > %d", start, end)
|
||||||
}
|
|
||||||
if e > len(asRunes) {
|
|
||||||
e = len(asRunes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(asRunes[s:e]), nil
|
if end < 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > rlen {
|
||||||
|
end = rlen
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(asRunes[start:end]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title returns a copy of the input s with all Unicode letters that begin words
|
// Title returns a copy of the input s with all Unicode letters that begin words
|
||||||
|
|
|
@ -441,8 +441,11 @@ func TestSubstr(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{"abc", 1, 2, "bc"},
|
{"abc", 1, 2, "bc"},
|
||||||
{"abc", 0, 1, "a"},
|
{"abc", 0, 1, "a"},
|
||||||
{"abcdef", -1, 2, "ef"},
|
{"abcdef", -1, 2, "f"},
|
||||||
{"abcdef", -3, 3, "bcd"},
|
{"abcdef", -3, 3, "def"},
|
||||||
|
{"abcdef", -1, nil, "f"},
|
||||||
|
{"abcdef", -2, nil, "ef"},
|
||||||
|
{"abcdef", -3, 1, "d"},
|
||||||
{"abcdef", 0, -1, "abcde"},
|
{"abcdef", 0, -1, "abcde"},
|
||||||
{"abcdef", 2, -1, "cde"},
|
{"abcdef", 2, -1, "cde"},
|
||||||
{"abcdef", 4, -4, false},
|
{"abcdef", 4, -4, false},
|
||||||
|
@ -480,12 +483,12 @@ func TestSubstr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, ok := test.expect.(bool); ok && !b {
|
if b, ok := test.expect.(bool); ok && !b {
|
||||||
c.Assert(err, qt.Not(qt.IsNil))
|
c.Assert(err, qt.Not(qt.IsNil), qt.Commentf("%v", test))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
c.Assert(result, qt.Equals, test.expect)
|
c.Assert(result, qt.Equals, test.expect, qt.Commentf("%v", test))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ns.Substr("abcdef")
|
_, err = ns.Substr("abcdef")
|
||||||
|
|
Loading…
Reference in a new issue