diff --git a/ext/README.md b/ext/README.md index 6f621ac4..2fac0cb2 100644 --- a/ext/README.md +++ b/ext/README.md @@ -414,3 +414,17 @@ Examples: 'TacoCat'.upperAscii() // returns 'TACOCAT' 'TacoCÆt Xii'.upperAscii() // returns 'TACOCÆT XII' + +### Reverse + +Returns a new string whose characters are the same as the target string, only formatted in +reverse order. +This function relies on converting strings to rune arrays in order to reverse. +It can be located in Version 3 of strings. + + .reverse() -> + +Examples: + + 'gums'.reverse() // returns 'smug' + 'John Smith'.reverse() // returns 'htimS nhoJ' \ No newline at end of file diff --git a/ext/strings.go b/ext/strings.go index 935c9b6a..13e91954 100644 --- a/ext/strings.go +++ b/ext/strings.go @@ -267,6 +267,19 @@ const ( // // 'TacoCat'.upperAscii() // returns 'TACOCAT' // 'TacoCÆt Xii'.upperAscii() // returns 'TACOCÆT XII' +// +// # Reverse +// +// Returns a new string whose characters are the same as the target string, only formatted in +// reverse order. +// This function relies on converting strings to rune arrays in order to reverse +// +// .reverse() -> +// +// Examples: +// +// 'gums'.reverse() // returns 'smug' +// 'John Smith'.reverse() // returns 'htimS nhoJ' func Strings(options ...StringsOption) cel.EnvOption { s := &stringLib{ version: math.MaxUint32, @@ -500,6 +513,16 @@ func (lib *stringLib) CompileOptions() []cel.EnvOption { }))), ) } + if lib.version >= 3 { + opts = append( opts, + cel.Function("reverse", + cel.MemberOverload("reverse", []*cel.Type{cel.StringType}, cel.StringType, + cel.UnaryBinding(func(str ref.Val) ref.Val { + s := str.(types.String) + return stringOrError(reverse(string(s))) + }))), + ) + } if lib.validateFormat { opts = append(opts, cel.ASTValidators(stringFormatValidator{})) } @@ -653,6 +676,14 @@ func upperASCII(str string) (string, error) { return string(runes), nil } +func reverse(str string) (string, error) { + chars := []rune(str) + for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { + chars[i], chars[j] = chars[j], chars[i] + } + return string(chars), nil +} + func joinSeparator(strs []string, separator string) (string, error) { return strings.Join(strs, separator), nil } diff --git a/ext/strings_test.go b/ext/strings_test.go index 002226eb..6c1583ad 100644 --- a/ext/strings_test.go +++ b/ext/strings_test.go @@ -105,6 +105,13 @@ var stringTests = []struct { // Upper ASCII tests. {expr: `'tacoCat'.upperAscii() == 'TACOCAT'`}, {expr: `'tacoCαt'.upperAscii() == 'TACOCαT'`}, + // Reverse tests. + {expr: `'gums'.reverse() == 'smug'`}, + {expr: `'palindromes'.reverse() == 'semordnilap'`}, + {expr: `'John Smith'.reverse() == 'htimS nhoJ'`}, + {expr: `'u180etext'.reverse() == 'txete081u'`}, + {expr: `'2600+U'.reverse() == 'U+0062'`}, + {expr: `'\u180e\u200b\u200c\u200d\u2060\ufeff'.reverse() == '\ufeff\u2060\u200d\u200c\u200b\u180e'`}, // Join tests. {expr: `['x', 'y'].join() == 'xy'`}, {expr: `['x', 'y'].join('-') == 'x-y'`},