Skip to main content

hcl/eval/
expr.rs

1use super::*;
2use std::collections::VecDeque;
3
4pub(super) fn evaluate_bool(expr: &Expression, ctx: &Context) -> EvalResult<bool> {
5    match expr.evaluate(ctx)? {
6        Value::Bool(value) => Ok(value),
7        other => Err(ctx.error(Error::unexpected(other, "a boolean"))),
8    }
9}
10
11// It's not formally defined, but the go HCL implementation allows object key expressions to
12// evaluate to either a string, boolean value or number and will then convert all of these to
13// string. Any other value shall produce an error.
14pub(super) fn evaluate_object_key(expr: &Expression, ctx: &Context) -> EvalResult<String> {
15    match expr.evaluate(ctx)? {
16        Value::String(value) => Ok(value),
17        Value::Bool(value) => Ok(value.to_string()),
18        Value::Number(value) => Ok(value.to_string()),
19        other => Err(ctx.error(Error::unexpected(other, "a string, boolean or number"))),
20    }
21}
22
23pub(super) fn evaluate_array(expr: &Expression, ctx: &Context) -> EvalResult<Vec<Value>> {
24    match expr.evaluate(ctx)? {
25        Value::Array(array) => Ok(array),
26        other => Err(ctx.error(Error::unexpected(other, "an array"))),
27    }
28}
29
30pub(super) fn evaluate_traversal(
31    mut value: Value,
32    mut operators: VecDeque<&TraversalOperator>,
33    ctx: &Context,
34) -> EvalResult<Value> {
35    while let Some(operator) = operators.pop_front() {
36        value = match operator {
37            TraversalOperator::LegacyIndex(index) => {
38                evaluate_array_value(value, *index as usize, ctx)?
39            }
40            TraversalOperator::Index(index_expr) => evaluate_index_expr(value, index_expr, ctx)?,
41            TraversalOperator::GetAttr(name) => evaluate_object_value(value, name, ctx)?,
42            TraversalOperator::AttrSplat => {
43                // Consume all immediately following GetAttr operators and apply them to each array
44                // element.
45                let mut remaining = VecDeque::with_capacity(operators.len());
46
47                while let Some(operator) = operators.pop_front() {
48                    match operator {
49                        TraversalOperator::GetAttr(_) => remaining.push_back(operator),
50                        other => {
51                            operators.push_front(other);
52                            break;
53                        }
54                    }
55                }
56
57                evaluate_splat(value, remaining, ctx)?
58            }
59            TraversalOperator::FullSplat => {
60                // Consume all remaining operators and apply them to each array element.
61                let remaining: VecDeque<&TraversalOperator> = std::mem::take(&mut operators);
62
63                evaluate_splat(value, remaining, ctx)?
64            }
65        }
66    }
67
68    Ok(value)
69}
70
71fn evaluate_splat(
72    value: Value,
73    operators: VecDeque<&TraversalOperator>,
74    ctx: &Context,
75) -> EvalResult<Value> {
76    let array = match value {
77        Value::Array(array) => array
78            .into_iter()
79            .map(|value| evaluate_traversal(value, operators.clone(), ctx))
80            .collect::<EvalResult<_>>()?,
81        Value::Null => vec![],
82        other => evaluate_traversal(other, operators, ctx).map(|expr| vec![expr])?,
83    };
84
85    Ok(Value::Array(array))
86}
87
88fn evaluate_index_expr(value: Value, index_expr: &Expression, ctx: &Context) -> EvalResult<Value> {
89    match index_expr.evaluate(ctx)? {
90        Value::String(name) => evaluate_object_value(value, &name, ctx),
91        Value::Number(num) => match num.as_u64() {
92            Some(index) => evaluate_array_value(value, index as usize, ctx),
93            None => Err(ctx.error(Error::unexpected(num, "an unsigned integer"))),
94        },
95        other => Err(ctx.error(Error::unexpected(other, "an unsigned integer or string"))),
96    }
97}
98
99fn evaluate_array_value(mut value: Value, index: usize, ctx: &Context) -> EvalResult<Value> {
100    match value.as_array_mut() {
101        Some(array) => {
102            if index < array.len() {
103                Ok(array.swap_remove(index))
104            } else {
105                Err(ctx.error(ErrorKind::Index(index)))
106            }
107        }
108        None => Err(ctx.error(Error::unexpected(value, "an array"))),
109    }
110}
111
112fn evaluate_object_value(mut value: Value, key: &str, ctx: &Context) -> EvalResult<Value> {
113    match value.as_object_mut() {
114        Some(object) => object
115            .swap_remove(key)
116            .ok_or_else(|| ctx.error(ErrorKind::NoSuchKey(key.to_string()))),
117        None => Err(ctx.error(Error::unexpected(value, "an object"))),
118    }
119}
120
121fn evaluate_collection(expr: &Expression, ctx: &Context) -> EvalResult<Vec<(Value, Value)>> {
122    match expr.evaluate(ctx)? {
123        Value::Array(array) => Ok(array
124            .into_iter()
125            .enumerate()
126            .map(|(index, value)| (Value::from(index), value))
127            .collect()),
128        Value::Object(object) => Ok(object
129            .into_iter()
130            .map(|(key, value)| (Value::from(key), value))
131            .collect()),
132        other => Err(ctx.error(Error::unexpected(other, "an array or object"))),
133    }
134}
135
136pub(super) struct Collection<'a> {
137    ctx: &'a Context<'a>,
138    key_var: Option<&'a Identifier>,
139    value_var: &'a Identifier,
140    cond_expr: Option<&'a Expression>,
141    #[allow(clippy::struct_field_names)]
142    collection: Vec<(Value, Value)>,
143}
144
145impl<'a> Collection<'a> {
146    pub(super) fn from_for_expr(for_expr: &'a ForExpr, ctx: &'a Context<'a>) -> EvalResult<Self> {
147        Ok(Collection {
148            ctx,
149            key_var: for_expr.key_var.as_ref(),
150            value_var: &for_expr.value_var,
151            cond_expr: for_expr.cond_expr.as_ref(),
152            collection: evaluate_collection(&for_expr.collection_expr, ctx)?,
153        })
154    }
155
156    pub(super) fn from_for_directive(
157        for_directive: &'a ForDirective,
158        ctx: &'a Context<'a>,
159    ) -> EvalResult<Self> {
160        Ok(Collection {
161            ctx,
162            key_var: for_directive.key_var.as_ref(),
163            value_var: &for_directive.value_var,
164            cond_expr: None,
165            collection: evaluate_collection(&for_directive.collection_expr, ctx)?,
166        })
167    }
168
169    pub(super) fn len(&self) -> usize {
170        self.collection.len()
171    }
172}
173
174impl<'a> IntoIterator for Collection<'a> {
175    type Item = EvalResult<Context<'a>>;
176    type IntoIter = IntoIter<'a>;
177
178    fn into_iter(self) -> Self::IntoIter {
179        IntoIter {
180            ctx: self.ctx,
181            key_var: self.key_var,
182            value_var: self.value_var,
183            cond_expr: self.cond_expr,
184            iter: self.collection.into_iter(),
185        }
186    }
187}
188
189pub(super) struct IntoIter<'a> {
190    ctx: &'a Context<'a>,
191    key_var: Option<&'a Identifier>,
192    value_var: &'a Identifier,
193    cond_expr: Option<&'a Expression>,
194    iter: std::vec::IntoIter<(Value, Value)>,
195}
196
197impl<'a> IntoIter<'a> {
198    fn cond(&self, ctx: &Context) -> EvalResult<bool> {
199        match &self.cond_expr {
200            None => Ok(true),
201            Some(cond_expr) => evaluate_bool(cond_expr, ctx),
202        }
203    }
204
205    fn next_ctx(&mut self) -> Option<Context<'a>> {
206        let (key, value) = self.iter.next()?;
207        let mut ctx = self.ctx.child();
208        if let Some(key_var) = self.key_var {
209            ctx.declare_var(key_var.clone(), key);
210        }
211
212        ctx.declare_var(self.value_var.clone(), value);
213        Some(ctx)
214    }
215}
216
217impl<'a> Iterator for IntoIter<'a> {
218    type Item = EvalResult<Context<'a>>;
219
220    fn next(&mut self) -> Option<Self::Item> {
221        loop {
222            let ctx = self.next_ctx()?;
223
224            match self.cond(&ctx) {
225                Ok(false) => {}
226                Ok(true) => return Some(Ok(ctx)),
227                Err(err) => return Some(Err(err)),
228            }
229        }
230    }
231}