hcl/structure/
block.rs

1//! Types to represent and build HCL blocks.
2
3use super::{Attribute, Body, BodyBuilder, Structure};
4use crate::Identifier;
5use serde::{Deserialize, Serialize};
6use std::borrow::Cow;
7
8/// Represents an HCL block which consists of a block identifier, zero or more block labels and a
9/// block body.
10///
11/// In HCL syntax this is represented as:
12///
13/// ```hcl
14/// block_identifier "block_label1" "block_label2" {
15///   body
16/// }
17/// ```
18#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
19pub struct Block {
20    /// The block identifier.
21    pub identifier: Identifier,
22    /// Zero or more block labels.
23    pub labels: Vec<BlockLabel>,
24    /// Represents the `Block`'s body.
25    pub body: Body,
26}
27
28impl Block {
29    /// Creates a new empty `Block`.
30    pub fn new<I>(ident: I) -> Block
31    where
32        I: Into<Identifier>,
33    {
34        Block {
35            identifier: ident.into(),
36            labels: Vec::new(),
37            body: Body::default(),
38        }
39    }
40
41    /// Creates a new [`BlockBuilder`] to start building a new `Block` with the provided
42    /// identifier.
43    pub fn builder<I>(identifier: I) -> BlockBuilder
44    where
45        I: Into<Identifier>,
46    {
47        BlockBuilder::new(identifier)
48    }
49
50    /// Returns a reference to the block's identifier.
51    pub fn identifier(&self) -> &str {
52        &self.identifier
53    }
54
55    /// Returns a reference to the block's labels.
56    pub fn labels(&self) -> &[BlockLabel] {
57        &self.labels
58    }
59
60    /// Returns a reference to the block's body.
61    pub fn body(&self) -> &Body {
62        &self.body
63    }
64}
65
66impl<I, B> From<(I, B)> for Block
67where
68    I: Into<Identifier>,
69    B: IntoIterator,
70    B::Item: Into<Structure>,
71{
72    fn from((ident, body): (I, B)) -> Block {
73        Block {
74            identifier: ident.into(),
75            labels: Vec::new(),
76            body: body.into_iter().collect(),
77        }
78    }
79}
80
81impl<I, L, B> From<(I, L, B)> for Block
82where
83    I: Into<Identifier>,
84    L: IntoIterator,
85    L::Item: Into<BlockLabel>,
86    B: IntoIterator,
87    B::Item: Into<Structure>,
88{
89    fn from((ident, labels, body): (I, L, B)) -> Block {
90        Block {
91            identifier: ident.into(),
92            labels: labels.into_iter().map(Into::into).collect(),
93            body: body.into_iter().collect(),
94        }
95    }
96}
97
98/// Represents an HCL block label.
99///
100/// In HCL syntax this can be represented either as a quoted string literal...
101///
102/// ```hcl
103/// block_identifier "block_label1" {
104///   body
105/// }
106/// ```
107///
108/// ...or as a bare identifier:
109///
110/// ```hcl
111/// block_identifier block_label1 {
112///   body
113/// }
114/// ```
115#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
116pub enum BlockLabel {
117    /// A bare HCL block label.
118    Identifier(Identifier),
119    /// A quoted string literal.
120    String(String),
121}
122
123impl BlockLabel {
124    /// Consumes `self` and returns the `String` wrapped by the `BlockLabel`.
125    ///
126    /// Beware that after calling `.into_inner()` it is not possible anymore to tell whether the
127    /// `String` resembles a quoted string or bare identifer.
128    pub fn into_inner(self) -> String {
129        match self {
130            BlockLabel::Identifier(ident) => ident.into_inner(),
131            BlockLabel::String(string) => string,
132        }
133    }
134
135    /// Borrows the `BlockLabel`'s inner value as a `&str`.
136    pub fn as_str(&self) -> &str {
137        match self {
138            BlockLabel::Identifier(ident) => ident.as_str(),
139            BlockLabel::String(string) => string.as_str(),
140        }
141    }
142}
143
144impl From<String> for BlockLabel {
145    fn from(s: String) -> BlockLabel {
146        BlockLabel::String(s)
147    }
148}
149
150impl From<&String> for BlockLabel {
151    fn from(s: &String) -> BlockLabel {
152        BlockLabel::String(s.clone())
153    }
154}
155
156impl From<&str> for BlockLabel {
157    fn from(s: &str) -> BlockLabel {
158        BlockLabel::String(s.to_string())
159    }
160}
161
162impl<'a> From<Cow<'a, str>> for BlockLabel {
163    fn from(s: Cow<'a, str>) -> BlockLabel {
164        BlockLabel::String(s.into_owned())
165    }
166}
167
168impl From<Identifier> for BlockLabel {
169    fn from(ident: Identifier) -> Self {
170        BlockLabel::Identifier(ident)
171    }
172}
173
174/// `BlockBuilder` builds an HCL [`Block`].
175///
176/// The builder allows to build the `Block` by adding labels, attributes and other nested blocks
177/// via chained method calls. A call to [`.build()`](BlockBuilder::build) produces the final
178/// `Block`.
179///
180/// ## Example
181///
182/// ```
183/// use hcl::Block;
184///
185/// let block = Block::builder("resource")
186///     .add_label("aws_s3_bucket")
187///     .add_label("mybucket")
188///     .add_attribute(("name", "mybucket"))
189///     .add_block(
190///         Block::builder("logging")
191///             .add_attribute(("target_bucket", "mylogsbucket"))
192///             .build()
193///     )
194///     .build();
195/// ```
196#[derive(Debug)]
197pub struct BlockBuilder {
198    identifier: Identifier,
199    labels: Vec<BlockLabel>,
200    body: BodyBuilder,
201}
202
203impl BlockBuilder {
204    /// Creates a new `BlockBuilder` to start building a new [`Block`] with the provided
205    /// identifier.
206    pub fn new<I>(identifier: I) -> BlockBuilder
207    where
208        I: Into<Identifier>,
209    {
210        BlockBuilder {
211            identifier: identifier.into(),
212            labels: Vec::new(),
213            body: Body::builder(),
214        }
215    }
216
217    /// Adds a `BlockLabel`.
218    ///
219    /// Consumes `self` and returns a new `BlockBuilder`.
220    pub fn add_label<L>(mut self, label: L) -> BlockBuilder
221    where
222        L: Into<BlockLabel>,
223    {
224        self.labels.push(label.into());
225        self
226    }
227
228    /// Adds `BlockLabel`s from an iterator.
229    ///
230    /// Consumes `self` and returns a new `BlockBuilder`.
231    pub fn add_labels<I>(mut self, iter: I) -> BlockBuilder
232    where
233        I: IntoIterator,
234        I::Item: Into<BlockLabel>,
235    {
236        self.labels.extend(iter.into_iter().map(Into::into));
237        self
238    }
239
240    /// Adds an `Attribute` to the block body.
241    ///
242    /// Consumes `self` and returns a new `BlockBuilder`.
243    pub fn add_attribute<A>(mut self, attr: A) -> BlockBuilder
244    where
245        A: Into<Attribute>,
246    {
247        self.body = self.body.add_attribute(attr);
248        self
249    }
250
251    /// Adds `Attribute`s to the block body from an iterator.
252    ///
253    /// Consumes `self` and returns a new `BlockBuilder`.
254    pub fn add_attributes<I>(mut self, iter: I) -> BlockBuilder
255    where
256        I: IntoIterator,
257        I::Item: Into<Attribute>,
258    {
259        self.body = self.body.add_attributes(iter.into_iter().map(Into::into));
260        self
261    }
262
263    /// Adds another `Block` to the block body.
264    ///
265    /// Consumes `self` and returns a new `BlockBuilder`.
266    pub fn add_block<B>(mut self, block: B) -> BlockBuilder
267    where
268        B: Into<Block>,
269    {
270        self.body = self.body.add_block(block);
271        self
272    }
273
274    /// Adds `Block`s to the block body from an iterator.
275    ///
276    /// Consumes `self` and returns a new `BlockBuilder`.
277    pub fn add_blocks<I>(mut self, iter: I) -> BlockBuilder
278    where
279        I: IntoIterator,
280        I::Item: Into<Block>,
281    {
282        self.body = self.body.add_blocks(iter.into_iter().map(Into::into));
283        self
284    }
285
286    /// Adds a `Structure` to the block body.
287    ///
288    /// Consumes `self` and returns a new `BlockBuilder`.
289    pub fn add_structure<S>(mut self, structure: S) -> BlockBuilder
290    where
291        S: Into<Structure>,
292    {
293        self.body = self.body.add_structure(structure);
294        self
295    }
296
297    /// Adds `Structure`s to the block body from an iterator.
298    ///
299    /// Consumes `self` and returns a new `BlockBuilder`.
300    pub fn add_structures<I>(mut self, iter: I) -> BlockBuilder
301    where
302        I: IntoIterator,
303        I::Item: Into<Structure>,
304    {
305        self.body = self.body.add_structures(iter.into_iter().map(Into::into));
306        self
307    }
308
309    /// Consumes `self` and builds the [`Block`] from the items added via the builder methods.
310    pub fn build(self) -> Block {
311        Block {
312            identifier: self.identifier,
313            labels: self.labels,
314            body: self.body.build(),
315        }
316    }
317}