mirror of
https://github.com/go-task/task.git
synced 2024-12-04 10:24:45 +02:00
151 lines
3.3 KiB
Go
151 lines
3.3 KiB
Go
package templater
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"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()
|
|
}
|
|
|
|
if ref == "." {
|
|
return cache.cacheMap
|
|
}
|
|
t, err := template.New("resolver").Funcs(templateFuncs).Parse(fmt.Sprintf("{{%s}}", ref))
|
|
if err != nil {
|
|
cache.err = err
|
|
return nil
|
|
}
|
|
val, err := t.Resolve(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,
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|