1
16
17 package v2
18
19 import (
20 "bytes"
21 "encoding/json"
22 "errors"
23 "fmt"
24 "reflect"
25 "strings"
26 "text/template"
27
28 "github.com/go-openapi/jsonreference"
29 "k8s.io/kubectl/pkg/util/term"
30 )
31
32 type explainError string
33
34 func (e explainError) Error() string {
35 return string(e)
36 }
37
38 func WithBuiltinTemplateFuncs(tmpl *template.Template) *template.Template {
39 return tmpl.Funcs(map[string]interface{}{
40 "throw": func(e string, args ...any) (string, error) {
41 errString := fmt.Sprintf(e, args...)
42 return "", explainError(errString)
43 },
44 "toJson": func(obj any) (string, error) {
45 res, err := json.Marshal(obj)
46 return string(res), err
47 },
48 "toPrettyJson": func(obj any) (string, error) {
49 res, err := json.MarshalIndent(obj, "", " ")
50 if err != nil {
51 return "", err
52 }
53 return string(res), err
54 },
55 "fail": func(message string) (string, error) {
56 return "", errors.New(message)
57 },
58 "wrap": func(l int, s string) (string, error) {
59 buf := bytes.NewBuffer(nil)
60 writer := term.NewWordWrapWriter(buf, uint(l))
61 _, err := writer.Write([]byte(s))
62 if err != nil {
63 return "", err
64 }
65 return buf.String(), nil
66 },
67 "split": func(s string, sep string) []string {
68 return strings.Split(s, sep)
69 },
70 "join": func(sep string, strs ...string) string {
71 return strings.Join(strs, sep)
72 },
73 "include": func(name string, data interface{}) (string, error) {
74 buf := bytes.NewBuffer(nil)
75 if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
76 return "", err
77 }
78 return buf.String(), nil
79 },
80 "ternary": func(a, b any, condition bool) any {
81 if condition {
82 return a
83 }
84 return b
85 },
86 "first": func(list any) (any, error) {
87 if list == nil {
88 return nil, errors.New("list is empty")
89 }
90
91 tp := reflect.TypeOf(list).Kind()
92 switch tp {
93 case reflect.Slice, reflect.Array:
94 l2 := reflect.ValueOf(list)
95
96 l := l2.Len()
97 if l == 0 {
98 return nil, errors.New("list is empty")
99 }
100
101 return l2.Index(0).Interface(), nil
102 default:
103 return nil, fmt.Errorf("first cannot be used on type: %T", list)
104 }
105 },
106 "last": func(list any) (any, error) {
107 if list == nil {
108 return nil, errors.New("list is empty")
109 }
110
111 tp := reflect.TypeOf(list).Kind()
112 switch tp {
113 case reflect.Slice, reflect.Array:
114 l2 := reflect.ValueOf(list)
115
116 l := l2.Len()
117 if l == 0 {
118 return nil, errors.New("list is empty")
119 }
120
121 return l2.Index(l - 1).Interface(), nil
122 default:
123 return nil, fmt.Errorf("last cannot be used on type: %T", list)
124 }
125 },
126 "indent": func(amount int, str string) string {
127 pad := strings.Repeat(" ", amount)
128 return pad + strings.Replace(str, "\n", "\n"+pad, -1)
129 },
130 "dict": func(keysAndValues ...any) (map[string]any, error) {
131 if len(keysAndValues)%2 != 0 {
132 return nil, errors.New("expected even # of arguments")
133 }
134
135 res := map[string]any{}
136 for i := 0; i+1 < len(keysAndValues); i = i + 2 {
137 if key, ok := keysAndValues[i].(string); ok {
138 res[key] = keysAndValues[i+1]
139 } else {
140 return nil, fmt.Errorf("key of type %T is not a string as expected", key)
141 }
142 }
143
144 return res, nil
145 },
146 "contains": func(list any, value any) bool {
147 if list == nil {
148 return false
149 }
150
151 val := reflect.ValueOf(list)
152 switch val.Kind() {
153 case reflect.Array:
154 case reflect.Slice:
155 for i := 0; i < val.Len(); i++ {
156 cur := val.Index(i)
157 if cur.CanInterface() && reflect.DeepEqual(cur.Interface(), value) {
158 return true
159 }
160 }
161 return false
162 default:
163 return false
164 }
165 return false
166 },
167 "set": func(dict map[string]any, keysAndValues ...any) (any, error) {
168 if len(keysAndValues)%2 != 0 {
169 return nil, errors.New("expected even number of arguments")
170 }
171
172 copyDict := make(map[string]any, len(dict))
173 for k, v := range dict {
174 copyDict[k] = v
175 }
176
177 for i := 0; i < len(keysAndValues); i += 2 {
178 key, ok := keysAndValues[i].(string)
179 if !ok {
180 return nil, errors.New("keys must be strings")
181 }
182
183 copyDict[key] = keysAndValues[i+1]
184 }
185
186 return copyDict, nil
187 },
188 "list": func(values ...any) ([]any, error) {
189 return values, nil
190 },
191 "add": func(value, operand int) int {
192 return value + operand
193 },
194 "sub": func(value, operand int) int {
195 return value - operand
196 },
197 "mul": func(value, operand int) int {
198 return value * operand
199 },
200 "resolveRef": func(refAny any, document map[string]any) map[string]any {
201 refString, ok := refAny.(string)
202 if !ok {
203
204
205 return nil
206 }
207
208
209 ref, err := jsonreference.New(refString)
210 if err != nil {
211
212 return nil
213 }
214
215 if !ref.HasFragmentOnly {
216
217 return nil
218 }
219
220 fragment := ref.GetURL().Fragment
221 components := strings.Split(fragment, "/")
222 cur := document
223
224 for _, k := range components {
225 if len(k) == 0 {
226
227 continue
228 }
229
230 next, ok := cur[k].(map[string]any)
231 if !ok {
232 return nil
233 }
234
235 cur = next
236 }
237 return cur
238 },
239 })
240 }
241
View as plain text