binwiederhier 7 месяцев назад
Родитель
Сommit
8b4834929d
5 измененных файлов с 287 добавлено и 269 удалено
  1. 7 0
      util/sprig/flow_control.go
  2. 190 220
      util/sprig/functions.go
  3. 0 28
      util/sprig/functions_windows_test.go
  4. 56 21
      util/sprig/numeric.go
  5. 34 0
      util/sprig/strings.go

+ 7 - 0
util/sprig/flow_control.go

@@ -0,0 +1,7 @@
+package sprig
+
+import "errors"
+
+func fail(msg string) (string, error) {
+	return "", errors.New(msg)
+}

+ 190 - 220
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,
 }

+ 0 - 28
util/sprig/functions_windows_test.go

@@ -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"))
-}

+ 56 - 21
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), "[]")
 }

+ 34 - 0
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)
+}