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)
    }
}