Skip to content

Commit 0024d6b

Browse files
committed
Extracts common template functions into a utility module
1 parent 3c7f9ec commit 0024d6b

File tree

3 files changed

+221
-170
lines changed

3 files changed

+221
-170
lines changed

pkg/knowledgebase/dynamic_value.go

+5-136
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"encoding"
66
"encoding/json"
77
"fmt"
8-
"path/filepath"
8+
"github.com/klothoplatform/klotho/pkg/templateutils"
99
"reflect"
1010
"regexp"
1111
"strconv"
@@ -54,7 +54,7 @@ func (ctx DynamicValueContext) KB() TemplateKB {
5454
}
5555

5656
func (ctx DynamicValueContext) TemplateFunctions() template.FuncMap {
57-
return template.FuncMap{
57+
return templateutils.WithCommonFuncs(template.FuncMap{
5858
"hasUpstream": ctx.HasUpstream,
5959
"upstream": ctx.Upstream,
6060
"layeredUpstream": ctx.LayeredUpstream,
@@ -73,41 +73,10 @@ func (ctx DynamicValueContext) TemplateFunctions() template.FuncMap {
7373

7474
"toJson": ctx.toJson,
7575

76-
"split": strings.Split,
77-
"join": strings.Join,
78-
"basename": filepath.Base,
79-
80-
"firstId": firstId,
81-
"filterIds": filterIds,
82-
"filterMatch": filterMatch,
83-
"mapString": mapString,
84-
"zipToMap": zipToMap,
85-
"keysToMapWithDefault": keysToMapWithDefault,
86-
"replace": replaceRegex,
87-
"hasSuffix": func(s, suffix string) bool {
88-
return strings.HasSuffix(s, suffix)
89-
},
90-
"toLower": strings.ToLower,
76+
"firstId": firstId,
77+
"filterIds": filterIds,
9178
"sanitizeName": sanitizeName,
92-
93-
"add": add,
94-
"sub": sub,
95-
"last": last,
96-
"makeSlice": func() []any {
97-
return []any{}
98-
},
99-
"appendSlice": func(slice []any, value any) []any {
100-
return append(slice, value)
101-
},
102-
"sliceContains": func(slice []any, value any) bool {
103-
for _, v := range slice {
104-
if v == value {
105-
return true
106-
}
107-
}
108-
return false
109-
},
110-
}
79+
})
11180
}
11281

11382
func (ctx DynamicValueContext) Parse(tmpl string) (*template.Template, error) {
@@ -611,22 +580,6 @@ func (ctx DynamicValueContext) PathAncestorExists(path construct.PropertyPath, d
611580
return len(path) > depth
612581
}
613582

614-
// filterMatch returns a json array by filtering the values array with the regex pattern
615-
func filterMatch(pattern string, values []string) ([]string, error) {
616-
re, err := regexp.Compile(pattern)
617-
if err != nil {
618-
return nil, err
619-
}
620-
621-
matches := make([]string, 0, len(values))
622-
for _, v := range values {
623-
if ok := re.MatchString(v); ok {
624-
matches = append(matches, v)
625-
}
626-
}
627-
return matches, nil
628-
}
629-
630583
func filterIds(selector any, ids []construct.ResourceId) ([]construct.ResourceId, error) {
631584
selId, err := TemplateArgToRID(selector)
632585
if err != nil {
@@ -657,90 +610,6 @@ func firstId(selector any, ids []construct.ResourceId) (construct.ResourceId, er
657610
return construct.ResourceId{}, fmt.Errorf("no ids match selector")
658611
}
659612

660-
// mapstring takes in a regex pattern and replacement as well as a json array of strings
661-
// roughly `unmarshal value | sed s/pattern/replace/g | marshal`
662-
func mapString(pattern, replace string, values []string) ([]string, error) {
663-
re, err := regexp.Compile(pattern)
664-
if err != nil {
665-
return nil, err
666-
}
667-
668-
nv := make([]string, len(values))
669-
for i, v := range values {
670-
nv[i] = re.ReplaceAllString(v, replace)
671-
}
672-
return nv, nil
673-
}
674-
675-
// zipToMap returns a json map by zipping the keys and values arrays
676-
// Example: zipToMap(['a', 'b'], [1, 2]) => {"a": 1, "b": 2}
677-
func zipToMap(keys []string, valuesArg any) (map[string]any, error) {
678-
// Have to use reflection here because technically, []string is not assignable to []any
679-
// thanks Go.
680-
valuesRefl := reflect.ValueOf(valuesArg)
681-
if valuesRefl.Kind() != reflect.Slice && valuesRefl.Kind() != reflect.Array {
682-
return nil, fmt.Errorf("values is not a slice or array")
683-
}
684-
if len(keys) != valuesRefl.Len() {
685-
return nil, fmt.Errorf("key length (%d) != value length (%d)", len(keys), valuesRefl.Len())
686-
}
687-
688-
m := make(map[string]any)
689-
for i, k := range keys {
690-
m[k] = valuesRefl.Index(i).Interface()
691-
}
692-
return m, nil
693-
}
694-
695-
// keysToMapWithDefault returns a json map by mapping the keys array to the static defaultValue
696-
// Example keysToMapWithDefault(0, ['a', 'b']) => {"a": 0, "b": 0}
697-
func keysToMapWithDefault(defaultValue any, keys []string) (map[string]any, error) {
698-
m := make(map[string]any)
699-
for _, k := range keys {
700-
m[k] = defaultValue
701-
}
702-
return m, nil
703-
}
704-
705-
func add(args ...int) int {
706-
total := 0
707-
for _, a := range args {
708-
total += a
709-
}
710-
return total
711-
}
712-
713-
func sub(args ...int) int {
714-
if len(args) == 0 {
715-
return 0
716-
}
717-
total := args[0]
718-
for _, a := range args[1:] {
719-
total -= a
720-
}
721-
return total
722-
}
723-
724-
func last(list any) (any, error) {
725-
v := reflect.ValueOf(list)
726-
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
727-
return nil, fmt.Errorf("list is not a slice or array, is %s", v.Kind())
728-
}
729-
if v.Len() == 0 {
730-
return nil, fmt.Errorf("list is empty")
731-
}
732-
return v.Index(v.Len() - 1).Interface(), nil
733-
}
734-
735-
func replaceRegex(pattern, replace, value string) (string, error) {
736-
re, err := regexp.Compile(pattern)
737-
if err != nil {
738-
return "", err
739-
}
740-
s := re.ReplaceAllString(value, replace)
741-
return s, nil
742-
}
743-
744613
// invalidNameCharacters matches characters that are not allowed in resource names. Basically,
745614
// the same as [construct2.resourceNamePattern] except inverted.
746615
var invalidNameCharacters = regexp.MustCompile(`[^a-zA-Z0-9_./\-:\[\]]`)

pkg/templateutils/embed.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package templateutils
22

33
import (
44
"embed"
5+
"strings"
56
"text/template"
67

78
sprig "github.com/Masterminds/sprig/v3"
@@ -13,11 +14,24 @@ func MustTemplate(fs embed.FS, name string) *template.Template {
1314
panic(err)
1415
}
1516
t, err := template.New(name).
16-
Funcs(Funcs).
17+
Funcs(mustTemplateFuncs).
1718
Funcs(sprig.HermeticTxtFuncMap()).
1819
Parse(string(content))
1920
if err != nil {
2021
panic(err)
2122
}
2223
return t
2324
}
25+
26+
var mustTemplateFuncs = template.FuncMap{
27+
"joinString": strings.Join,
28+
29+
"json": ToJSON,
30+
"jsonPretty": ToJSONPretty,
31+
32+
"fileBase": FileBase,
33+
"fileTrimExt": FileTrimExtFunc,
34+
"fileSep": FileSep,
35+
36+
"replaceAll": ReplaceAll,
37+
}

0 commit comments

Comments
 (0)