hcl/eval/
impls.rs

1use super::error::EvalResultExt;
2use super::*;
3use indexmap::map::Entry;
4use std::hash::Hash;
5
6impl private::Sealed for Body {}
7
8impl Evaluate for Body {
9    type Output = Self;
10
11    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
12        self.iter()
13            .map(|structure| structure.evaluate(ctx))
14            .collect()
15    }
16
17    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
18        #[allow(clippy::manual_try_fold)]
19        self.iter_mut().fold(Ok(()), |res, structure| {
20            res.add_errors(structure.evaluate_in_place(ctx))
21        })
22    }
23}
24
25impl private::Sealed for Structure {}
26
27impl Evaluate for Structure {
28    type Output = Self;
29
30    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
31        match self {
32            Structure::Attribute(attr) => attr.evaluate(ctx).map(Structure::Attribute),
33            Structure::Block(block) => block.evaluate(ctx).map(Structure::Block),
34        }
35    }
36
37    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
38        match self {
39            Structure::Attribute(attr) => attr.evaluate_in_place(ctx),
40            Structure::Block(block) => block.evaluate_in_place(ctx),
41        }
42    }
43}
44
45impl private::Sealed for Attribute {}
46
47impl Evaluate for Attribute {
48    type Output = Self;
49
50    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
51        Ok(Attribute {
52            key: self.key.clone(),
53            expr: self.expr.evaluate(ctx).map(Into::into)?,
54        })
55    }
56
57    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
58        self.expr.evaluate_in_place(ctx)
59    }
60}
61
62impl private::Sealed for Block {}
63
64impl Evaluate for Block {
65    type Output = Self;
66
67    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
68        Ok(Block {
69            identifier: self.identifier.clone(),
70            labels: self.labels.clone(),
71            body: self.body.evaluate(ctx)?,
72        })
73    }
74
75    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
76        self.body.evaluate_in_place(ctx)
77    }
78}
79
80impl private::Sealed for Expression {}
81
82impl Evaluate for Expression {
83    type Output = Value;
84
85    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
86        let ctx = &ctx.child_with_expr(self);
87        match self {
88            Expression::Array(array) => array.evaluate(ctx).map(Value::Array),
89            Expression::Object(object) => object.evaluate(ctx).map(Value::Object),
90            Expression::TemplateExpr(expr) => expr.evaluate(ctx),
91            Expression::Variable(ident) => ctx.lookup_var(ident).cloned(),
92            Expression::Traversal(traversal) => traversal.evaluate(ctx),
93            Expression::FuncCall(func_call) => func_call.evaluate(ctx),
94            Expression::Parenthesis(expr) => expr.evaluate(ctx),
95            Expression::Conditional(cond) => cond.evaluate(ctx),
96            Expression::Operation(op) => op.evaluate(ctx),
97            Expression::ForExpr(expr) => expr.evaluate(ctx),
98            other => Ok(Value::from(other.clone())),
99        }
100    }
101
102    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
103        // We can ignore expressions that cannot be further evaluated. This avoids unnecessary
104        // clone operations.
105        if !matches!(
106            self,
107            Expression::Null | Expression::Bool(_) | Expression::Number(_) | Expression::String(_)
108        ) {
109            evaluate_nested_exprs(self, ctx)?;
110            let value = self.evaluate(ctx)?;
111            *self = value.into();
112        }
113
114        Ok(())
115    }
116}
117
118fn evaluate_nested_exprs(expr: &mut Expression, ctx: &Context) -> EvalResult<(), Errors> {
119    let expr_clone = expr.clone();
120    let ctx = &ctx.child_with_expr(&expr_clone);
121
122    match expr {
123        Expression::Array(array) => array.evaluate_in_place(ctx),
124        Expression::Object(object) => object.evaluate_in_place(ctx),
125        Expression::Traversal(traversal) => traversal.evaluate_in_place(ctx),
126        Expression::FuncCall(func_call) => func_call.evaluate_in_place(ctx),
127        Expression::Parenthesis(expr) => expr.evaluate_in_place(ctx),
128        Expression::Conditional(cond) => cond.evaluate_in_place(ctx),
129        Expression::Operation(op) => op.evaluate_in_place(ctx),
130        Expression::ForExpr(expr) => expr.evaluate_in_place(ctx),
131        _ => Ok(()),
132    }
133}
134
135impl<T> private::Sealed for Vec<T> where T: Evaluate {}
136
137impl<T> Evaluate for Vec<T>
138where
139    T: Evaluate,
140{
141    type Output = Vec<T::Output>;
142
143    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
144        self.iter().map(|expr| expr.evaluate(ctx)).collect()
145    }
146
147    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
148        #[allow(clippy::manual_try_fold)]
149        self.iter_mut().fold(Ok(()), |res, element| {
150            res.add_errors(element.evaluate_in_place(ctx))
151        })
152    }
153}
154
155impl<K, V> private::Sealed for Object<K, V>
156where
157    K: Evaluate,
158    V: Evaluate,
159{
160}
161
162impl<K, V> Evaluate for Object<K, V>
163where
164    K: Evaluate + Eq,
165    K::Output: Hash + Eq,
166    V: Evaluate,
167{
168    type Output = Map<K::Output, V::Output>;
169
170    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
171        self.iter()
172            .map(|(key, expr)| Ok((key.evaluate(ctx)?, expr.evaluate(ctx)?)))
173            .collect()
174    }
175
176    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
177        let mut new_object = Object::with_capacity(self.len());
178
179        #[allow(clippy::manual_try_fold)]
180        let res = self
181            .drain(..)
182            .fold(Ok(()), |mut res, (mut key, mut value)| {
183                res = res
184                    .add_errors(key.evaluate_in_place(ctx))
185                    .add_errors(value.evaluate_in_place(ctx));
186                new_object.insert(key, value);
187                res
188            });
189
190        *self = new_object;
191
192        res
193    }
194}
195
196impl private::Sealed for ObjectKey {}
197
198impl Evaluate for ObjectKey {
199    type Output = String;
200
201    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
202        match self {
203            ObjectKey::Expression(expr) => expr::evaluate_object_key(expr, ctx),
204            ident => Ok(ident.to_string()),
205        }
206    }
207
208    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
209        if let ObjectKey::Expression(expr) = self {
210            expr.evaluate_in_place(ctx)?;
211        }
212
213        Ok(())
214    }
215}
216
217impl private::Sealed for TemplateExpr {}
218
219impl Evaluate for TemplateExpr {
220    type Output = Value;
221
222    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
223        let template = Template::from_expr(self)?;
224        let elements = template.elements();
225
226        // If the template consists only of a single interpolation, with no surrounding literals,
227        // directives or other interpolations, perform interpolation unwrapping as described in the
228        // spec:
229        //
230        // https://github.com/hashicorp/hcl/blob/main/hclsyntax/spec.md#template-interpolation-unwrapping
231        match elements.first() {
232            Some(Element::Interpolation(interp)) if elements.len() == 1 => {
233                interp.expr.evaluate(ctx)
234            }
235            _ => template.evaluate(ctx).map(Value::String),
236        }
237    }
238}
239
240impl private::Sealed for Template {}
241
242impl Evaluate for Template {
243    type Output = String;
244
245    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
246        let mut result = String::new();
247        template::evaluate_template(&mut result, self, ctx, Strip::None, Strip::None)?;
248        Ok(result)
249    }
250
251    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
252        #[allow(clippy::manual_try_fold)]
253        self.elements_mut()
254            .iter_mut()
255            .fold(Ok(()), |mut res, element| match element {
256                Element::Literal(_) => res,
257                Element::Interpolation(interp) => {
258                    res.add_errors(interp.expr.evaluate_in_place(ctx))
259                }
260                Element::Directive(dir) => match &mut **dir {
261                    Directive::If(dir) => {
262                        res = res
263                            .add_errors(dir.cond_expr.evaluate_in_place(ctx))
264                            .add_errors(dir.true_template.evaluate_in_place(ctx));
265
266                        match &mut dir.false_template {
267                            Some(false_template) => {
268                                res.add_errors(false_template.evaluate_in_place(ctx))
269                            }
270                            None => res,
271                        }
272                    }
273                    Directive::For(dir) => res
274                        .add_errors(dir.collection_expr.evaluate_in_place(ctx))
275                        .add_errors(dir.template.evaluate_in_place(ctx)),
276                },
277            })
278    }
279}
280
281impl private::Sealed for Traversal {}
282
283impl Evaluate for Traversal {
284    type Output = Value;
285
286    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
287        let value = self.expr.evaluate(ctx)?;
288        let deque = self.operators.iter().collect();
289        expr::evaluate_traversal(value, deque, ctx)
290    }
291
292    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
293        #[allow(clippy::manual_try_fold)]
294        self.operators
295            .iter_mut()
296            .fold(Ok(()), |res, operator| match operator {
297                TraversalOperator::Index(expr) => res.add_errors(expr.evaluate_in_place(ctx)),
298                _ => res,
299            })
300    }
301}
302
303impl private::Sealed for FuncCall {}
304
305impl Evaluate for FuncCall {
306    type Output = Value;
307
308    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
309        let name = &self.name;
310        let func = ctx.lookup_func(name)?;
311        let len = self.args.len();
312        let mut args = Vec::with_capacity(len);
313
314        for (index, arg) in self.args.iter().enumerate() {
315            if self.expand_final && index == len - 1 {
316                args.extend(expr::evaluate_array(arg, ctx)?);
317            } else {
318                args.push(arg.evaluate(ctx)?);
319            }
320        }
321
322        func.call(args)
323            .map_err(|err| ctx.error(ErrorKind::FuncCall(name.clone(), err)))
324    }
325
326    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
327        self.args.evaluate_in_place(ctx)
328    }
329}
330
331impl private::Sealed for Conditional {}
332
333impl Evaluate for Conditional {
334    type Output = Value;
335
336    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
337        if expr::evaluate_bool(&self.cond_expr, ctx)? {
338            self.true_expr.evaluate(ctx)
339        } else {
340            self.false_expr.evaluate(ctx)
341        }
342    }
343
344    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
345        let cond = expr::evaluate_bool(&self.cond_expr, ctx)?;
346
347        self.cond_expr = Expression::from(cond);
348
349        if cond {
350            self.true_expr.evaluate_in_place(ctx)
351        } else {
352            self.false_expr.evaluate_in_place(ctx)
353        }
354    }
355}
356
357impl private::Sealed for Operation {}
358
359impl Evaluate for Operation {
360    type Output = Value;
361
362    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
363        match self {
364            Operation::Unary(unary) => unary.evaluate(ctx),
365            Operation::Binary(binary) => binary.evaluate(ctx),
366        }
367    }
368
369    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
370        match self {
371            Operation::Unary(unary) => unary.evaluate_in_place(ctx),
372            Operation::Binary(binary) => binary.evaluate_in_place(ctx),
373        }
374    }
375}
376
377impl private::Sealed for UnaryOp {}
378
379impl Evaluate for UnaryOp {
380    type Output = Value;
381
382    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
383        use {UnaryOperator::*, Value::*};
384
385        let value = self.expr.evaluate(ctx)?;
386
387        let value = match (self.operator, value) {
388            (Not, Bool(v)) => Bool(!v),
389            (Neg, Number(n)) => Number(-n),
390            (operator, value) => return Err(ctx.error(ErrorKind::UnaryOp(operator, value))),
391        };
392
393        Ok(value)
394    }
395
396    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
397        self.expr.evaluate_in_place(ctx)
398    }
399}
400
401impl private::Sealed for BinaryOp {}
402
403impl Evaluate for BinaryOp {
404    type Output = Value;
405
406    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
407        use {BinaryOperator::*, Value::*};
408
409        let lhs = self.lhs_expr.evaluate(ctx)?;
410        let rhs = self.rhs_expr.evaluate(ctx)?;
411
412        let value = match (lhs, self.operator, rhs) {
413            (lhs, Eq, rhs) => Bool(lhs == rhs),
414            (lhs, NotEq, rhs) => Bool(lhs != rhs),
415            (Bool(lhs), And, Bool(rhs)) => Bool(lhs && rhs),
416            (Bool(lhs), Or, Bool(rhs)) => Bool(lhs || rhs),
417            (Number(lhs), LessEq, Number(rhs)) => Bool(lhs <= rhs),
418            (Number(lhs), GreaterEq, Number(rhs)) => Bool(lhs >= rhs),
419            (Number(lhs), Less, Number(rhs)) => Bool(lhs < rhs),
420            (Number(lhs), Greater, Number(rhs)) => Bool(lhs > rhs),
421            (Number(lhs), Plus, Number(rhs)) => Number(lhs + rhs),
422            (Number(lhs), Minus, Number(rhs)) => Number(lhs - rhs),
423            (Number(lhs), Mul, Number(rhs)) => Number(lhs * rhs),
424            (Number(lhs), Div, Number(rhs)) => Number(lhs / rhs),
425            (Number(lhs), Mod, Number(rhs)) => Number(lhs % rhs),
426            (lhs, operator, rhs) => return Err(ctx.error(ErrorKind::BinaryOp(lhs, operator, rhs))),
427        };
428
429        Ok(value)
430    }
431
432    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
433        self.lhs_expr
434            .evaluate_in_place(ctx)
435            .add_errors(self.rhs_expr.evaluate_in_place(ctx))
436    }
437}
438
439impl private::Sealed for ForExpr {}
440
441impl Evaluate for ForExpr {
442    type Output = Value;
443
444    fn evaluate(&self, ctx: &Context) -> EvalResult<Self::Output> {
445        let collection = expr::Collection::from_for_expr(self, ctx)?;
446
447        if let Some(key_expr) = &self.key_expr {
448            // Result will be an object.
449            let mut result = Map::with_capacity(collection.len());
450
451            for ctx in collection {
452                let ctx = &ctx?;
453                let key = expr::evaluate_object_key(key_expr, ctx)?;
454                let value = self.value_expr.evaluate(ctx)?;
455
456                if self.grouping {
457                    result
458                        .entry(key)
459                        .or_insert_with(|| Value::Array(Vec::new()))
460                        .as_array_mut()
461                        .unwrap()
462                        .push(value);
463                } else {
464                    match result.entry(key) {
465                        Entry::Occupied(entry) => {
466                            return Err(ctx.error(ErrorKind::KeyExists(entry.key().clone())))
467                        }
468                        Entry::Vacant(entry) => {
469                            entry.insert(value);
470                        }
471                    }
472                }
473            }
474
475            Ok(Value::Object(result))
476        } else {
477            // Result will be an array.
478            let result = collection
479                .into_iter()
480                .map(|ctx| self.value_expr.evaluate(&ctx?))
481                .collect::<EvalResult<_>>()?;
482
483            Ok(Value::Array(result))
484        }
485    }
486
487    fn evaluate_in_place(&mut self, ctx: &Context) -> EvalResult<(), Errors> {
488        self.collection_expr.evaluate_in_place(ctx)
489    }
490}