refactor: include additional important template funcs (#4690)

* refactor: include additional important template funcs

* fix: use of interface

* test: improve test cases
pull/4675/head^2
James Elliott 2023-01-04 00:11:10 +11:00 committed by GitHub
parent a4718e4fd0
commit 08cda5d165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 236 additions and 120 deletions

View File

@ -76,6 +76,8 @@ The following functions which mimic the behaviour of helm exist in most templati
- typeIsLike - typeIsLike
- kindOf - kindOf
- kindIs - kindIs
- default
- empty
See the [Helm Documentation](https://helm.sh/docs/chart_template_guide/function_list/) for more information. Please See the [Helm Documentation](https://helm.sh/docs/chart_template_guide/function_list/) for more information. Please
note that only the functions listed above are supported and the functions don't necessarily behave exactly the same. note that only the functions listed above are supported and the functions don't necessarily behave exactly the same.

View File

@ -71,77 +71,11 @@ func FuncMap() map[string]any {
"typeIsLike": FuncTypeIsLike, "typeIsLike": FuncTypeIsLike,
"kindOf": FuncKindOf, "kindOf": FuncKindOf,
"kindIs": FuncKindIs, "kindIs": FuncKindIs,
"default": FuncDefault,
"empty": FuncEmpty,
} }
} }
// FuncTypeIs is a helper function that provides similar functionality to the helm typeIs func.
func FuncTypeIs(is string, v any) bool {
return is == FuncTypeOf(v)
}
// FuncTypeIsLike is a helper function that provides similar functionality to the helm typeIsLike func.
func FuncTypeIsLike(is string, v any) bool {
t := FuncTypeOf(v)
return is == t || "*"+is == t
}
// FuncTypeOf is a helper function that provides similar functionality to the helm typeOf func.
func FuncTypeOf(v any) string {
return reflect.ValueOf(v).Type().String()
}
// FuncKindIs is a helper function that provides similar functionality to the helm kindIs func.
func FuncKindIs(is string, v any) bool {
return is == FuncKindOf(v)
}
// FuncKindOf is a helper function that provides similar functionality to the helm kindOf func.
func FuncKindOf(v any) string {
return reflect.ValueOf(v).Kind().String()
}
// FuncList is a helper function that provides similar functionality to the helm list func.
func FuncList(items ...any) []any {
return items
}
// FuncDict is a helper function that provides similar functionality to the helm dict func.
func FuncDict(pairs ...any) map[string]any {
m := map[string]any{}
p := len(pairs)
for i := 0; i < p; i += 2 {
key := strval(pairs[i])
if i+1 >= p {
m[key] = ""
continue
}
m[key] = pairs[i+1]
}
return m
}
// FuncGet is a helper function that provides similar functionality to the helm get func.
func FuncGet(m map[string]any, key string) any {
if val, ok := m[key]; ok {
return val
}
return ""
}
// FuncSet is a helper function that provides similar functionality to the helm set func.
func FuncSet(m map[string]any, key string, value any) map[string]any {
m[key] = value
return m
}
// FuncB64Enc is a helper function that provides similar functionality to the helm b64enc func. // FuncB64Enc is a helper function that provides similar functionality to the helm b64enc func.
func FuncB64Enc(input string) string { func FuncB64Enc(input string) string {
return base64.StdEncoding.EncodeToString([]byte(input)) return base64.StdEncoding.EncodeToString([]byte(input))
@ -292,58 +226,6 @@ func FuncStringQuote(in ...any) string {
return strings.Join(out, " ") return strings.Join(out, " ")
} }
func strval(v any) string {
switch v := v.(type) {
case string:
return v
case []byte:
return string(v)
case fmt.Stringer:
return v.String()
default:
return fmt.Sprintf("%v", v)
}
}
func strslice(v any) []string {
switch v := v.(type) {
case []string:
return v
case []any:
b := make([]string, 0, len(v))
for _, s := range v {
if s != nil {
b = append(b, strval(s))
}
}
return b
default:
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Array, reflect.Slice:
l := val.Len()
b := make([]string, 0, l)
for i := 0; i < l; i++ {
value := val.Index(i).Interface()
if value != nil {
b = append(b, strval(value))
}
}
return b
default:
if v == nil {
return []string{}
}
return []string{strval(v)}
}
}
}
// FuncIterate is a template function which takes a single uint returning a slice of units from 0 up to that number. // FuncIterate is a template function which takes a single uint returning a slice of units from 0 up to that number.
func FuncIterate(count *uint) (out []uint) { func FuncIterate(count *uint) (out []uint) {
var i uint var i uint
@ -398,3 +280,107 @@ func FuncStringJoinX(elems []string, sep string, n int, p string) string {
return buf.String() return buf.String()
} }
// FuncTypeIs is a helper function that provides similar functionality to the helm typeIs func.
func FuncTypeIs(is string, v any) bool {
return is == FuncTypeOf(v)
}
// FuncTypeIsLike is a helper function that provides similar functionality to the helm typeIsLike func.
func FuncTypeIsLike(is string, v any) bool {
t := FuncTypeOf(v)
return is == t || "*"+is == t
}
// FuncTypeOf is a helper function that provides similar functionality to the helm typeOf func.
func FuncTypeOf(v any) string {
return reflect.ValueOf(v).Type().String()
}
// FuncKindIs is a helper function that provides similar functionality to the helm kindIs func.
func FuncKindIs(is string, v any) bool {
return is == FuncKindOf(v)
}
// FuncKindOf is a helper function that provides similar functionality to the helm kindOf func.
func FuncKindOf(v any) string {
return reflect.ValueOf(v).Kind().String()
}
// FuncList is a helper function that provides similar functionality to the helm list func.
func FuncList(items ...any) []any {
return items
}
// FuncDict is a helper function that provides similar functionality to the helm dict func.
func FuncDict(pairs ...any) map[string]any {
m := map[string]any{}
p := len(pairs)
for i := 0; i < p; i += 2 {
key := strval(pairs[i])
if i+1 >= p {
m[key] = ""
continue
}
m[key] = pairs[i+1]
}
return m
}
// FuncGet is a helper function that provides similar functionality to the helm get func.
func FuncGet(m map[string]any, key string) any {
if val, ok := m[key]; ok {
return val
}
return ""
}
// FuncSet is a helper function that provides similar functionality to the helm set func.
func FuncSet(m map[string]any, key string, value any) map[string]any {
m[key] = value
return m
}
// FuncDefault is a helper function that provides similar functionality to the helm default func.
func FuncDefault(d any, vals ...any) any {
if FuncEmpty(vals) || FuncEmpty(vals[0]) {
return d
}
return vals[0]
}
// FuncEmpty is a helper function that provides similar functionality to the helm empty func.
func FuncEmpty(v any) bool {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return true
}
switch rv.Kind() {
default:
return rv.IsNil()
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return rv.Len() == 0
case reflect.Bool:
return !rv.Bool()
case reflect.Complex64, reflect.Complex128:
return rv.Complex() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return rv.Uint() == 0
case reflect.Float32, reflect.Float64:
return rv.Float() == 0
case reflect.Struct:
return false
}
}

