]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package hclsyntax |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "fmt" | |
6 | ||
7 | "github.com/hashicorp/hcl2/hcl" | |
8 | "github.com/zclconf/go-cty/cty" | |
9 | "github.com/zclconf/go-cty/cty/convert" | |
10 | ) | |
11 | ||
12 | type TemplateExpr struct { | |
13 | Parts []Expression | |
14 | ||
15 | SrcRange hcl.Range | |
16 | } | |
17 | ||
18 | func (e *TemplateExpr) walkChildNodes(w internalWalkFunc) { | |
107c1cdb ND |
19 | for _, part := range e.Parts { |
20 | w(part) | |
15c0b25d AP |
21 | } |
22 | } | |
23 | ||
24 | func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
25 | buf := &bytes.Buffer{} | |
26 | var diags hcl.Diagnostics | |
27 | isKnown := true | |
28 | ||
29 | for _, part := range e.Parts { | |
30 | partVal, partDiags := part.Value(ctx) | |
31 | diags = append(diags, partDiags...) | |
32 | ||
33 | if partVal.IsNull() { | |
34 | diags = append(diags, &hcl.Diagnostic{ | |
35 | Severity: hcl.DiagError, | |
36 | Summary: "Invalid template interpolation value", | |
37 | Detail: fmt.Sprintf( | |
38 | "The expression result is null. Cannot include a null value in a string template.", | |
39 | ), | |
107c1cdb ND |
40 | Subject: part.Range().Ptr(), |
41 | Context: &e.SrcRange, | |
42 | Expression: part, | |
43 | EvalContext: ctx, | |
15c0b25d AP |
44 | }) |
45 | continue | |
46 | } | |
47 | ||
48 | if !partVal.IsKnown() { | |
49 | // If any part is unknown then the result as a whole must be | |
50 | // unknown too. We'll keep on processing the rest of the parts | |
51 | // anyway, because we want to still emit any diagnostics resulting | |
52 | // from evaluating those. | |
53 | isKnown = false | |
54 | continue | |
55 | } | |
56 | ||
57 | strVal, err := convert.Convert(partVal, cty.String) | |
58 | if err != nil { | |
59 | diags = append(diags, &hcl.Diagnostic{ | |
60 | Severity: hcl.DiagError, | |
61 | Summary: "Invalid template interpolation value", | |
62 | Detail: fmt.Sprintf( | |
63 | "Cannot include the given value in a string template: %s.", | |
64 | err.Error(), | |
65 | ), | |
107c1cdb ND |
66 | Subject: part.Range().Ptr(), |
67 | Context: &e.SrcRange, | |
68 | Expression: part, | |
69 | EvalContext: ctx, | |
15c0b25d AP |
70 | }) |
71 | continue | |
72 | } | |
73 | ||
74 | buf.WriteString(strVal.AsString()) | |
75 | } | |
76 | ||
77 | if !isKnown { | |
78 | return cty.UnknownVal(cty.String), diags | |
79 | } | |
80 | ||
81 | return cty.StringVal(buf.String()), diags | |
82 | } | |
83 | ||
84 | func (e *TemplateExpr) Range() hcl.Range { | |
85 | return e.SrcRange | |
86 | } | |
87 | ||
88 | func (e *TemplateExpr) StartRange() hcl.Range { | |
89 | return e.Parts[0].StartRange() | |
90 | } | |
91 | ||
92 | // TemplateJoinExpr is used to convert tuples of strings produced by template | |
93 | // constructs (i.e. for loops) into flat strings, by converting the values | |
94 | // tos strings and joining them. This AST node is not used directly; it's | |
95 | // produced as part of the AST of a "for" loop in a template. | |
96 | type TemplateJoinExpr struct { | |
97 | Tuple Expression | |
98 | } | |
99 | ||
100 | func (e *TemplateJoinExpr) walkChildNodes(w internalWalkFunc) { | |
107c1cdb | 101 | w(e.Tuple) |
15c0b25d AP |
102 | } |
103 | ||
104 | func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
105 | tuple, diags := e.Tuple.Value(ctx) | |
106 | ||
107 | if tuple.IsNull() { | |
108 | // This indicates a bug in the code that constructed the AST. | |
109 | panic("TemplateJoinExpr got null tuple") | |
110 | } | |
111 | if tuple.Type() == cty.DynamicPseudoType { | |
112 | return cty.UnknownVal(cty.String), diags | |
113 | } | |
114 | if !tuple.Type().IsTupleType() { | |
115 | // This indicates a bug in the code that constructed the AST. | |
116 | panic("TemplateJoinExpr got non-tuple tuple") | |
117 | } | |
118 | if !tuple.IsKnown() { | |
119 | return cty.UnknownVal(cty.String), diags | |
120 | } | |
121 | ||
122 | buf := &bytes.Buffer{} | |
123 | it := tuple.ElementIterator() | |
124 | for it.Next() { | |
125 | _, val := it.Element() | |
126 | ||
127 | if val.IsNull() { | |
128 | diags = append(diags, &hcl.Diagnostic{ | |
129 | Severity: hcl.DiagError, | |
130 | Summary: "Invalid template interpolation value", | |
131 | Detail: fmt.Sprintf( | |
132 | "An iteration result is null. Cannot include a null value in a string template.", | |
133 | ), | |
107c1cdb ND |
134 | Subject: e.Range().Ptr(), |
135 | Expression: e, | |
136 | EvalContext: ctx, | |
15c0b25d AP |
137 | }) |
138 | continue | |
139 | } | |
140 | if val.Type() == cty.DynamicPseudoType { | |
141 | return cty.UnknownVal(cty.String), diags | |
142 | } | |
143 | strVal, err := convert.Convert(val, cty.String) | |
144 | if err != nil { | |
145 | diags = append(diags, &hcl.Diagnostic{ | |
146 | Severity: hcl.DiagError, | |
147 | Summary: "Invalid template interpolation value", | |
148 | Detail: fmt.Sprintf( | |
149 | "Cannot include one of the interpolation results into the string template: %s.", | |
150 | err.Error(), | |
151 | ), | |
107c1cdb ND |
152 | Subject: e.Range().Ptr(), |
153 | Expression: e, | |
154 | EvalContext: ctx, | |
15c0b25d AP |
155 | }) |
156 | continue | |
157 | } | |
158 | if !val.IsKnown() { | |
159 | return cty.UnknownVal(cty.String), diags | |
160 | } | |
161 | ||
162 | buf.WriteString(strVal.AsString()) | |
163 | } | |
164 | ||
165 | return cty.StringVal(buf.String()), diags | |
166 | } | |
167 | ||
168 | func (e *TemplateJoinExpr) Range() hcl.Range { | |
169 | return e.Tuple.Range() | |
170 | } | |
171 | ||
172 | func (e *TemplateJoinExpr) StartRange() hcl.Range { | |
173 | return e.Tuple.StartRange() | |
174 | } | |
175 | ||
176 | // TemplateWrapExpr is used instead of a TemplateExpr when a template | |
177 | // consists _only_ of a single interpolation sequence. In that case, the | |
178 | // template's result is the single interpolation's result, verbatim with | |
179 | // no type conversions. | |
180 | type TemplateWrapExpr struct { | |
181 | Wrapped Expression | |
182 | ||
183 | SrcRange hcl.Range | |
184 | } | |
185 | ||
186 | func (e *TemplateWrapExpr) walkChildNodes(w internalWalkFunc) { | |
107c1cdb | 187 | w(e.Wrapped) |
15c0b25d AP |
188 | } |
189 | ||
190 | func (e *TemplateWrapExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
191 | return e.Wrapped.Value(ctx) | |
192 | } | |
193 | ||
194 | func (e *TemplateWrapExpr) Range() hcl.Range { | |
195 | return e.SrcRange | |
196 | } | |
197 | ||
198 | func (e *TemplateWrapExpr) StartRange() hcl.Range { | |
199 | return e.SrcRange | |
200 | } |