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}