hcl/expr/
traversal.rs

1use super::Expression;
2use crate::Identifier;
3use serde::{Deserialize, Serialize};
4
5/// Traverse an expression to access attributes, object keys or element indices.
6#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
7pub struct Traversal {
8    /// The expression that the access operator is applied to.
9    pub expr: Expression,
10    /// The traversal operators to apply to `expr` one of the other.
11    pub operators: Vec<TraversalOperator>,
12}
13
14impl Traversal {
15    /// Creates a new `Traversal` structure from an expression and traversal operators that should
16    /// be applied to it.
17    pub fn new<E, I>(expr: E, operators: I) -> Self
18    where
19        E: Into<Expression>,
20        I: IntoIterator,
21        I::Item: Into<TraversalOperator>,
22    {
23        Traversal {
24            expr: expr.into(),
25            operators: operators.into_iter().map(Into::into).collect(),
26        }
27    }
28
29    /// Create a new `TraversalBuilder` for the given expression.
30    pub fn builder<T>(expr: T) -> TraversalBuilder
31    where
32        T: Into<Expression>,
33    {
34        TraversalBuilder {
35            expr: expr.into(),
36            operators: Vec::new(),
37        }
38    }
39}
40
41/// A builder for expression traversals.
42///
43/// It is constructed via the [`builder`][Traversal::builder] method of the [`Traversal`] type.
44///
45/// # Example
46///
47/// ```
48/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
49/// use hcl::expr::{Traversal, Variable};
50///
51/// let traversal = Traversal::builder(Variable::new("var")?)
52///     .attr("some_array")
53///     .index(0)
54///     .build();
55///
56/// // Serializes as `var.some_array[0]`.
57/// #     Ok(())
58/// # }
59/// ```
60#[derive(Debug)]
61pub struct TraversalBuilder {
62    expr: Expression,
63    operators: Vec<TraversalOperator>,
64}
65
66impl TraversalBuilder {
67    /// Add an [attribute access operator][TraversalOperator::GetAttr] to the traversal chain.
68    pub fn attr<T>(mut self, ident: T) -> Self
69    where
70        T: Into<Identifier>,
71    {
72        self.operators
73            .push(TraversalOperator::GetAttr(ident.into()));
74        self
75    }
76
77    /// Add an [attribute splat operator][TraversalOperator::AttrSplat] to the traversal chain.
78    pub fn attr_splat(mut self) -> Self {
79        self.operators.push(TraversalOperator::AttrSplat);
80        self
81    }
82
83    /// Add a [full splat operator][TraversalOperator::FullSplat] to the traversal chain.
84    pub fn full_splat(mut self) -> Self {
85        self.operators.push(TraversalOperator::FullSplat);
86        self
87    }
88
89    /// Add an [index operator][TraversalOperator::Index] to the traversal chain.
90    pub fn index<T>(mut self, expr: T) -> Self
91    where
92        T: Into<Expression>,
93    {
94        self.operators.push(TraversalOperator::Index(expr.into()));
95        self
96    }
97
98    /// Consume `self` and return a `Traversal`.
99    pub fn build(self) -> Traversal {
100        Traversal {
101            expr: self.expr,
102            operators: self.operators,
103        }
104    }
105}
106
107/// The expression traversal operators that are supported by HCL.
108#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
109pub enum TraversalOperator {
110    /// The attribute-only splat operator supports only attribute lookups into the elements from a
111    /// list, but supports an arbitrary number of them.
112    AttrSplat,
113    /// The full splat operator additionally supports indexing into the elements from a list, and
114    /// allows any combination of attribute access and index operations.
115    FullSplat,
116    /// The attribute access operator returns the value of a single attribute in an object value.
117    GetAttr(Identifier),
118    /// The index operator returns the value of a single element of a collection value based on
119    /// the result of the expression.
120    Index(Expression),
121    /// The legacy index operator returns the value of a single element of a collection value.
122    /// Exists only for compatibility with the precursor language HIL. Use the `Index` variant
123    /// instead.
124    LegacyIndex(u64),
125}
126
127impl<T> From<T> for TraversalOperator
128where
129    T: Into<Identifier>,
130{
131    fn from(value: T) -> TraversalOperator {
132        TraversalOperator::GetAttr(value.into())
133    }
134}
135
136impl From<Expression> for TraversalOperator {
137    fn from(value: Expression) -> TraversalOperator {
138        TraversalOperator::Index(value)
139    }
140}
141
142impl From<u64> for TraversalOperator {
143    fn from(value: u64) -> TraversalOperator {
144        TraversalOperator::LegacyIndex(value)
145    }
146}