View File

@ -357,6 +357,7 @@ func TestFuncSortAlpha(t *testing.T) {
}{ }{
{"ShouldSortStrings", []string{"a", "c", "b"}, []string{"a", "b", "c"}}, {"ShouldSortStrings", []string{"a", "c", "b"}, []string{"a", "b", "c"}},
{"ShouldSortIntegers", []int{2, 3, 1}, []string{"1", "2", "3"}}, {"ShouldSortIntegers", []int{2, 3, 1}, []string{"1", "2", "3"}},
{"ShouldSortSingleValue", 1, []string{"1"}},
} }
for _, tc := range testCases { for _, tc := range testCases {
@ -563,3 +564,77 @@ func TestFuncSet(t *testing.T) {
assert.Equal(t, map[string]any{"abc": 123, "123": true}, FuncSet(map[string]any{"abc": 123}, "123", true)) assert.Equal(t, map[string]any{"abc": 123, "123": true}, FuncSet(map[string]any{"abc": 123}, "123", true))
assert.Equal(t, map[string]any{"abc": true}, FuncSet(map[string]any{"abc": 123}, "abc", true)) assert.Equal(t, map[string]any{"abc": true}, FuncSet(map[string]any{"abc": 123}, "abc", true))
} }
func TestFuncDefault(t *testing.T) {
testCases := []struct {
name string
value []any
have any
expected any
}{
{"ShouldDefaultEmptyString", []any{""}, "default", "default"},
{"ShouldNotDefaultString", []any{"not default"}, "default", "not default"},
{"ShouldDefaultEmptyInteger", []any{0}, 1, 1},
{"ShouldNotDefaultInteger", []any{20}, 1, 20},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expected, FuncDefault(tc.have, tc.value...))
})
}
}
func TestFuncEmpty(t *testing.T) {
var nilv *string
testCases := []struct {
name string
value any
expected bool
}{
{"ShouldBeEmptyNil", nilv, true},
{"ShouldBeEmptyNilNil", nil, true},
{"ShouldBeEmptyString", "", true},
{"ShouldNotBeEmptyString", "abc", false},
{"ShouldBeEmptyArray", []string{}, true},
{"ShouldNotBeEmptyArray", []string{"abc"}, false},
{"ShouldBeEmptyInteger", 0, true},
{"ShouldNotBeEmptyInteger", 1, false},
{"ShouldBeEmptyInteger8", int8(0), true},
{"ShouldNotBeEmptyInteger8", int8(1), false},
{"ShouldBeEmptyInteger16", int16(0), true},
{"ShouldNotBeEmptyInteger16", int16(1), false},
{"ShouldBeEmptyInteger32", int32(0), true},
{"ShouldNotBeEmptyInteger32", int32(1), false},
{"ShouldBeEmptyInteger64", int64(0), true},
{"ShouldNotBeEmptyInteger64", int64(1), false},
{"ShouldBeEmptyUnsignedInteger", uint(0), true},
{"ShouldNotBeEmptyUnsignedInteger", uint(1), false},
{"ShouldBeEmptyUnsignedInteger8", uint8(0), true},
{"ShouldNotBeEmptyUnsignedInteger8", uint8(1), false},
{"ShouldBeEmptyUnsignedInteger16", uint16(0), true},
{"ShouldNotBeEmptyUnsignedInteger16", uint16(1), false},
{"ShouldBeEmptyUnsignedInteger32", uint32(0), true},
{"ShouldNotBeEmptyUnsignedInteger32", uint32(1), false},
{"ShouldBeEmptyUnsignedInteger64", uint64(0), true},
{"ShouldNotBeEmptyUnsignedInteger64", uint64(1), false},
{"ShouldBeEmptyComplex64", complex64(complex(0, 0)), true},
{"ShouldNotBeEmptyComplex64", complex64(complex(100000, 7.5)), false},
{"ShouldBeEmptyComplex128", complex128(complex(0, 0)), true},
{"ShouldNotBeEmptyComplex128", complex128(complex(100000, 7.5)), false},
{"ShouldBeEmptyFloat32", float32(0), true},
{"ShouldNotBeEmptyFloat32", float32(1), false},
{"ShouldBeEmptyFloat64", float64(0), true},
{"ShouldNotBeEmptyFloat64", float64(1), false},
{"ShouldBeEmptyBoolean", false, true},
{"ShouldNotBeEmptyBoolean", true, false},
{"ShouldNotBeEmptyStruct", struct{}{}, false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expected, FuncEmpty(tc.value))
})
}
}

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
"strings" "strings"
tt "text/template" tt "text/template"
) )
@ -118,3 +119,55 @@ func loadEmailTemplate(name, overridePath string) (t *EmailTemplate, err error)
return t, nil return t, nil
} }
func strval(v any) string {
switch v := v.(type) {
case string:
return v
case []byte:
return string(v)
case fmt.Stringer:
return v.String()
default:
return fmt.Sprintf("%v", v)
}
}
func strslice(v any) []string {
switch v := v.(type) {
case []string:
return v
case []any:
b := make([]string, 0, len(v))
for _, s := range v {
if s != nil {
b = append(b, strval(s))
}
}
return b
default:
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Array, reflect.Slice:
l := val.Len()
b := make([]string, 0, l)
for i := 0; i < l; i++ {
value := val.Index(i).Interface()
if value != nil {
b = append(b, strval(value))
}
}
return b
default:
if v == nil {
return []string{}
}
return []string{strval(v)}
}
}
}