From 8b4834929d3566960e8041eb8ec0592b5b974968 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Sat, 19 Jul 2025 22:30:07 +0200 Subject: [PATCH] Clean code --- util/sprig/flow_control.go | 7 + util/sprig/functions.go | 410 +++++++++++++-------------- util/sprig/functions_windows_test.go | 28 -- util/sprig/numeric.go | 77 +++-- util/sprig/strings.go | 34 +++ 5 files changed, 287 insertions(+), 269 deletions(-) create mode 100644 util/sprig/flow_control.go delete mode 100644 util/sprig/functions_windows_test.go diff --git a/util/sprig/flow_control.go b/util/sprig/flow_control.go new file mode 100644 index 00000000..2bdf382c --- /dev/null +++ b/util/sprig/flow_control.go @@ -0,0 +1,7 @@ +package sprig + +import "errors" + +func fail(msg string) (string, error) { + return "", errors.New(msg) +} diff --git a/util/sprig/functions.go b/util/sprig/functions.go index c9b9f86b..27d52524 100644 --- a/util/sprig/functions.go +++ b/util/sprig/functions.go @@ -1,14 +1,9 @@ package sprig import ( - "errors" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "math/rand" "path" "path/filepath" "reflect" - "strconv" "strings" "text/template" "time" @@ -27,220 +22,195 @@ const ( // // TxtFuncMap returns a 'text/template'.FuncMap func TxtFuncMap() template.FuncMap { - gfm := make(map[string]any, len(genericMap)) - for k, v := range genericMap { - gfm[k] = v + return map[string]any{ + // Date functions + "ago": dateAgo, + "date": date, + "date_in_zone": dateInZone, + "date_modify": dateModify, + "dateInZone": dateInZone, + "dateModify": dateModify, + "duration": duration, + "durationRound": durationRound, + "htmlDate": htmlDate, + "htmlDateInZone": htmlDateInZone, + "must_date_modify": mustDateModify, + "mustDateModify": mustDateModify, + "mustToDate": mustToDate, + "now": time.Now, + "toDate": toDate, + "unixEpoch": unixEpoch, + + // Strings + "trunc": trunc, + "trim": strings.TrimSpace, + "upper": strings.ToUpper, + "lower": strings.ToLower, + "title": title, + "substr": substring, + "repeat": repeat, + "trimAll": trimAll, + "trimPrefix": trimPrefix, + "trimSuffix": trimSuffix, + "contains": contains, + "hasPrefix": hasPrefix, + "hasSuffix": hasSuffix, + "quote": quote, + "squote": squote, + "cat": cat, + "indent": indent, + "nindent": nindent, + "replace": replace, + "plural": plural, + "sha1sum": sha1sum, + "sha256sum": sha256sum, + "sha512sum": sha512sum, + "adler32sum": adler32sum, + "toString": strval, + + // Wrap Atoi to stop errors. + "atoi": atoi, + "seq": seq, + "toDecimal": toDecimal, + "split": split, + "splitList": splitList, + "splitn": splitn, + "toStrings": strslice, + + "until": until, + "untilStep": untilStep, + + // Basic arithmetic + "add1": add1, + "add": add, + "sub": sub, + "div": div, + "mod": mod, + "mul": mul, + "randInt": randInt, + "biggest": maxAsInt64, + "max": maxAsInt64, + "min": minAsInt64, + "maxf": maxAsFloat64, + "minf": minAsFloat64, + "ceil": ceil, + "floor": floor, + "round": round, + + // string slices. Note that we reverse the order b/c that's better + // for template processing. + "join": join, + "sortAlpha": sortAlpha, + + // Defaults + "default": dfault, + "empty": empty, + "coalesce": coalesce, + "all": all, + "any": anyNonEmpty, + "compact": compact, + "mustCompact": mustCompact, + "fromJSON": fromJSON, + "toJSON": toJSON, + "toPrettyJSON": toPrettyJSON, + "toRawJSON": toRawJSON, + "mustFromJSON": mustFromJSON, + "mustToJSON": mustToJSON, + "mustToPrettyJSON": mustToPrettyJSON, + "mustToRawJSON": mustToRawJSON, + "ternary": ternary, + + // Reflection + "typeOf": typeOf, + "typeIs": typeIs, + "typeIsLike": typeIsLike, + "kindOf": kindOf, + "kindIs": kindIs, + "deepEqual": reflect.DeepEqual, + + // Paths + "base": path.Base, + "dir": path.Dir, + "clean": path.Clean, + "ext": path.Ext, + "isAbs": path.IsAbs, + + // Filepaths + "osBase": filepath.Base, + "osClean": filepath.Clean, + "osDir": filepath.Dir, + "osExt": filepath.Ext, + "osIsAbs": filepath.IsAbs, + + // Encoding + "b64enc": base64encode, + "b64dec": base64decode, + "b32enc": base32encode, + "b32dec": base32decode, + + // Data Structures + "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable. + "list": list, + "dict": dict, + "get": get, + "set": set, + "unset": unset, + "hasKey": hasKey, + "pluck": pluck, + "keys": keys, + "pick": pick, + "omit": omit, + "values": values, + + "append": push, + "push": push, + "mustAppend": mustPush, + "mustPush": mustPush, + "prepend": prepend, + "mustPrepend": mustPrepend, + "first": first, + "mustFirst": mustFirst, + "rest": rest, + "mustRest": mustRest, + "last": last, + "mustLast": mustLast, + "initial": initial, + "mustInitial": mustInitial, + "reverse": reverse, + "mustReverse": mustReverse, + "uniq": uniq, + "mustUniq": mustUniq, + "without": without, + "mustWithout": mustWithout, + "has": has, + "mustHas": mustHas, + "slice": slice, + "mustSlice": mustSlice, + "concat": concat, + "dig": dig, + "chunk": chunk, + "mustChunk": mustChunk, + + // Flow Control + "fail": fail, + + // Regex + "regexMatch": regexMatch, + "mustRegexMatch": mustRegexMatch, + "regexFindAll": regexFindAll, + "mustRegexFindAll": mustRegexFindAll, + "regexFind": regexFind, + "mustRegexFind": mustRegexFind, + "regexReplaceAll": regexReplaceAll, + "mustRegexReplaceAll": mustRegexReplaceAll, + "regexReplaceAllLiteral": regexReplaceAllLiteral, + "mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral, + "regexSplit": regexSplit, + "mustRegexSplit": mustRegexSplit, + "regexQuoteMeta": regexQuoteMeta, + + // URLs + "urlParse": urlParse, + "urlJoin": urlJoin, } - return gfm -} - -var genericMap = map[string]any{ - // Date functions - "ago": dateAgo, - "date": date, - "date_in_zone": dateInZone, - "date_modify": dateModify, - "dateInZone": dateInZone, - "dateModify": dateModify, - "duration": duration, - "durationRound": durationRound, - "htmlDate": htmlDate, - "htmlDateInZone": htmlDateInZone, - "must_date_modify": mustDateModify, - "mustDateModify": mustDateModify, - "mustToDate": mustToDate, - "now": time.Now, - "toDate": toDate, - "unixEpoch": unixEpoch, - - // Strings - "trunc": trunc, - "trim": strings.TrimSpace, - "upper": strings.ToUpper, - "lower": strings.ToLower, - "title": func(s string) string { - return cases.Title(language.English).String(s) - }, - "substr": substring, - // Switch order so that "foo" | repeat 5 - "repeat": repeat, - "trimAll": func(a, b string) string { return strings.Trim(b, a) }, - "trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) }, - "trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) }, - // Switch order so that "foobar" | contains "foo" - "contains": func(substr string, str string) bool { return strings.Contains(str, substr) }, - "hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) }, - "hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) }, - "quote": quote, - "squote": squote, - "cat": cat, - "indent": indent, - "nindent": nindent, - "replace": replace, - "plural": plural, - "sha1sum": sha1sum, - "sha256sum": sha256sum, - "sha512sum": sha512sum, - "adler32sum": adler32sum, - "toString": strval, - - // Wrap Atoi to stop errors. - "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i }, - "seq": seq, - "toDecimal": toDecimal, - - // split "/" foo/bar returns map[int]string{0: foo, 1: bar} - "split": split, - "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) }, - // splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu} - "splitn": splitn, - "toStrings": strslice, - - "until": until, - "untilStep": untilStep, - - // VERY basic arithmetic. - "add1": func(i any) int64 { return toInt64(i) + 1 }, - "add": func(i ...any) int64 { - var a int64 = 0 - for _, b := range i { - a += toInt64(b) - } - return a - }, - "sub": func(a, b any) int64 { return toInt64(a) - toInt64(b) }, - "div": func(a, b any) int64 { return toInt64(a) / toInt64(b) }, - "mod": func(a, b any) int64 { return toInt64(a) % toInt64(b) }, - "mul": func(a any, v ...any) int64 { - val := toInt64(a) - for _, b := range v { - val = val * toInt64(b) - } - return val - }, - "randInt": func(min, max int) int { return rand.Intn(max-min) + min }, - "biggest": max, - "max": max, - "min": min, - "maxf": maxf, - "minf": minf, - "ceil": ceil, - "floor": floor, - "round": round, - - // string slices. Note that we reverse the order b/c that's better - // for template processing. - "join": join, - "sortAlpha": sortAlpha, - - // Defaults - "default": dfault, - "empty": empty, - "coalesce": coalesce, - "all": all, - "any": anyNonEmpty, - "compact": compact, - "mustCompact": mustCompact, - "fromJSON": fromJSON, - "toJSON": toJSON, - "toPrettyJSON": toPrettyJSON, - "toRawJSON": toRawJSON, - "mustFromJSON": mustFromJSON, - "mustToJSON": mustToJSON, - "mustToPrettyJSON": mustToPrettyJSON, - "mustToRawJSON": mustToRawJSON, - "ternary": ternary, - - // Reflection - "typeOf": typeOf, - "typeIs": typeIs, - "typeIsLike": typeIsLike, - "kindOf": kindOf, - "kindIs": kindIs, - "deepEqual": reflect.DeepEqual, - - // Paths: - "base": path.Base, - "dir": path.Dir, - "clean": path.Clean, - "ext": path.Ext, - "isAbs": path.IsAbs, - - // Filepaths: - "osBase": filepath.Base, - "osClean": filepath.Clean, - "osDir": filepath.Dir, - "osExt": filepath.Ext, - "osIsAbs": filepath.IsAbs, - - // Encoding: - "b64enc": base64encode, - "b64dec": base64decode, - "b32enc": base32encode, - "b32dec": base32decode, - - // Data Structures: - "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable. - "list": list, - "dict": dict, - "get": get, - "set": set, - "unset": unset, - "hasKey": hasKey, - "pluck": pluck, - "keys": keys, - "pick": pick, - "omit": omit, - "values": values, - - "append": push, - "push": push, - "mustAppend": mustPush, - "mustPush": mustPush, - "prepend": prepend, - "mustPrepend": mustPrepend, - "first": first, - "mustFirst": mustFirst, - "rest": rest, - "mustRest": mustRest, - "last": last, - "mustLast": mustLast, - "initial": initial, - "mustInitial": mustInitial, - "reverse": reverse, - "mustReverse": mustReverse, - "uniq": uniq, - "mustUniq": mustUniq, - "without": without, - "mustWithout": mustWithout, - "has": has, - "mustHas": mustHas, - "slice": slice, - "mustSlice": mustSlice, - "concat": concat, - "dig": dig, - "chunk": chunk, - "mustChunk": mustChunk, - - // Flow Control: - "fail": func(msg string) (string, error) { return "", errors.New(msg) }, - - // Regex - "regexMatch": regexMatch, - "mustRegexMatch": mustRegexMatch, - "regexFindAll": regexFindAll, - "mustRegexFindAll": mustRegexFindAll, - "regexFind": regexFind, - "mustRegexFind": mustRegexFind, - "regexReplaceAll": regexReplaceAll, - "mustRegexReplaceAll": mustRegexReplaceAll, - "regexReplaceAllLiteral": regexReplaceAllLiteral, - "mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral, - "regexSplit": regexSplit, - "mustRegexSplit": mustRegexSplit, - "regexQuoteMeta": regexQuoteMeta, - - // URLs: - "urlParse": urlParse, - "urlJoin": urlJoin, } diff --git a/util/sprig/functions_windows_test.go b/util/sprig/functions_windows_test.go deleted file mode 100644 index 9d8bd0e5..00000000 --- a/util/sprig/functions_windows_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package sprig - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestOsBase(t *testing.T) { - assert.NoError(t, runt(`{{ osBase "C:\\foo\\bar" }}`, "bar")) -} - -func TestOsDir(t *testing.T) { - assert.NoError(t, runt(`{{ osDir "C:\\foo\\bar\\baz" }}`, "C:\\foo\\bar")) -} - -func TestOsIsAbs(t *testing.T) { - assert.NoError(t, runt(`{{ osIsAbs "C:\\foo" }}`, "true")) - assert.NoError(t, runt(`{{ osIsAbs "foo" }}`, "false")) -} - -func TestOsClean(t *testing.T) { - assert.NoError(t, runt(`{{ osClean "C:\\foo\\..\\foo\\..\\bar" }}`, "C:\\bar")) -} - -func TestOsExt(t *testing.T) { - assert.NoError(t, runt(`{{ osExt "C:\\foo\\bar\\baz.txt" }}`, ".txt")) -} diff --git a/util/sprig/numeric.go b/util/sprig/numeric.go index 901fe3f3..32466818 100644 --- a/util/sprig/numeric.go +++ b/util/sprig/numeric.go @@ -3,6 +3,7 @@ package sprig import ( "fmt" "math" + "math/rand" "reflect" "strconv" "strings" @@ -78,7 +79,43 @@ func toInt64(v any) int64 { } } -func max(a any, i ...any) int64 { +func add1(i any) int64 { + return toInt64(i) + 1 +} + +func add(i ...any) int64 { + var a int64 + for _, b := range i { + a += toInt64(b) + } + return a +} + +func sub(a, b any) int64 { + return toInt64(a) - toInt64(b) +} + +func div(a, b any) int64 { + return toInt64(a) / toInt64(b) +} + +func mod(a, b any) int64 { + return toInt64(a) % toInt64(b) +} + +func mul(a any, v ...any) int64 { + val := toInt64(a) + for _, b := range v { + val = val * toInt64(b) + } + return val +} + +func randInt(min, max int) int { + return rand.Intn(max-min) + min +} + +func maxAsInt64(a any, i ...any) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) @@ -89,16 +126,15 @@ func max(a any, i ...any) int64 { return aa } -func maxf(a any, i ...any) float64 { - aa := toFloat64(a) +func maxAsFloat64(a any, i ...any) float64 { + m := toFloat64(a) for _, b := range i { - bb := toFloat64(b) - aa = math.Max(aa, bb) + m = math.Max(m, toFloat64(b)) } - return aa + return m } -func min(a any, i ...any) int64 { +func minAsInt64(a any, i ...any) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) @@ -109,13 +145,12 @@ func min(a any, i ...any) int64 { return aa } -func minf(a any, i ...any) float64 { - aa := toFloat64(a) +func minAsFloat64(a any, i ...any) float64 { + m := toFloat64(a) for _, b := range i { - bb := toFloat64(b) - aa = math.Min(aa, bb) + m = math.Min(m, toFloat64(b)) } - return aa + return m } func until(count int) []int { @@ -131,12 +166,10 @@ func untilStep(start, stop, step int) []int { if step == 0 { return v } - iterations := math.Abs(float64(stop)-float64(start)) / float64(step) if iterations > loopExecutionLimit { panic(fmt.Sprintf("too many iterations in untilStep; max allowed is %d, got %f", loopExecutionLimit, iterations)) } - if stop < start { if step >= 0 { return v @@ -146,7 +179,6 @@ func untilStep(start, stop, step int) []int { } return v } - if step <= 0 { return v } @@ -157,13 +189,11 @@ func untilStep(start, stop, step int) []int { } func floor(a any) float64 { - aa := toFloat64(a) - return math.Floor(aa) + return math.Floor(toFloat64(a)) } func ceil(a any) float64 { - aa := toFloat64(a) - return math.Ceil(aa) + return math.Ceil(toFloat64(a)) } func round(a any, p int, rOpt ...float64) float64 { @@ -195,6 +225,11 @@ func toDecimal(v any) int64 { return result } +func atoi(a string) int { + i, _ := strconv.Atoi(a) + return i +} + func seq(params ...int) string { increment := 1 switch len(params) { @@ -231,6 +266,6 @@ func seq(params ...int) string { } } -func intArrayToString(slice []int, delimeter string) string { - return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]") +func intArrayToString(slice []int, delimiter string) string { + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimiter), "[]") } diff --git a/util/sprig/strings.go b/util/sprig/strings.go index 11459a4b..8a1bdc1b 100644 --- a/util/sprig/strings.go +++ b/util/sprig/strings.go @@ -4,6 +4,8 @@ import ( "encoding/base32" "encoding/base64" "fmt" + "golang.org/x/text/cases" + "golang.org/x/text/language" "reflect" "strconv" "strings" @@ -149,6 +151,10 @@ func trunc(c int, s string) string { return s } +func title(s string) string { + return cases.Title(language.English).String(s) +} + func join(sep string, v any) string { return strings.Join(strslice(v), sep) } @@ -162,6 +168,10 @@ func split(sep, orig string) map[string]string { return res } +func splitList(sep, orig string) []string { + return strings.Split(orig, sep) +} + func splitn(sep string, n int, orig string) map[string]string { parts := strings.SplitN(orig, sep, n) res := make(map[string]string, len(parts)) @@ -196,3 +206,27 @@ func repeat(count int, str string) string { } return strings.Repeat(str, count) } + +func trimAll(a, b string) string { + return strings.Trim(b, a) +} + +func trimPrefix(a, b string) string { + return strings.TrimPrefix(b, a) +} + +func trimSuffix(a, b string) string { + return strings.TrimSuffix(b, a) +} + +func contains(substr string, str string) bool { + return strings.Contains(str, substr) +} + +func hasPrefix(substr string, str string) bool { + return strings.HasPrefix(str, substr) +} + +func hasSuffix(substr string, str string) bool { + return strings.HasSuffix(str, substr) +}