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