-
-
Notifications
You must be signed in to change notification settings - Fork 551
/
templater.go
143 lines (120 loc) · 3.21 KB
/
templater.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package templater
import (
"bytes"
"maps"
"strings"
"github.com/go-task/task/v3/internal/deepcopy"
"github.com/go-task/task/v3/taskfile/ast"
"github.com/go-task/template"
)
// Cache is a help struct that allow us to call "replaceX" funcs multiple
// times, without having to check for error each time. The first error that
// happen will be assigned to r.err, and consecutive calls to funcs will just
// return the zero value.
type Cache struct {
Vars *ast.Vars
cacheMap map[string]any
err error
}
func (r *Cache) ResetCache() {
r.cacheMap = r.Vars.ToCacheMap()
}
func (r *Cache) Err() error {
return r.err
}
func ResolveRef(ref string, cache *Cache) any {
// If there is already an error, do nothing
if cache.err != nil {
return nil
}
// Initialize the cache map if it's not already initialized
if cache.cacheMap == nil {
cache.cacheMap = cache.Vars.ToCacheMap()
}
val, err := template.ResolveRef(ref, cache.cacheMap)
if err != nil {
cache.err = err
return nil
}
return val
}
func Replace[T any](v T, cache *Cache) T {
return ReplaceWithExtra(v, cache, nil)
}
func ReplaceWithExtra[T any](v T, cache *Cache, extra map[string]any) T {
// If there is already an error, do nothing
if cache.err != nil {
return v
}
// Initialize the cache map if it's not already initialized
if cache.cacheMap == nil {
cache.cacheMap = cache.Vars.ToCacheMap()
}
// Create a copy of the cache map to avoid editing the original
// If there is extra data, merge it with the cache map
data := maps.Clone(cache.cacheMap)
if extra != nil {
maps.Copy(data, extra)
}
// Traverse the value and parse any template variables
copy, err := deepcopy.TraverseStringsFunc(v, func(v string) (string, error) {
tpl, err := template.New("").Funcs(templateFuncs).Parse(v)
if err != nil {
return v, err
}
var b bytes.Buffer
if err := tpl.Execute(&b, data); err != nil {
return v, err
}
return strings.ReplaceAll(b.String(), "<no value>", ""), nil
})
if err != nil {
cache.err = err
return v
}
return copy
}
func ReplaceGlobs(globs []*ast.Glob, cache *Cache) []*ast.Glob {
if cache.err != nil || len(globs) == 0 {
return nil
}
new := make([]*ast.Glob, len(globs))
for i, g := range globs {
new[i] = &ast.Glob{
Glob: Replace(g.Glob, cache),
Negate: g.Negate,
}
}
return new
}
func ReplaceVar(v ast.Var, cache *Cache) ast.Var {
return ReplaceVarWithExtra(v, cache, nil)
}
func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var {
if v.Ref != "" {
return ast.Var{Value: ResolveRef(v.Ref, cache)}
}
return ast.Var{
Value: ReplaceWithExtra(v.Value, cache, extra),
Sh: ReplaceWithExtra(v.Sh, cache, extra),
Live: v.Live,
Ref: v.Ref,
Dir: v.Dir,
Json: ReplaceWithExtra(v.Json, cache, extra),
Yaml: ReplaceWithExtra(v.Yaml, cache, extra),
}
}
func ReplaceVars(vars *ast.Vars, cache *Cache) *ast.Vars {
return ReplaceVarsWithExtra(vars, cache, nil)
}
func ReplaceVarsWithExtra(vars *ast.Vars, cache *Cache, extra map[string]any) *ast.Vars {
if cache.err != nil || vars.Len() == 0 {
return nil
}
var newVars ast.Vars
_ = vars.Range(func(k string, v ast.Var) error {
newVars.Set(k, ReplaceVarWithExtra(v, cache, extra))
return nil
})
return &newVars
}