-
Notifications
You must be signed in to change notification settings - Fork 44
/
i.go
133 lines (110 loc) · 3.37 KB
/
i.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
package interpolate
import (
"bytes"
"regexp"
"strings"
"text/template"
"github.com/google/uuid"
)
// Context is the context that an interpolation is done in. This defines
// things such as available variables.
type Context struct {
// Data is the data for the template that is available
Data interface{}
// Funcs are extra functions available in the template
Funcs map[string]interface{}
// UserVariables is the mapping of user variables that the
// "user" function reads from.
UserVariables map[string]string
// SensitiveVariables is a list of variables to sanitize.
SensitiveVariables []string
// EnableEnv enables the env function
EnableEnv bool
// All the fields below are used for built-in functions.
//
// BuildName and BuildType are the name and type, respectively,
// of the builder being used.
//
// TemplatePath is the path to the template that this is being
// rendered within.
BuildName string
BuildType string
CorePackerVersionString string
TemplatePath string
}
// NewContext returns an initialized empty context.
func NewContext() *Context {
return &Context{}
}
// RenderOnce is shorthand for constructing an I and calling Render one time.
func RenderOnce(v string, ctx *Context) (string, error) {
return (&I{Value: v}).Render(ctx)
}
// Render is shorthand for constructing an I and calling Render until all variables are rendered.
func Render(v string, ctx *Context) (rendered string, err error) {
// Keep interpolating until all variables are done
// Sometimes a variable can been inside another one
for {
rendered, err = (&I{Value: v}).Render(ctx)
if err != nil || rendered == v {
break
}
v = rendered
}
return
}
// Render is shorthand for constructing an I and calling Render.
// Use regex to filter variables that are not supposed to be interpolated now
func RenderRegex(v string, ctx *Context, regex string) (string, error) {
re := regexp.MustCompile(regex)
matches := re.FindAllStringSubmatch(v, -1)
// Replace variables to be excluded with a unique UUID
excluded := make(map[string]string)
for _, value := range matches {
id := uuid.New().String()
excluded[id] = value[0]
v = strings.ReplaceAll(v, value[0], id)
}
rendered, err := (&I{Value: v}).Render(ctx)
if err != nil {
return rendered, err
}
// Replace back by the UUID the previously excluded values
for id, value := range excluded {
rendered = strings.ReplaceAll(rendered, id, value)
}
return rendered, nil
}
// Validate is shorthand for constructing an I and calling Validate.
func Validate(v string, ctx *Context) error {
return (&I{Value: v}).Validate(ctx)
}
// I stands for "interpolation" and is the main interpolation struct
// in order to render values.
type I struct {
Value string
}
// Render renders the interpolation with the given context.
func (i *I) Render(ictx *Context) (string, error) {
tpl, err := i.template(ictx)
if err != nil {
return "", err
}
var result bytes.Buffer
var data interface{}
if ictx != nil {
data = ictx.Data
}
if err := tpl.Execute(&result, data); err != nil {
return "", err
}
return result.String(), nil
}
// Validate validates that the template is syntactically valid.
func (i *I) Validate(ctx *Context) error {
_, err := i.template(ctx)
return err
}
func (i *I) template(ctx *Context) (*template.Template, error) {
return template.New("root").Funcs(Funcs(ctx)).Parse(i.Value)
}