hcl/ser/
mod.rs

1//! Serialize a Rust data structure into HCL data.
2//!
3//! This module provides the [`Serializer`] type and the convienince functions [`to_string`],
4//! [`to_vec`] and [`to_writer`] for serializing data to HCL.
5//!
6//! Furthermore, the [`Block`] and [`LabeledBlock`] wrapper types, and the
7//! [`block`][crate::ser::block()], [`labeled_block`] and [`doubly_labeled_block`] functions can be
8//! used to construct HCL block structures from custom types. See the type and function level
9//! documentation for usage examples.
10//!
11//! If you want to serialize the data structures provided by this crate (e.g. [`Body`]) consider
12//! using the functionality in the [`format`](crate::format) module instead because it is more
13//! efficient.
14//!
15//! ## Supported top-level types
16//!
17//! The [`Serializer`] supports serialization to HCL for types that are either structured like
18//! maps or sequences of maps. For example, at the top level a struct with one or more named
19//! fields is supported, while a newtype struct wrapping a primitive type like `u8` is not.
20//!
21//! Other example of supported top-level types:
22//!
23//! - tuple or newtype structs wrapping a map-like type
24//! - enums with newtype or tuple variants wrapping map-like types, or struct variants
25//!
26//! Please note that these restrictions only apply to the top-level type that is serialized.
27//! Nested fields can have any type that is serializable.
28//!
29//! ## Serializing a custom type
30//!
31//! The following example will serialize the data as a deeply nested HCL attribute.
32//!
33//! ```
34//! # use std::error::Error;
35//! #
36//! # fn main() -> Result<(), Box<dyn Error>> {
37//! use serde::Serialize;
38//!
39//! #[derive(Serialize)]
40//! struct User {
41//!     age: u8,
42//!     username: &'static str,
43//!     email: &'static str,
44//! }
45//!
46//! #[derive(Serialize)]
47//! struct Data {
48//!     users: Vec<User>,
49//! }
50//!
51//! let data = Data {
52//!     users: vec![
53//!         User {
54//!             age: 34,
55//!             username: "johndoe",
56//!             email: "johndoe@example.com",
57//!         },
58//!         User {
59//!             age: 27,
60//!             username: "janedoe",
61//!             email: "janedoe@example.com",
62//!         },
63//!     ],
64//! };
65//!
66//! let expected = r#"
67//! users = [
68//!   {
69//!     "age" = 34
70//!     "username" = "johndoe"
71//!     "email" = "johndoe@example.com"
72//!   },
73//!   {
74//!     "age" = 27
75//!     "username" = "janedoe"
76//!     "email" = "janedoe@example.com"
77//!   }
78//! ]
79//! "#.trim_start();
80//!
81//! let serialized = hcl::to_string(&data)?;
82//!
83//! assert_eq!(serialized, expected);
84//! #   Ok(())
85//! # }
86//! ```
87//!
88//! ## Serializing context-aware HCL
89//!
90//! If you need full control over the way data is serialized to HCL, you can make use of the [`Body`][Body] type which can be constructed using the builder pattern.
91//!
92//! The following example uses HCL blocks to format the same data from above in a different way.
93//!
94//! [Body]: ../struct.Body.html
95//!
96//! ```
97//! # use std::error::Error;
98//! #
99//! # fn main() -> Result<(), Box<dyn Error>> {
100//! use hcl::{Block, Body};
101//!
102//! let body = Body::builder()
103//!     .add_block(
104//!         Block::builder("user")
105//!             .add_label("johndoe")
106//!             .add_attribute(("age", 34))
107//!             .add_attribute(("email", "johndoe@example.com"))
108//!             .build(),
109//!     )
110//!     .add_block(
111//!         Block::builder("user")
112//!             .add_label("janedoe")
113//!             .add_attribute(("age", 27))
114//!             .add_attribute(("email", "janedoe@example.com"))
115//!             .build(),
116//!     )
117//!     .build();
118//!
119//! let expected = r#"
120//! user "johndoe" {
121//!   age = 34
122//!   email = "johndoe@example.com"
123//! }
124//!
125//! user "janedoe" {
126//!   age = 27
127//!   email = "janedoe@example.com"
128//! }
129//! "#.trim_start();
130//!
131//! let serialized = hcl::to_string(&body)?;
132//!
133//! assert_eq!(serialized, expected);
134//! #   Ok(())
135//! # }
136//! ```
137//!
138//! The same result could be acheived using the [`block!`] macro:
139//!
140//! ```
141//! # use std::error::Error;
142//! #
143//! # fn main() -> Result<(), Box<dyn Error>> {
144//! use serde::Serialize;
145//!
146//! #[derive(Serialize)]
147//! struct User {
148//!     age: u8,
149//!     username: &'static str,
150//!     email: &'static str,
151//! }
152//!
153//! let users = vec![
154//!     User {
155//!         age: 34,
156//!         username: "johndoe",
157//!         email: "johndoe@example.com",
158//!     },
159//!     User {
160//!         age: 27,
161//!         username: "janedoe",
162//!         email: "janedoe@example.com",
163//!     },
164//! ];
165//!
166//! let body: hcl::Body = users
167//!     .into_iter()
168//!     .map(|user| {
169//!         hcl::block! {
170//!             user (user.username) {
171//!                 age = (user.age)
172//!                 email = (user.email)
173//!             }
174//!         }
175//!     })
176//!     .collect();
177//!
178//! let expected = r#"
179//! user "johndoe" {
180//!   age = 34
181//!   email = "johndoe@example.com"
182//! }
183//!
184//! user "janedoe" {
185//!   age = 27
186//!   email = "janedoe@example.com"
187//! }
188//! "#
189//! .trim_start();
190//!
191//! let serialized = hcl::to_string(&body)?;
192//!
193//! assert_eq!(serialized, expected);
194//! #   Ok(())
195//! # }
196//! ```
197//! ## Serializing HCL blocks using a custom type
198//!
199//! An example to serialize a terraform configuration block using a custom type and the
200//! [`LabeledBlock`] and [`Block`] marker types from this module:
201//!
202//! ```
203//! use hcl::expr::{Expression, Traversal, Variable};
204//! use indexmap::{indexmap, IndexMap};
205//! use serde::Serialize;
206//!
207//! #[derive(Serialize)]
208//! struct Config {
209//!     #[serde(
210//!         rename = "resource",
211//!         serialize_with = "hcl::ser::labeled_block"
212//!     )]
213//!     resources: Resources,
214//! }
215//!
216//! #[derive(Serialize)]
217//! struct Resources {
218//!     #[serde(
219//!         rename = "aws_sns_topic_subscription",
220//!         serialize_with = "hcl::ser::labeled_block"
221//!     )]
222//!     aws_sns_topic_subscriptions: IndexMap<String, AwsSnsTopicSubscription>,
223//! }
224//!
225//! #[derive(Serialize)]
226//! struct AwsSnsTopicSubscription {
227//!     topic_arn: Traversal,
228//!     protocol: Expression,
229//!     endpoint: Traversal,
230//! }
231//!
232//! let subscription = AwsSnsTopicSubscription {
233//!     topic_arn: Traversal::builder(Variable::new("aws_sns_topic").unwrap())
234//!         .attr("my-topic")
235//!         .attr("arn")
236//!         .build(),
237//!     protocol: "sqs".into(),
238//!     endpoint: Traversal::builder(Variable::new("aws_sqs_queue").unwrap())
239//!         .attr("my-queue")
240//!         .attr("arn")
241//!         .build()
242//! };
243//!
244//! let config = Config {
245//!     resources: Resources {
246//!         aws_sns_topic_subscriptions: indexmap! {
247//!             "my-subscription".into() => subscription,
248//!         },
249//!     },
250//! };
251//!
252//! let expected = r#"
253//! resource "aws_sns_topic_subscription" "my-subscription" {
254//!   topic_arn = aws_sns_topic.my-topic.arn
255//!   protocol = "sqs"
256//!   endpoint = aws_sqs_queue.my-queue.arn
257//! }
258//! "#.trim_start();
259//!
260//! let serialized = hcl::to_string(&config).unwrap();
261//!
262//! assert_eq!(serialized, expected);
263//! ```
264
265pub(crate) mod blocks;
266
267pub use self::blocks::{block, doubly_labeled_block, labeled_block, Block, LabeledBlock};
268use crate::format::{Format, Formatter};
269use crate::structure::Body;
270use crate::{Error, Identifier, Result};
271use serde::ser::{self, Impossible, Serialize, SerializeStruct};
272use std::cell::RefCell;
273use std::collections::BTreeMap;
274use std::fmt;
275use std::io;
276use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
277
278// Deprecated, this re-export will be removed in a future release.
279#[doc(hidden)]
280pub use crate::expr::to_expression;
281
282thread_local! {
283    static INTERNAL_SERIALIZATION: AtomicBool = const { AtomicBool::new(false) };
284}
285
286pub(crate) fn in_internal_serialization() -> bool {
287    INTERNAL_SERIALIZATION.with(|flag| flag.load(Ordering::Relaxed))
288}
289
290pub(crate) fn with_internal_serialization<R, F: FnOnce() -> R>(f: F) -> R {
291    INTERNAL_SERIALIZATION.with(|flag| {
292        let old = flag.load(Ordering::Relaxed);
293        flag.store(true, Ordering::Relaxed);
294        let _on_drop = OnDrop::new(|| {
295            flag.store(old, Ordering::Relaxed);
296        });
297        f()
298    })
299}
300
301/// A structure for serializing Rust values into HCL.
302pub struct Serializer<'a, W> {
303    formatter: Formatter<'a, W>,
304}
305
306impl<'a, W> Serializer<'a, W>
307where
308    W: io::Write,
309{
310    /// Creates a new `Serializer` which serializes to the provides writer using the default
311    /// formatter.
312    pub fn new(writer: W) -> Serializer<'a, W> {
313        Serializer::with_formatter(Formatter::new(writer))
314    }
315
316    /// Creates a new `Serializer` which uses the provides formatter to format the serialized HCL.
317    pub fn with_formatter(formatter: Formatter<'a, W>) -> Serializer<'a, W> {
318        Serializer { formatter }
319    }
320
321    /// Consumes `self` and returns the wrapped writer.
322    pub fn into_inner(self) -> W {
323        self.formatter.into_inner()
324    }
325
326    /// Serialize the given value as HCL via the serializer's `Formatter` to the underlying writer.
327    ///
328    /// # Errors
329    ///
330    /// Serialization fails if the type cannot be represented as HCL.
331    pub fn serialize<T>(&mut self, value: &T) -> Result<()>
332    where
333        T: ?Sized + Serialize,
334    {
335        let serialized = Body::from_serializable(value)?;
336        serialized.format(&mut self.formatter)
337    }
338}
339
340impl<W> Serializer<'_, W>
341where
342    W: io::Write + AsMut<Vec<u8>>,
343{
344    /// Serialize the given value as HCL and returns the result as a `String`.
345    ///
346    /// # Errors
347    ///
348    /// Serialization fails if the type cannot be represented as HCL.
349    pub fn serialize_string<T>(&mut self, value: &T) -> Result<String>
350    where
351        T: ?Sized + Serialize,
352    {
353        let serialized = Body::from_serializable(value)?;
354        serialized.format_string(&mut self.formatter)
355    }
356
357    /// Serialize the given value as HCL and returns the result as a `Vec<u8>`.
358    ///
359    /// # Errors
360    ///
361    /// Serialization fails if the type cannot be represented as HCL.
362    pub fn serialize_vec<T>(&mut self, value: &T) -> Result<Vec<u8>>
363    where
364        T: ?Sized + Serialize,
365    {
366        let serialized = Body::from_serializable(value)?;
367        serialized.format_vec(&mut self.formatter)
368    }
369}
370
371/// Serialize the given value as an HCL byte vector.
372///
373/// If you want to serialize the data structures provided by this crate (e.g. [`Body`]) consider
374/// using [`hcl::format::to_vec`](crate::format::to_vec) instead because it is more efficient.
375///
376/// # Errors
377///
378/// Serialization fails if the type cannot be represented as HCL.
379pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
380where
381    T: ?Sized + Serialize,
382{
383    let mut serializer = Serializer::with_formatter(Formatter::default());
384    serializer.serialize_vec(value)
385}
386
387/// Serialize the given value as an HCL string.
388///
389/// If you want to serialize the data structures provided by this crate (e.g. [`Body`]) consider
390/// using [`hcl::format::to_string`](crate::format::to_string) instead because it is more
391/// efficient.
392///
393/// # Errors
394///
395/// Serialization fails if the type cannot be represented as HCL.
396pub fn to_string<T>(value: &T) -> Result<String>
397where
398    T: ?Sized + Serialize,
399{
400    let mut serializer = Serializer::with_formatter(Formatter::default());
401    serializer.serialize_string(value)
402}
403
404/// Serialize the given value as HCL into the IO stream.
405///
406/// If you want to serialize the data structures provided by this crate (e.g. [`Body`]) consider
407/// using [`hcl::format::to_writer`](crate::format::to_writer) instead because it is more
408/// efficient.
409///
410/// # Errors
411///
412/// Serialization fails if any operation on the writer fails or if the type cannot be represented
413/// as HCL.
414pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
415where
416    W: io::Write,
417    T: ?Sized + Serialize,
418{
419    let mut serializer = Serializer::new(writer);
420    serializer.serialize(value)
421}
422
423pub(crate) struct StringSerializer;
424
425impl ser::Serializer for StringSerializer {
426    type Ok = String;
427    type Error = Error;
428
429    type SerializeSeq = Impossible<String, Error>;
430    type SerializeTuple = Impossible<String, Error>;
431    type SerializeTupleStruct = Impossible<String, Error>;
432    type SerializeTupleVariant = Impossible<String, Error>;
433    type SerializeMap = Impossible<String, Error>;
434    type SerializeStruct = Impossible<String, Error>;
435    type SerializeStructVariant = Impossible<String, Error>;
436
437    serialize_unsupported! {
438        i8 i16 i32 i64 u8 u16 u32 u64
439        bool f32 f64 bytes unit unit_struct newtype_variant none
440        seq tuple tuple_struct tuple_variant map struct struct_variant
441    }
442    serialize_self! { some newtype_struct }
443
444    fn serialize_char(self, value: char) -> Result<Self::Ok> {
445        Ok(value.to_string())
446    }
447
448    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
449        Ok(value.to_owned())
450    }
451
452    fn serialize_unit_variant(
453        self,
454        _name: &'static str,
455        _variant_index: u32,
456        variant: &'static str,
457    ) -> Result<Self::Ok> {
458        Ok(variant.to_owned())
459    }
460
461    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
462    where
463        T: ?Sized + fmt::Display,
464    {
465        Ok(value.to_string())
466    }
467}
468
469pub(crate) struct IdentifierSerializer;
470
471impl ser::Serializer for IdentifierSerializer {
472    type Ok = Identifier;
473    type Error = Error;
474
475    type SerializeSeq = Impossible<Identifier, Error>;
476    type SerializeTuple = Impossible<Identifier, Error>;
477    type SerializeTupleStruct = Impossible<Identifier, Error>;
478    type SerializeTupleVariant = Impossible<Identifier, Error>;
479    type SerializeMap = Impossible<Identifier, Error>;
480    type SerializeStruct = Impossible<Identifier, Error>;
481    type SerializeStructVariant = Impossible<Identifier, Error>;
482
483    serialize_unsupported! {
484        i8 i16 i32 i64 u8 u16 u32 u64
485        bool f32 f64 bytes unit unit_struct newtype_variant none
486        seq tuple tuple_struct tuple_variant map struct struct_variant
487    }
488    serialize_self! { some newtype_struct }
489
490    fn serialize_char(self, value: char) -> Result<Self::Ok> {
491        self.serialize_str(&value.to_string())
492    }
493
494    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
495        Identifier::new(value)
496    }
497
498    fn serialize_unit_variant(
499        self,
500        _name: &'static str,
501        _variant_index: u32,
502        variant: &'static str,
503    ) -> Result<Self::Ok> {
504        self.serialize_str(variant)
505    }
506}
507
508struct U64Serializer;
509
510impl ser::Serializer for U64Serializer {
511    type Ok = u64;
512    type Error = Error;
513
514    type SerializeSeq = Impossible<u64, Error>;
515    type SerializeTuple = Impossible<u64, Error>;
516    type SerializeTupleStruct = Impossible<u64, Error>;
517    type SerializeTupleVariant = Impossible<u64, Error>;
518    type SerializeMap = Impossible<u64, Error>;
519    type SerializeStruct = Impossible<u64, Error>;
520    type SerializeStructVariant = Impossible<u64, Error>;
521
522    serialize_unsupported! {
523        i8 i16 i32 i64 u8 u16 u32 f32 f64 char str bool bytes
524        unit unit_variant unit_struct newtype_struct newtype_variant
525        some none seq tuple tuple_struct tuple_variant map struct struct_variant
526    }
527
528    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
529        Ok(value)
530    }
531}
532
533pub(crate) struct SerializeInternalHandleStruct {
534    handle: Option<u64>,
535}
536
537impl SerializeInternalHandleStruct {
538    pub(crate) fn new() -> Self {
539        SerializeInternalHandleStruct { handle: None }
540    }
541}
542
543impl ser::SerializeStruct for SerializeInternalHandleStruct {
544    type Ok = usize;
545    type Error = Error;
546
547    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
548    where
549        T: ?Sized + ser::Serialize,
550    {
551        assert_eq!(key, "handle", "bad handle struct");
552        self.handle = Some(value.serialize(U64Serializer)?);
553        Ok(())
554    }
555
556    fn end(self) -> Result<Self::Ok> {
557        let handle = self.handle.expect("bad handle reference in roundtrip");
558        Ok(handle as usize)
559    }
560}
561
562pub(crate) struct InternalHandles<T> {
563    marker: &'static str,
564    last_handle: AtomicUsize,
565    handles: RefCell<BTreeMap<usize, T>>,
566}
567
568impl<T> InternalHandles<T> {
569    pub(crate) fn new(marker: &'static str) -> InternalHandles<T> {
570        InternalHandles {
571            marker,
572            last_handle: AtomicUsize::new(0),
573            handles: RefCell::new(BTreeMap::new()),
574        }
575    }
576
577    pub(crate) fn remove(&self, handle: usize) -> T {
578        self.handles
579            .borrow_mut()
580            .remove(&handle)
581            .expect("handle not in registry")
582    }
583
584    pub(crate) fn serialize<V, S>(&self, value: V, serializer: S) -> Result<S::Ok, S::Error>
585    where
586        S: serde::Serializer,
587        V: Into<T>,
588    {
589        let handle = self.last_handle.fetch_add(1, Ordering::Relaxed);
590        self.handles.borrow_mut().insert(handle, value.into());
591        let mut s = serializer.serialize_struct(self.marker, 1)?;
592        s.serialize_field("handle", &handle)?;
593        s.end()
594    }
595}
596
597struct OnDrop<F: FnOnce()>(Option<F>);
598
599impl<F: FnOnce()> OnDrop<F> {
600    fn new(f: F) -> Self {
601        Self(Some(f))
602    }
603}
604
605impl<F: FnOnce()> Drop for OnDrop<F> {
606    fn drop(&mut self) {
607        self.0.take().unwrap()();
608    }
609}