diff --git a/builtins.go b/builtins.go index e426b069..1d734401 100644 --- a/builtins.go +++ b/builtins.go @@ -1324,6 +1324,47 @@ func builtinSplitLimit(i *interpreter, strv, cv, maxSplitsV value) (value, error return makeValueArray(res), nil } +func builtinSplitLimitR(i *interpreter, strv, cv, maxSplitsV value) (value, error) { + str, err := i.getString(strv) + if err != nil { + return nil, err + } + c, err := i.getString(cv) + if err != nil { + return nil, err + } + maxSplits, err := i.getInt(maxSplitsV) + if err != nil { + return nil, err + } + if maxSplits < -1 { + return nil, i.Error(fmt.Sprintf("std.splitLimitR third parameter should be -1 or non-negative, got %v", maxSplits)) + } + sStr := str.getGoString() + sC := c.getGoString() + if len(sC) < 1 { + return nil, i.Error(fmt.Sprintf("std.splitLimitR second parameter should have length 1 or greater, got %v", len(sC))) + } + + count := strings.Count(sStr, sC) + if maxSplits > -1 && count > maxSplits { + count = maxSplits + } + strs := make([]string, count+1) + for i := count; i > 0; i-- { + index := strings.LastIndex(sStr, sC) + strs[i] = sStr[index+len(sC):] + sStr = sStr[:index] + } + strs[0] = sStr + res := make([]*cachedThunk, len(strs)) + for i := range strs { + res[i] = readyThunk(makeValueString(strs[i])) + } + + return makeValueArray(res), nil +} + func builtinStrReplace(i *interpreter, strv, fromv, tov value) (value, error) { str, err := i.getString(strv) if err != nil { @@ -2511,6 +2552,7 @@ var funcBuiltins = buildBuiltinMap([]builtin{ &binaryBuiltin{name: "stripChars", function: builtinStripChars, params: ast.Identifiers{"str", "chars"}}, &ternaryBuiltin{name: "substr", function: builtinSubstr, params: ast.Identifiers{"str", "from", "len"}}, &ternaryBuiltin{name: "splitLimit", function: builtinSplitLimit, params: ast.Identifiers{"str", "c", "maxsplits"}}, + &ternaryBuiltin{name: "splitLimitR", function: builtinSplitLimitR, params: ast.Identifiers{"str", "c", "maxsplits"}}, &ternaryBuiltin{name: "strReplace", function: builtinStrReplace, params: ast.Identifiers{"str", "from", "to"}}, &unaryBuiltin{name: "isEmpty", function: builtinIsEmpty, params: ast.Identifiers{"str"}}, &binaryBuiltin{name: "equalsIgnoreCase", function: builtinEqualsIgnoreCase, params: ast.Identifiers{"str1", "str2"}}, diff --git a/linter/internal/types/stdlib.go b/linter/internal/types/stdlib.go index 63c0eed3..2bbc2770 100644 --- a/linter/internal/types/stdlib.go +++ b/linter/internal/types/stdlib.go @@ -90,6 +90,7 @@ func prepareStdlib(g *typeGraph) { "rstripChars": g.newSimpleFuncType(stringType, "str", "chars"), "split": g.newSimpleFuncType(arrayOfString, "str", "c"), "splitLimit": g.newSimpleFuncType(arrayOfString, "str", "c", "maxsplits"), + "splitLimitR": g.newSimpleFuncType(arrayOfString, "str", "c", "maxsplits"), "strReplace": g.newSimpleFuncType(stringType, "str", "from", "to"), "asciiUpper": g.newSimpleFuncType(stringType, "str"), "asciiLower": g.newSimpleFuncType(stringType, "str"), diff --git a/testdata/builtinSplitLimitR.golden b/testdata/builtinSplitLimitR.golden new file mode 100644 index 00000000..711ef61d --- /dev/null +++ b/testdata/builtinSplitLimitR.golden @@ -0,0 +1,5 @@ +[ + "foo,bar", + "baz", + "qux" +] diff --git a/testdata/builtinSplitLimitR.jsonnet b/testdata/builtinSplitLimitR.jsonnet new file mode 100644 index 00000000..85aa16cb --- /dev/null +++ b/testdata/builtinSplitLimitR.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', ',', 2) diff --git a/testdata/builtinSplitLimitR.linter.golden b/testdata/builtinSplitLimitR.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinSplitLimitR2.golden b/testdata/builtinSplitLimitR2.golden new file mode 100644 index 00000000..890b11bf --- /dev/null +++ b/testdata/builtinSplitLimitR2.golden @@ -0,0 +1,3 @@ +[ + "foo,bar,baz,qux" +] diff --git a/testdata/builtinSplitLimitR2.jsonnet b/testdata/builtinSplitLimitR2.jsonnet new file mode 100644 index 00000000..802e95ac --- /dev/null +++ b/testdata/builtinSplitLimitR2.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', ',', 0) diff --git a/testdata/builtinSplitLimitR2.linter.golden b/testdata/builtinSplitLimitR2.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinSplitLimitR3.golden b/testdata/builtinSplitLimitR3.golden new file mode 100644 index 00000000..054ad80f --- /dev/null +++ b/testdata/builtinSplitLimitR3.golden @@ -0,0 +1,6 @@ +[ + "foo", + "bar", + "baz", + "qux" +] diff --git a/testdata/builtinSplitLimitR3.jsonnet b/testdata/builtinSplitLimitR3.jsonnet new file mode 100644 index 00000000..5e6dda8c --- /dev/null +++ b/testdata/builtinSplitLimitR3.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', ',', -1) diff --git a/testdata/builtinSplitLimitR3.linter.golden b/testdata/builtinSplitLimitR3.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinSplitLimitR4.golden b/testdata/builtinSplitLimitR4.golden new file mode 100644 index 00000000..054ad80f --- /dev/null +++ b/testdata/builtinSplitLimitR4.golden @@ -0,0 +1,6 @@ +[ + "foo", + "bar", + "baz", + "qux" +] diff --git a/testdata/builtinSplitLimitR4.jsonnet b/testdata/builtinSplitLimitR4.jsonnet new file mode 100644 index 00000000..31a59c31 --- /dev/null +++ b/testdata/builtinSplitLimitR4.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', ',', 4) diff --git a/testdata/builtinSplitLimitR4.linter.golden b/testdata/builtinSplitLimitR4.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinSplitLimitR5.golden b/testdata/builtinSplitLimitR5.golden new file mode 100644 index 00000000..1d2d01e7 --- /dev/null +++ b/testdata/builtinSplitLimitR5.golden @@ -0,0 +1,10 @@ +RUNTIME ERROR: std.splitLimitR third parameter should be -1 or non-negative, got -2 +------------------------------------------------- + testdata/builtinSplitLimitR5:1:1-44 $ + +std.splitLimitR('foo,bar,baz,qux', ',', -2) + +------------------------------------------------- + During evaluation + + diff --git a/testdata/builtinSplitLimitR5.jsonnet b/testdata/builtinSplitLimitR5.jsonnet new file mode 100644 index 00000000..bced0a5d --- /dev/null +++ b/testdata/builtinSplitLimitR5.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', ',', -2) diff --git a/testdata/builtinSplitLimitR5.linter.golden b/testdata/builtinSplitLimitR5.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinSplitLimitR6.golden b/testdata/builtinSplitLimitR6.golden new file mode 100644 index 00000000..ebd98836 --- /dev/null +++ b/testdata/builtinSplitLimitR6.golden @@ -0,0 +1,10 @@ +RUNTIME ERROR: std.splitLimitR second parameter should have length 1 or greater, got 0 +------------------------------------------------- + testdata/builtinSplitLimitR6:1:1-43 $ + +std.splitLimitR('foo,bar,baz,qux', '', -1) + +------------------------------------------------- + During evaluation + + diff --git a/testdata/builtinSplitLimitR6.jsonnet b/testdata/builtinSplitLimitR6.jsonnet new file mode 100644 index 00000000..d90ffbe0 --- /dev/null +++ b/testdata/builtinSplitLimitR6.jsonnet @@ -0,0 +1 @@ +std.splitLimitR('foo,bar,baz,qux', '', -1) diff --git a/testdata/builtinSplitLimitR6.linter.golden b/testdata/builtinSplitLimitR6.linter.golden new file mode 100644 index 00000000..e69de29b