hcl_edit/expr/
for_expr.rs

1use crate::expr::Expression;
2use crate::{Decor, Decorate, Decorated, Ident};
3use std::ops::Range;
4
5/// A for expression is a construct for constructing a collection by projecting the items from
6/// another collection.
7#[derive(Debug, Clone, Eq)]
8pub struct ForExpr {
9    /// The `for` expression introduction, containing an optional key var, value var and the
10    /// collection expression that is iterated.
11    pub intro: ForIntro,
12    /// An expression that is evaluated once for each key in the source collection. If set, the
13    /// result of the `for` expression will be an object. Otherwise, the result will be an array.
14    pub key_expr: Option<Expression>,
15    /// An expression that is evaluated once for each value in the source collection.
16    pub value_expr: Expression,
17    /// Indicates whether grouping mode is enabled. In grouping mode, each value in the resulting
18    /// object is a list of all of the values that were produced against each distinct key. This is
19    /// ignored if `key_expr` is `None`.
20    pub grouping: bool,
21    /// An optional filter expression. Elements for which the condition evaluates to `true` will
22    /// be evaluated as normal, while if `false` the element will be skipped.
23    pub cond: Option<ForCond>,
24
25    decor: Decor,
26    span: Option<Range<usize>>,
27}
28
29impl ForExpr {
30    /// Creates a new `ForExpr` from a `for` expression introduction and a result value
31    /// expression.
32    pub fn new(intro: ForIntro, value_expr: impl Into<Expression>) -> ForExpr {
33        ForExpr {
34            intro,
35            key_expr: None,
36            value_expr: value_expr.into(),
37            grouping: false,
38            cond: None,
39            decor: Decor::default(),
40            span: None,
41        }
42    }
43
44    pub(crate) fn despan(&mut self, input: &str) {
45        self.decor.despan(input);
46        self.intro.despan(input);
47
48        if let Some(key_expr) = &mut self.key_expr {
49            key_expr.despan(input);
50        }
51
52        self.value_expr.despan(input);
53
54        if let Some(cond) = &mut self.cond {
55            cond.despan(input);
56        }
57    }
58}
59
60impl PartialEq for ForExpr {
61    fn eq(&self, other: &Self) -> bool {
62        self.intro == other.intro
63            && self.key_expr == other.key_expr
64            && self.value_expr == other.value_expr
65            && self.grouping == other.grouping
66            && self.cond == other.cond
67    }
68}
69
70/// The `for` expression introduction, containing an optional key var, value var and the
71/// collection expression that is iterated.
72#[derive(Debug, Clone, Eq)]
73pub struct ForIntro {
74    /// Optional name of the variable that will be temporarily assigned the key of each element
75    /// during iteration. If the source collection is an array, it gets assigned the zero-based
76    /// array index. For an object source collection, this gets assigned the object's key.
77    pub key_var: Option<Decorated<Ident>>,
78    /// The name of the variable that will be temporarily assigned the value of each element
79    /// during iteration.
80    pub value_var: Decorated<Ident>,
81    /// An expression that must evaluate to a value that can be iterated.
82    pub collection_expr: Expression,
83
84    decor: Decor,
85    span: Option<Range<usize>>,
86}
87
88impl ForIntro {
89    /// Creates a new `ForIntro` from a value variable and a collection expression.
90    pub fn new(
91        value_var: impl Into<Decorated<Ident>>,
92        collection_expr: impl Into<Expression>,
93    ) -> ForIntro {
94        ForIntro {
95            key_var: None,
96            value_var: value_var.into(),
97            collection_expr: collection_expr.into(),
98            decor: Decor::default(),
99            span: None,
100        }
101    }
102
103    pub(crate) fn despan(&mut self, input: &str) {
104        self.decor.despan(input);
105        if let Some(key_var) = &mut self.key_var {
106            key_var.decor_mut().despan(input);
107        }
108
109        self.value_var.decor_mut().despan(input);
110        self.collection_expr.despan(input);
111    }
112}
113
114impl PartialEq for ForIntro {
115    fn eq(&self, other: &Self) -> bool {
116        self.key_var == other.key_var
117            && self.value_var == other.value_var
118            && self.collection_expr == other.collection_expr
119    }
120}
121
122/// A filter expression. Elements for which the condition evaluates to `true` will be evaluated as
123/// normal, while if `false` the element will be skipped.
124#[derive(Debug, Clone, Eq)]
125pub struct ForCond {
126    /// The filter expression.
127    pub expr: Expression,
128
129    decor: Decor,
130    span: Option<Range<usize>>,
131}
132
133impl ForCond {
134    /// Creates a new `ForCond` from an expression.
135    pub fn new(expr: impl Into<Expression>) -> ForCond {
136        ForCond {
137            expr: expr.into(),
138            decor: Decor::default(),
139            span: None,
140        }
141    }
142
143    pub(crate) fn despan(&mut self, input: &str) {
144        self.decor.despan(input);
145        self.expr.despan(input);
146    }
147}
148
149impl PartialEq for ForCond {
150    fn eq(&self, other: &Self) -> bool {
151        self.expr == other.expr
152    }
153}
154
155impl From<Expression> for ForCond {
156    fn from(value: Expression) -> Self {
157        ForCond::new(value)
158    }
159}
160
161decorate_impl!(ForExpr, ForIntro, ForCond);
162span_impl!(ForExpr, ForIntro, ForCond);