hcl/ser/
blocks.rs

1use super::in_internal_serialization;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::ops;
5use vecmap::VecMap;
6
7pub(crate) const BLOCK_MARKER: &str = "$hcl::Block";
8
9/// A transparent wrapper type which hints the [`Serializer`][crate::ser::Serializer] to serialize
10/// `T` as an HCL block.
11///
12/// When passed to a serializer other than the one from this crate, a `Block<T>` serializes
13/// exactly like `T`, if `T` implements `serde::Serialize`.
14///
15/// A `Block<T>` can only be used in the *value position of a map-like structure*. For example:
16///
17/// - It can be used to wrap the *value type of a map*, e.g. `Map<K, Block<T>>`
18/// - As the value of a *struct field*, e.g. `struct S { field: Block<T> }`
19/// - Or as the value of an *enum variant*, e.g. `enum E { Variant(Block<T>) }`
20///
21/// **The serialized block's identifier will be the respective map key, struct field name or variant
22/// name.**
23///
24/// The wrapped `T` must be shaped as follows to be serialized as an HCL block:
25///
26/// - A *map-like* value (e.g. a map or struct).
27/// - A *sequence-like* value (e.g. a vector, slice or tuple) with map-like elements as described
28///   above. In this case, multiple blocks with the same identifier are produced.
29///
30/// Wrapping a type `T` that does not fulfil one of the criteria above in a `Block<T>` will result
31/// in serialization errors.
32///
33/// For more convenient usage, see the [`block`][crate::ser::block()] function.
34///
35/// # Example
36///
37/// ```
38/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
39/// use hcl::ser::Block;
40/// use serde::Serialize;
41///
42/// #[derive(Serialize)]
43/// struct Config {
44///     user: Block<Vec<User>>,
45/// }
46///
47/// #[derive(Serialize)]
48/// struct User {
49///     name: String,
50///     email: String,
51/// }
52///
53/// let users = vec![
54///     User {
55///         name: "john".into(),
56///         email: "johndoe@example.com".into(),
57///     },
58///     User {
59///         name: "jane".into(),
60///         email: "janedoe@example.com".into(),
61///     },
62/// ];
63///
64/// let config = Config {
65///     user: Block::new(users),
66/// };
67///
68/// let expected = r#"
69/// user {
70///   name = "john"
71///   email = "johndoe@example.com"
72/// }
73///
74/// user {
75///   name = "jane"
76///   email = "janedoe@example.com"
77/// }
78/// "#.trim_start();
79///
80/// assert_eq!(hcl::to_string(&config)?, expected);
81/// #    Ok(())
82/// # }
83/// ```
84pub struct Block<T>(T);
85
86impl<T> Block<T> {
87    /// Create a new `Block<T>` from a `T`.
88    pub fn new(value: T) -> Block<T> {
89        Block(value)
90    }
91
92    /// Consume the `Block` and return the wrapped `T`.
93    pub fn into_inner(self) -> T {
94        self.0
95    }
96}
97
98impl<T> ops::Deref for Block<T> {
99    type Target = T;
100
101    fn deref(&self) -> &Self::Target {
102        &self.0
103    }
104}
105
106impl<T> ops::DerefMut for Block<T> {
107    fn deref_mut(&mut self) -> &mut Self::Target {
108        &mut self.0
109    }
110}
111
112impl<T> Clone for Block<T>
113where
114    T: Clone,
115{
116    fn clone(&self) -> Self {
117        Block(self.0.clone())
118    }
119}
120
121impl<T> fmt::Debug for Block<T>
122where
123    T: fmt::Debug,
124{
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.debug_tuple("Block").field(&self.0).finish()
127    }
128}
129
130impl<T> Serialize for Block<T>
131where
132    T: Serialize,
133{
134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135    where
136        S: serde::Serializer,
137    {
138        if in_internal_serialization() {
139            serializer.serialize_newtype_struct(BLOCK_MARKER, &self.0)
140        } else {
141            self.0.serialize(serializer)
142        }
143    }
144}
145
146impl<'de, T> Deserialize<'de> for Block<T>
147where
148    T: Deserialize<'de>,
149{
150    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151    where
152        D: serde::Deserializer<'de>,
153    {
154        T::deserialize(deserializer).map(Block)
155    }
156}
157
158pub(crate) const LABELED_BLOCK_MARKER: &str = "$hcl::LabeledBlock";
159
160/// A transparent wrapper type which hints the [`Serializer`][crate::ser::Serializer] to serialize
161/// `T` as a labeled HCL block.
162///
163/// When passed to a serializer other than the one from this crate, a `LabeledBlock<T>` serializes
164/// exactly like `T`, if `T` implements `serde::Serialize`.
165///
166/// A `LabeledBlock<T>` can only be used in the *value position of a map-like structure*. For example:
167///
168/// - It can be used to wrap the *value type of a map*, e.g. `Map<K, LabeledBlock<T>>`
169/// - As the value of a *struct field*, e.g. `struct S { field: LabeledBlock<T> }`
170/// - Or as the value of an *enum variant*, e.g. `enum E { Variant(LabeledBlock<T>) }`
171///
172/// **The serialized block's identifier will be the respective map key, struct field name or variant
173/// name.**
174///
175/// The wrapped `T` must be shaped as follows to be serialized as a labeled HCL block:
176///
177/// - A *map-like* value (e.g. a map or struct) where the value may to be another
178///   `LabeledBlock<T>`, in which case a block with multiple labels is produced. Can be nested
179///   arbitrarily deep to allow for any number of block labels.
180/// - A *sequence-like* value (e.g. a vector, slice or tuple) with map-like elements as described
181///   above. In this case, multiple blocks with the same identifier and labels are produced.
182///
183/// Wrapping a type `T` that does not fulfil one of the criteria above in a [`LabeledBlock<T>`]
184/// will result in serialization errors.
185///
186/// For more convenient usage, see the [`labeled_block`] and [`doubly_labeled_block`] functions.
187///
188/// # Example
189///
190/// ```
191/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
192/// use hcl::ser::LabeledBlock;
193/// use indexmap::{indexmap, IndexMap};
194/// use serde::Serialize;
195///
196/// #[derive(Serialize)]
197/// struct Config {
198///     user: LabeledBlock<IndexMap<String, User>>,
199/// }
200///
201/// #[derive(Serialize)]
202/// struct User {
203///     email: String,
204/// }
205///
206/// let users = indexmap! {
207///     "john".into() => User {
208///         email: "johndoe@example.com".into(),
209///     },
210///     "jane".into() => User {
211///         email: "janedoe@example.com".into(),
212///     },
213/// };
214///
215/// let config = Config {
216///     user: LabeledBlock::new(users),
217/// };
218///
219/// let expected = r#"
220/// user "john" {
221///   email = "johndoe@example.com"
222/// }
223///
224/// user "jane" {
225///   email = "janedoe@example.com"
226/// }
227/// "#.trim_start();
228///
229/// assert_eq!(hcl::to_string(&config)?, expected);
230/// #    Ok(())
231/// # }
232/// ```
233pub struct LabeledBlock<T>(T);
234
235impl<T> LabeledBlock<T> {
236    /// Create a new `LabeledBlock<T>` from a `T`.
237    pub fn new(value: T) -> LabeledBlock<T> {
238        LabeledBlock(value)
239    }
240
241    /// Consume the `LabeledBlock` and return the wrapped `T`.
242    pub fn into_inner(self) -> T {
243        self.0
244    }
245}
246
247impl<T> ops::Deref for LabeledBlock<T> {
248    type Target = T;
249
250    fn deref(&self) -> &Self::Target {
251        &self.0
252    }
253}
254
255impl<T> ops::DerefMut for LabeledBlock<T> {
256    fn deref_mut(&mut self) -> &mut Self::Target {
257        &mut self.0
258    }
259}
260
261impl<T> Clone for LabeledBlock<T>
262where
263    T: Clone,
264{
265    fn clone(&self) -> Self {
266        LabeledBlock(self.0.clone())
267    }
268}
269
270impl<T> fmt::Debug for LabeledBlock<T>
271where
272    T: fmt::Debug,
273{
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        f.debug_tuple("LabeledBlock").field(&self.0).finish()
276    }
277}
278
279impl<T> Serialize for LabeledBlock<T>
280where
281    T: Serialize,
282{
283    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
284    where
285        S: serde::Serializer,
286    {
287        if in_internal_serialization() {
288            serializer.serialize_newtype_struct(LABELED_BLOCK_MARKER, &self.0)
289        } else {
290            self.0.serialize(serializer)
291        }
292    }
293}
294
295impl<'de, T> Deserialize<'de> for LabeledBlock<T>
296where
297    T: Deserialize<'de>,
298{
299    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300    where
301        D: serde::Deserializer<'de>,
302    {
303        T::deserialize(deserializer).map(LabeledBlock)
304    }
305}
306
307/// Hints the [`Serializer`][crate::ser::Serializer] to serialize `T` as an HCL block.
308///
309/// This function is intended to be used in the `#[serde(serialize_with)]` attribute and wraps `T`
310/// with a [`Block<T>`].
311///
312/// See the type-level documentation of [`Block<T>`] for more.
313///
314/// # Example
315///
316/// ```
317/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
318/// use serde::Serialize;
319///
320/// #[derive(Serialize)]
321/// struct Config {
322///     #[serde(serialize_with = "hcl::ser::block")]
323///     user: Vec<User>,
324/// }
325///
326/// #[derive(Serialize)]
327/// struct User {
328///     name: String,
329///     email: String,
330/// }
331///
332/// let config = Config {
333///     user: vec![
334///         User {
335///             name: "john".into(),
336///             email: "johndoe@example.com".into(),
337///         },
338///         User {
339///             name: "jane".into(),
340///             email: "janedoe@example.com".into(),
341///         },
342///     ],
343/// };
344///
345/// let expected = r#"
346/// user {
347///   name = "john"
348///   email = "johndoe@example.com"
349/// }
350///
351/// user {
352///   name = "jane"
353///   email = "janedoe@example.com"
354/// }
355/// "#.trim_start();
356///
357/// assert_eq!(hcl::to_string(&config)?, expected);
358/// #    Ok(())
359/// # }
360/// ```
361///
362/// # Errors
363///
364/// Serialization fails if the type's shape makes it impossible to represent it as an HCL block
365/// with two labels.
366pub fn block<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
367where
368    T: Serialize,
369    S: serde::Serializer,
370{
371    Block::new(value).serialize(serializer)
372}
373
374/// Hints the [`Serializer`][crate::ser::Serializer] to serialize `T` as a labeled HCL block.
375///
376/// This function is intended to be used in the `#[serde(serialize_with)]` attribute and wraps `T`
377/// with a [`LabeledBlock<T>`].
378///
379/// See the type-level documentation of [`LabeledBlock<T>`] for more.
380///
381/// # Example
382///
383/// ```
384/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
385/// use indexmap::{indexmap, IndexMap};
386/// use serde::Serialize;
387///
388/// #[derive(Serialize)]
389/// struct Config {
390///     #[serde(serialize_with = "hcl::ser::labeled_block")]
391///     user: IndexMap<String, User>,
392/// }
393///
394/// #[derive(Serialize)]
395/// struct User {
396///     email: String,
397/// }
398///
399/// let config = Config {
400///     user: indexmap! {
401///         "john".into() => User {
402///             email: "johndoe@example.com".into(),
403///         },
404///         "jane".into() => User {
405///             email: "janedoe@example.com".into(),
406///         },
407///     },
408/// };
409///
410/// let expected = r#"
411/// user "john" {
412///   email = "johndoe@example.com"
413/// }
414///
415/// user "jane" {
416///   email = "janedoe@example.com"
417/// }
418/// "#.trim_start();
419///
420/// assert_eq!(hcl::to_string(&config)?, expected);
421/// #    Ok(())
422/// # }
423/// ```
424///
425/// # Errors
426///
427/// Serialization fails if the type's shape makes it impossible to represent it as a labeled HCL
428/// block.
429pub fn labeled_block<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
430where
431    T: Serialize,
432    S: serde::Serializer,
433{
434    LabeledBlock::new(value).serialize(serializer)
435}
436
437/// Hints the [`Serializer`][crate::ser::Serializer] to serialize `T` as an HCL block with two
438/// labels.
439///
440/// This function is intended to be used in the `#[serde(serialize_with)]` attribute and wraps `T`
441/// and each value of `T` with a [`LabeledBlock<T>`]. One use case for this function is terraform
442/// configuration where blocks with two labels are common in various places.
443///
444/// See the type-level documentation of [`LabeledBlock<T>`] for more.
445///
446/// # Example
447///
448/// The following example shows a very simplified and incomplete way to serialize terraform
449/// configuration.
450///
451/// ```
452/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
453/// use indexmap::{indexmap, IndexMap};
454/// use serde::Serialize;
455///
456/// #[derive(Serialize)]
457/// struct Config {
458///     #[serde(serialize_with = "hcl::ser::doubly_labeled_block")]
459///     resource: IndexMap<String, IndexMap<String, IndexMap<String, String>>>,
460/// }
461///
462/// let config = Config {
463///     resource: indexmap! {
464///         "aws_sns_topic".into() => indexmap! {
465///             "mytopic".into() => indexmap! {
466///                 "name".into() => "mytopic".into(),
467///             },
468///         },
469///     },
470/// };
471///
472/// let expected = r#"
473/// resource "aws_sns_topic" "mytopic" {
474///   name = "mytopic"
475/// }
476/// "#.trim_start();
477///
478/// assert_eq!(hcl::to_string(&config)?, expected);
479/// #    Ok(())
480/// # }
481/// ```
482///
483/// # Errors
484///
485/// Serialization fails if the type's shape makes it impossible to represent it as an HCL block
486/// with two labels.
487pub fn doubly_labeled_block<T, K, V, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
488where
489    T: IntoIterator<Item = (K, V)>,
490    K: Serialize + Eq,
491    V: Serialize,
492    S: serde::Serializer,
493{
494    let value: VecMap<K, LabeledBlock<V>> = value
495        .into_iter()
496        .map(|(k, v)| (k, LabeledBlock::new(v)))
497        .collect();
498    labeled_block(value, serializer)
499}