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
- kindOf
- kindIs
- default
- empty
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.

View File

@ -71,77 +71,11 @@ func FuncMap() map[string]any {
"typeIsLike": FuncTypeIsLike,
"kindOf": FuncKindOf,
"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.
func FuncB64Enc(input string) string {
return base64.StdEncoding.EncodeToString([]byte(input))
@ -292,58 +226,6 @@ func FuncStringQuote(in ...any) string {
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.
func FuncIterate(count *uint) (out []uint) {
var i uint
@ -398,3 +280,107 @@ func FuncStringJoinX(elems []string, sep string, n int, p string) 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"}},
{"ShouldSortIntegers", []int{2, 3, 1}, []string{"1", "2", "3"}},
{"ShouldSortSingleValue", 1, []string{"1"}},
}
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": 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"
"path"
"path/filepath"
"reflect"
"strings"
tt "text/template"
)
@ -118,3 +119,55 @@ func loadEmailTemplate(name, overridePath string) (t *EmailTemplate, err error)
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)}
}
}
}