Repository: martinohmann/hcl-rs
Pull request #53 (2022-08-20 08:32:54)

chore(deps): bump actions/cache from 3.0.5 to 3.0.7
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.0.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/cache/releases">actions/cache's releases</a>.</em></p>
<blockquote>
<h2>v3.0.7</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix for the download stuck problem has been added in actions/cache for users who were intermittently facing the issue. As part of this fix, new timeout has been introduced in the download step to stop the download if it doesn't complete within an hour and run the rest of the workflow without erroring out.</li>
</ul>
<h2>v3.0.6</h2>
<h3>What's Changed</h3>
<ul>
<li>Add example for clojure lein project dependencies by <a href="https://github.com/shivamarora1"><code>@​shivamarora1</code></a> in PR <a href="https://github-redirect.dependabot.com/actions/cache/pull/835">actions/cache#835</a></li>
<li>Update toolkit's cache npm module to latest. Bump cache version to v3.0.6 by <a href="https://github.com/pdotl"><code>@​pdotl</code></a> in PR <a href="https://github-redirect.dependabot.com/actions/cache/pull/887">actions/cache#887</a></li>
<li>Fix issue <a href="https://github-redirect.dependabot.com/actions/cache/issues/809">#809</a> where cache save/restore was failing for Amazon Linux 2 runners due to older tar version</li>
<li>Fix issue <a href="https://github-redirect.dependabot.com/actions/cache/issues/833">#833</a> where cache save was not working for caching github workspace directory</li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a href="https://github.com/shivamarora1"><code>@​shivamarora1</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/actions/cache/pull/835">actions/cache#835</a></li>
<li><a href="https://github.com/pdotl"><code>@​pdotl</code></a> made their first contribution in <a href="https://github-redirect.dependabot.com/actions/cache/pull/887">actions/cache#887</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/cache/compare/v3...v3.0.6">https://github.com/actions/cache/compare/v3...v3.0.6</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/cache/blob/main/RELEASES.md">actions/cache's changelog</a>.</em></p>
<blockquote>
<h3>3.0.5</h3>
<ul>
<li>Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. (<a href="https://github-redirect.dependabot.com/actions/cache/pull/834">PR</a>)</li>
</ul>
<h3>3.0.6</h3>
<ul>
<li>Fixed <a href="https://github-redirect.dependabot.com/actions/cache/issues/809">#809</a> - zstd -d: no such file or directory error</li>
<li>Fixed <a href="https://github-redirect.dependabot.com/actions/cache/issues/833">#833</a> - cache doesn't work with github workspace directory</li>
</ul>
<h3>3.0.7</h3>
<ul>
<li>Fixed <a href="https://github-redirect.dependabot.com/actions/cache/issues/810">#810</a> - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/cache/commit/a7c34adf76222e77931dedbf4a45b2e4648ced19"><code>a7c34ad</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/actions/cache/issues/894">#894</a> from actions/kotewar/update-toolkit-version</li>
<li><a href="https://github.com/actions/cache/commit/83394c99b7089b29ee6c302fad5e940df48c472a"><code>83394c9</code></a> Updated cache version in license file</li>
<li><a href="https://github.com/actions/cache/commit/e839c25979e6ffdd90792d34de69d6a87d3016f6"><code>e839c25</code></a> Updated actions/cache version to 3.0.3</li>
<li><a href="https://github.com/actions/cache/commit/33a923d660988a68affcd47de6bd6744193223a6"><code>33a923d</code></a> Added release information</li>
<li><a href="https://github.com/actions/cache/commit/a404368986bb049d406166fc20afc01d032ad8a2"><code>a404368</code></a> Updated actions/cache version to 3.0.2</li>
<li><a href="https://github.com/actions/cache/commit/f4278025ab0f432ce369118909e46deec636f50c"><code>f427802</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/actions/cache/issues/887">#887</a> from actions/pdotl-version-patch</li>
<li><a href="https://github.com/actions/cache/commit/9916fe1701f098b95a05e7c2bacee7c5a022c553"><code>9916fe1</code></a> Update cache version in licences</li>
<li><a href="https://github.com/actions/cache/commit/318935ef66557a1906560e4e7d0069344a4b31bf"><code>318935e</code></a> Update README and RELEASES</li>
<li><a href="https://github.com/actions/cache/commit/85efbb58b99b611af5e7f0bc6405955d82e12ef8"><code>85efbb5</code></a> Update cache npm module to latest</li>
<li><a href="https://github.com/actions/cache/commit/4387dbc81ae9321da605e73c0b1f097efb97dd1d"><code>4387dbc</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/actions/cache/issues/835">#835</a> from shivamarora1/clojure-lein-example</li>
<li>Additional commits viewable in <a href="https://github.com/actions/cache/compare/v3.0.5...v3.0.7">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/cache&package-manager=github_actions&previous-version=3.0.5&new-version=3.0.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c09c468..0e2ab87 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,7 +37,7 @@ jobs:
           override: true
 
       - name: Load cache
-        uses: actions/cache@v3.0.5
+        uses: actions/cache@v3.0.7
         with:
           path: |
             ~/.cargo/bin/
diff --git a/src/format/impls.rs b/src/format/impls.rs
new file mode 100644
index 0000000..c3a652d
--- /dev/null
+++ b/src/format/impls.rs
@@ -0,0 +1,252 @@
+use super::{private, Format};
+use crate::{
+    ser,
+    structure::{
+        Attribute, Block, BlockLabel, Body, Expression, Identifier, ObjectKey, RawExpression,
+        Structure,
+    },
+    Map, Number, Result, Value,
+};
+use std::io;
+
+impl private::Sealed for Body {}
+
+impl Format for Body {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        for structure in self.iter() {
+            structure.format(writer, fmt)?;
+        }
+
+        Ok(())
+    }
+}
+
+impl private::Sealed for Structure {}
+
+impl Format for Structure {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Structure::Attribute(attr) => attr.format(writer, fmt),
+            Structure::Block(block) => block.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Attribute {}
+
+impl Format for Attribute {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.begin_attribute(writer)?;
+        fmt.write_ident(writer, &self.key)?;
+        fmt.begin_attribute_value(writer)?;
+        self.expr.format(writer, fmt)?;
+        fmt.end_attribute(writer)?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for Block {}
+
+impl Format for Block {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.begin_block(writer)?;
+        fmt.write_ident(writer, &self.identifier)?;
+
+        for label in &self.labels {
+            writer.write_all(b" ")?;
+            label.format(writer, fmt)?;
+        }
+
+        fmt.begin_block_body(writer)?;
+        self.body.format(writer, fmt)?;
+        fmt.end_block(writer)?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for BlockLabel {}
+
+impl Format for BlockLabel {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            BlockLabel::Identifier(ident) => ident.format(writer, fmt),
+            BlockLabel::String(string) => string.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Expression {}
+
+impl Format for Expression {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Expression::Null => Ok(fmt.write_null(writer)?),
+            Expression::Bool(b) => Ok(fmt.write_bool(writer, *b)?),
+            Expression::Number(num) => num.format(writer, fmt),
+            Expression::String(string) => string.format(writer, fmt),
+            Expression::Array(array) => format_array(writer, fmt, array),
+            Expression::Object(object) => format_object(writer, fmt, object),
+            Expression::Raw(raw) => raw.format(writer, fmt),
+        }
+    }
+}
+
+impl private::Sealed for Value {}
+
+impl Format for Value {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            Value::Null => Ok(fmt.write_null(writer)?),
+            Value::Bool(b) => Ok(fmt.write_bool(writer, *b)?),
+            Value::Number(num) => num.format(writer, fmt),
+            Value::String(string) => string.format(writer, fmt),
+            Value::Array(array) => format_array(writer, fmt, array),
+            Value::Object(object) => format_object(writer, fmt, object),
+        }
+    }
+}
+
+impl private::Sealed for Number {}
+
+impl Format for Number {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match *self {
+            Number::PosInt(pos) => fmt.write_int(writer, pos)?,
+            Number::NegInt(neg) => fmt.write_int(writer, neg)?,
+            Number::Float(float) => fmt.write_float(writer, float)?,
+        }
+
+        Ok(())
+    }
+}
+
+impl private::Sealed for ObjectKey {}
+
+impl Format for ObjectKey {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        match self {
+            ObjectKey::Identifier(ident) => ident.format(writer, fmt),
+            ObjectKey::String(string) => string.format(writer, fmt),
+            ObjectKey::RawExpression(raw) => {
+                fmt.begin_interpolated_string(writer)?;
+                raw.format(writer, fmt)?;
+                fmt.end_interpolated_string(writer)?;
+                Ok(())
+            }
+        }
+    }
+}
+
+impl private::Sealed for RawExpression {}
+
+impl Format for RawExpression {
+    fn format<W, F>(&self, writer: &mut W, _: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        writer.write_all(self.as_str().as_bytes())?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for Identifier {}
+
+impl Format for Identifier {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.write_ident(writer, self.as_str())?;
+        Ok(())
+    }
+}
+
+impl private::Sealed for String {}
+
+impl Format for String {
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format,
+    {
+        fmt.write_quoted_string(writer, self)?;
+        Ok(())
+    }
+}
+
+fn format_array<W, F, T>(writer: &mut W, fmt: &mut F, array: &[T]) -> Result<()>
+where
+    W: ?Sized + io::Write,
+    F: ?Sized + ser::Format,
+    T: Format,
+{
+    fmt.begin_array(writer)?;
+
+    for value in array {
+        fmt.begin_array_value(writer)?;
+        value.format(writer, fmt)?;
+        fmt.end_array_value(writer)?;
+    }
+
+    fmt.end_array(writer)?;
+    Ok(())
+}
+
+fn format_object<W, F, K, V>(writer: &mut W, fmt: &mut F, object: &Map<K, V>) -> Result<()>
+where
+    W: ?Sized + io::Write,
+    F: ?Sized + ser::Format,
+    K: Format,
+    V: Format,
+{
+    fmt.begin_object(writer)?;
+
+    for (key, value) in object {
+        fmt.begin_object_key(writer)?;
+        key.format(writer, fmt)?;
+        fmt.begin_object_value(writer)?;
+        value.format(writer, fmt)?;
+        fmt.end_object_value(writer)?;
+    }
+
+    fmt.end_object(writer)?;
+    Ok(())
+}
diff --git a/src/format/mod.rs b/src/format/mod.rs
new file mode 100644
index 0000000..85f6eb0
--- /dev/null
+++ b/src/format/mod.rs
@@ -0,0 +1,29 @@
+//! Contains the logic to format HCL data structure.
+
+// @NOTE(mohmann): This module is not exported yet since it is subject to change due to a bigger
+// multi-step refactoring. It will eventually replace the formatting code inside serializer
+// module.
+
+mod impls;
+
+use crate::{ser, Result};
+use std::io;
+
+mod private {
+    pub trait Sealed {}
+}
+
+/// A trait to format data structures as HCL.
+///
+/// This trait is sealed to prevent implementation outside of this crate.
+pub trait Format: private::Sealed {
+    /// Formats a HCL structure using a formatter and writes the result to the provided writer.
+    ///
+    /// ## Errors
+    ///
+    /// Formatting the data structure or writing to the writer may fail with an `Error`.
+    fn format<W, F>(&self, writer: &mut W, fmt: &mut F) -> Result<()>
+    where
+        W: ?Sized + io::Write,
+        F: ?Sized + ser::Format;
+}
diff --git a/src/lib.rs b/src/lib.rs
index 23b33bc..910892e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,7 @@ pub mod de;
 pub mod error;
 #[macro_use]
 mod macros;
+mod format;
 mod number;
 mod parser;
 pub mod ser;
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 2f6a787..17b39cd 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -192,8 +192,14 @@ mod format;
 mod tests;
 
 pub use self::format::{Format, PrettyFormatter, PrettyFormatterBuilder};
-use crate::{Error, Result};
-use serde::ser::{self, Impossible, Serialize, SerializeSeq};
+use crate::{
+    structure::ser::body::{
+        BodySerializer, SerializeBodyMap, SerializeBodySeq, SerializeBodyStruct,
+        SerializeBodyStructVariant, SerializeBodyTupleVariant,
+    },
+    Error, Result,
+};
+use serde::ser::{self, Serialize};
 use std::io;
 
 /// A structure for serializing Rust values into HCL.
@@ -228,68 +234,11 @@ where
         self.writer
     }
 
-    fn serialize_attribute<K, V>(&mut self, key: &K, value: &V) -> Result<()>
+    fn format<V>(&mut self, value: V) -> Result<()>
     where
-        K: ?Sized + Serialize,
-        V: ?Sized + Serialize,
+        V: crate::format::Format,
     {
-        self.serialize_attribute_key(key)?;
-        self.serialize_attribute_value(value)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn serialize_attribute_key<K>(&mut self, key: &K) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-    {
-        self.formatter.begin_attribute(&mut self.writer)?;
-        key.serialize(IdentifierSerializer::new(self))
-    }
-
-    fn serialize_attribute_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))
-    }
-
-    fn serialize_array_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_array_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))?;
-        self.formatter.end_array_value(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn serialize_object_key_value<K, V>(&mut self, key: &K, value: &V) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-        V: ?Sized + Serialize,
-    {
-        self.serialize_object_key(key)?;
-        self.serialize_object_value(value)
-    }
-
-    fn serialize_object_key<K>(&mut self, key: &K) -> Result<()>
-    where
-        K: ?Sized + Serialize,
-    {
-        self.formatter.begin_object_key(&mut self.writer)?;
-        key.serialize(ObjectKeySerializer::new(self))
-    }
-
-    fn serialize_object_value<V>(&mut self, value: &V) -> Result<()>
-    where
-        V: ?Sized + Serialize,
-    {
-        self.formatter.begin_object_value(&mut self.writer)?;
-        value.serialize(ExpressionSerializer::new(self))?;
-        self.formatter.end_object_value(&mut self.writer)?;
-        Ok(())
+        value.format(&mut self.writer, &mut self.formatter)
     }
 }
 
@@ -301,13 +250,13 @@ where
     type Ok = ();
     type Error = Error;
 
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Self;
-    type SerializeTupleVariant = Self;
-    type SerializeMap = Self;
+    type SerializeSeq = SerializeSeq<'a, W, F>;
+    type SerializeTuple = SerializeSeq<'a, W, F>;
+    type SerializeTupleStruct = SerializeSeq<'a, W, F>;
+    type SerializeTupleVariant = SerializeTupleVariant<'a, W, F>;
+    type SerializeMap = SerializeMap<'a, W, F>;
     type SerializeStruct = SerializeStruct<'a, W, F>;
-    type SerializeStructVariant = Self;
+    type SerializeStructVariant = SerializeStructVariant<'a, W, F>;
 
     serialize_unsupported! {
         bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
@@ -318,49 +267,45 @@ where
     where
         T: ?Sized + Serialize,
     {
-        value.serialize(self)
+        BodySerializer
+            .serialize_some(value)
+            .and_then(|body| self.format(body))
     }
 
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
+    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        value.serialize(self)
+        BodySerializer
+            .serialize_newtype_struct(name, value)
+            .and_then(|body| self.format(body))
     }
 
-    /// Newtype variants have special handling for `hcl::Structure`. For this enum, the inner type
-    /// is serialized, which is either `hcl::Attribute` or `hcl::Block`. These will be handled by
-    /// `serialize_struct` below.
-    ///
-    /// Any other newtype variant is serialized as an HCL attribute (`VARIANT = VALUE`)
     fn serialize_newtype_variant<T>(
         self,
         name: &'static str,
-        _variant_index: u32,
+        variant_index: u32,
         variant: &'static str,
         value: &T,
     ) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        if name == "$hcl::structure" {
-            value.serialize(self)
-        } else {
-            self.serialize_attribute(variant, value)
-        }
+        BodySerializer
+            .serialize_newtype_variant(name, variant_index, variant, value)
+            .and_then(|body| self.format(body))
     }
 
-    /// A sequence of HCL attributes and blocks.
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(self)
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        BodySerializer
+            .serialize_seq(len)
+            .map(|inner| SerializeSeq { ser: self, inner })
     }
 
-    /// A tuple of HCL attributes and blocks.
     fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
         self.serialize_seq(Some(len))
     }
 
-    /// A tuple of HCL attributes and blocks.
     fn serialize_tuple_struct(
         self,
         _name: &'static str,
@@ -369,76 +314,50 @@ where
         self.serialize_seq(Some(len))
     }
 
-    /// Tuple variants are serialized as HCL attributes with an array value (`VARIANT = [...]`).
     fn serialize_tuple_variant(
         self,
-        _name: &'static str,
-        _variant_index: u32,
+        name: &'static str,
+        variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeTupleVariant> {
-        self.serialize_attribute_key(variant)?;
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        self.formatter.begin_array(&mut self.writer)?;
-        Ok(self)
+        BodySerializer
+            .serialize_tuple_variant(name, variant_index, variant, len)
+            .map(|inner| SerializeTupleVariant { ser: self, inner })
     }
 
-    /// Maps are serialized as sequences of HCL attributes (`KEY1 = VALUE1`).
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Ok(self)
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        BodySerializer
+            .serialize_map(len)
+            .map(|inner| SerializeMap { ser: self, inner })
     }
 
-    /// Structs have special handling for `hcl::Attribute` and `hcl::Block`. Attributes are
-    /// serialized as key-expression pairs (`KEY = EXPR`), whereas blocks are serialized as block
-    /// identifier, block labels (if any) and block body.
-    ///
-    /// Any other struct is serialized as a sequence of HCL attributes.
-    fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        let kind = match name {
-            "$hcl::attribute" => StructKind::Attribute,
-            "$hcl::block" => StructKind::Block,
-            _ => StructKind::Custom,
-        };
-
-        Ok(SerializeStruct::new(kind, self))
+    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        BodySerializer
+            .serialize_struct(name, len)
+            .map(|inner| SerializeStruct { ser: self, inner })
     }
 
-    /// Struct variants are serialized as HCL attributes with object value (`VARIANT = {...}`).
     fn serialize_struct_variant(
         self,
-        _name: &'static str,
-        _variant_index: u32,
+        name: &'static str,
+        variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant> {
-        self.serialize_attribute_key(variant)?;
-        self.formatter.begin_attribute_value(&mut self.writer)?;
-        self.formatter.begin_object(&mut self.writer)?;
-        Ok(self)
+        BodySerializer
+            .serialize_struct_variant(name, variant_index, variant, len)
+            .map(|inner| SerializeStructVariant { ser: self, inner })
     }
 }
 
-impl<'a, W, F> ser::SerializeSeq for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(&mut **self)
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeSeq<'a, W, F> {
+    inner: SerializeBodySeq,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeTuple for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeSeq for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -450,15 +369,15 @@ where
     where
         T: ?Sized + Serialize,
     {
-        ser::SerializeSeq::serialize_element(self, value)
+        self.inner.serialize_element(value)
     }
 
     fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleStruct for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeTuple for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -466,7 +385,7 @@ where
     type Ok = ();
     type Error = Error;
 
-    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
@@ -478,7 +397,7 @@ where
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleVariant for &'a mut Serializer<W, F>
+impl<'a, W, F> ser::SerializeTupleStruct for SerializeSeq<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -487,403 +406,6 @@ where
     type Error = Error;
 
     fn serialize_field<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_array_value(value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.formatter.end_array(&mut self.writer)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeMap for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute_key(key)
-    }
-
-    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute_value(value)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeStruct for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeStructVariant for &'a mut Serializer<W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.serialize_attribute(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.formatter.end_object(&mut self.writer)?;
-        self.formatter.end_attribute(&mut self.writer)?;
-        Ok(())
-    }
-}
-
-struct IdentifierSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> IdentifierSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> IdentifierSerializer<'a, W, F> {
-        IdentifierSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for IdentifierSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant newtype_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser.formatter.write_ident(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-}
-
-struct RawExpressionSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> RawExpressionSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> RawExpressionSerializer<'a, W, F> {
-        RawExpressionSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for RawExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant newtype_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser.writer.write_all(v.as_bytes())?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-}
-
-struct ObjectKeySerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> ObjectKeySerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> ObjectKeySerializer<'a, W, F> {
-        ObjectKeySerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for ObjectKeySerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Impossible<(), Error>;
-    type SerializeTuple = Impossible<(), Error>;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant
-        seq tuple tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        _variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        match name {
-            "$hcl::identifier" => value.serialize(IdentifierSerializer::new(self.ser)),
-            "$hcl::raw_expression" => {
-                self.ser
-                    .formatter
-                    .begin_interpolated_string(&mut self.ser.writer)?;
-                value.serialize(RawExpressionSerializer::new(self.ser))?;
-                self.ser
-                    .formatter
-                    .end_interpolated_string(&mut self.ser.writer)?;
-                Ok(())
-            }
-            _ => value.serialize(self),
-        }
-    }
-}
-
-struct BlockLabelSerializer<'a, W: 'a, F: 'a> {
-    ser: &'a mut Serializer<W, F>,
-}
-
-impl<'a, W, F> BlockLabelSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> BlockLabelSerializer<'a, W, F> {
-        BlockLabelSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Impossible<(), Error>;
-    type SerializeTupleVariant = Impossible<(), Error>;
-    type SerializeMap = Impossible<(), Error>;
-    type SerializeStruct = Impossible<(), Error>;
-    type SerializeStructVariant = Impossible<(), Error>;
-
-    serialize_unsupported! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
-        bytes none unit unit_struct unit_variant
-        tuple_struct tuple_variant
-        map struct struct_variant
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        _variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::identifier" {
-            value.serialize(IdentifierSerializer::new(self.ser))
-        } else {
-            value.serialize(self)
-        }
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(self)
-    }
-
-    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
-        self.serialize_seq(Some(len))
-    }
-}
-
-impl<'a, W, F> ser::SerializeSeq for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.writer.write_all(b" ")?;
-        value.serialize(BlockLabelSerializer::new(self.ser))
-    }
-
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeTuple for BlockLabelSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
@@ -895,260 +417,13 @@ where
     }
 }
 
-struct ExpressionSerializer<'a, W: 'a, F: 'a> {
+#[doc(hidden)]
+pub struct SerializeTupleVariant<'a, W, F> {
+    inner: SerializeBodyTupleVariant,
     ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ExpressionSerializer<'a, W, F> {
-    fn new(ser: &'a mut Serializer<W, F>) -> ExpressionSerializer<'a, W, F> {
-        ExpressionSerializer { ser }
-    }
-}
-
-impl<'a, W, F> ser::Serializer for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    type SerializeSeq = Self;
-    type SerializeTuple = Self;
-    type SerializeTupleStruct = Self;
-    type SerializeTupleVariant = Self;
-    type SerializeMap = Self;
-    type SerializeStruct = Self;
-    type SerializeStructVariant = Self;
-
-    fn serialize_bool(self, v: bool) -> Result<()> {
-        self.ser.formatter.write_bool(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_i8(self, v: i8) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i16(self, v: i16) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i32(self, v: i32) -> Result<()> {
-        self.serialize_i64(i64::from(v))
-    }
-
-    fn serialize_i64(self, v: i64) -> Result<()> {
-        self.ser.formatter.write_int(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_u8(self, v: u8) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u16(self, v: u16) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u32(self, v: u32) -> Result<()> {
-        self.serialize_u64(u64::from(v))
-    }
-
-    fn serialize_u64(self, v: u64) -> Result<()> {
-        self.ser.formatter.write_int(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_f32(self, v: f32) -> Result<()> {
-        self.serialize_f64(f64::from(v))
-    }
-
-    fn serialize_f64(self, v: f64) -> Result<()> {
-        self.ser.formatter.write_float(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_char(self, v: char) -> Result<()> {
-        self.serialize_str(&v.to_string())
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        self.ser
-            .formatter
-            .write_quoted_string(&mut self.ser.writer, v)?;
-        Ok(())
-    }
-
-    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
-        let mut seq = self.serialize_seq(Some(v.len()))?;
-        for byte in v {
-            seq.serialize_element(byte)?;
-        }
-        seq.end()
-    }
-
-    fn serialize_none(self) -> Result<()> {
-        self.serialize_unit()
-    }
-
-    fn serialize_some<T>(self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_unit(self) -> Result<()> {
-        self.ser.formatter.write_null(&mut self.ser.writer)?;
-        Ok(())
-    }
-
-    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
-        self.serialize_unit()
-    }
-
-    fn serialize_unit_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-    ) -> Result<()> {
-        self.serialize_str(variant)
-    }
-
-    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::raw_expression" {
-            value.serialize(RawExpressionSerializer::new(self.ser))
-        } else {
-            value.serialize(self)
-        }
-    }
-
-    fn serialize_newtype_variant<T>(
-        self,
-        name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        value: &T,
-    ) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        if name == "$hcl::expression" {
-            value.serialize(self)
-        } else {
-            self.ser.formatter.begin_object(&mut self.ser.writer)?;
-            self.ser.serialize_object_key_value(variant, value)?;
-            self.ser.formatter.end_object(&mut self.ser.writer)?;
-            Ok(())
-        }
-    }
-
-    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
-        self.ser.formatter.begin_array(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
-        self.serialize_seq(Some(len))
-    }
-
-    fn serialize_tuple_struct(
-        self,
-        _name: &'static str,
-        len: usize,
-    ) -> Result<Self::SerializeTupleStruct> {
-        self.serialize_seq(Some(len))
-    }
-
-    fn serialize_tuple_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        _len: usize,
-    ) -> Result<Self::SerializeTupleVariant> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        self.ser.serialize_object_key(variant)?;
-        self.ser
-            .formatter
-            .begin_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.begin_array(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        Ok(self)
-    }
-
-    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
-        self.serialize_map(Some(len))
-    }
-
-    fn serialize_struct_variant(
-        self,
-        _name: &'static str,
-        _variant_index: u32,
-        variant: &'static str,
-        _len: usize,
-    ) -> Result<Self::SerializeStructVariant> {
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        self.ser.serialize_object_key(variant)?;
-        self.ser
-            .formatter
-            .begin_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.begin_object(&mut self.ser.writer)?;
-        Ok(self)
-    }
-}
-
-impl<'a, W, F> ser::SerializeSeq for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.serialize_array_value(value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_array(&mut self.ser.writer)?;
-        Ok(())
-    }
-}
-
-impl<'a, W, F> ser::SerializeTuple for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
-    }
-}
-
-impl<'a, W, F> ser::SerializeTupleStruct for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeTupleVariant for SerializeTupleVariant<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1160,38 +435,21 @@ where
     where
         T: ?Sized + Serialize,
     {
-        ser::SerializeSeq::serialize_element(self, value)
+        self.inner.serialize_field(value)
     }
 
     fn end(self) -> Result<()> {
-        ser::SerializeSeq::end(self)
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeTupleVariant for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        ser::SerializeSeq::serialize_element(self, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_array(&mut self.ser.writer)?;
-        self.ser.formatter.end_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeMap<'a, W, F> {
+    inner: SerializeBodyMap,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeMap for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeMap for SerializeMap<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1203,44 +461,28 @@ where
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_key(key)
+        self.inner.serialize_key(key)
     }
 
     fn serialize_value<T>(&mut self, value: &T) -> Result<()>
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_value(value)
+        self.inner.serialize_value(value)
     }
 
     fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-impl<'a, W, F> ser::SerializeStruct for ExpressionSerializer<'a, W, F>
-where
-    W: io::Write,
-    F: Format,
-{
-    type Ok = ();
-    type Error = Error;
-
-    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
-    where
-        T: ?Sized + Serialize,
-    {
-        self.ser.serialize_object_key_value(key, value)
-    }
-
-    fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
-    }
+#[doc(hidden)]
+pub struct SerializeStructVariant<'a, W, F> {
+    inner: SerializeBodyStructVariant,
+    ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> ser::SerializeStructVariant for ExpressionSerializer<'a, W, F>
+impl<'a, W, F> ser::SerializeStructVariant for SerializeStructVariant<'a, W, F>
 where
     W: io::Write,
     F: Format,
@@ -1252,35 +494,20 @@ where
     where
         T: ?Sized + Serialize,
     {
-        self.ser.serialize_object_key_value(key, value)
+        self.inner.serialize_field(key, value)
     }
 
     fn end(self) -> Result<()> {
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        self.ser.formatter.end_object_value(&mut self.ser.writer)?;
-        self.ser.formatter.end_object(&mut self.ser.writer)?;
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
-enum StructKind {
-    Attribute,
-    Block,
-    Custom,
-}
-
 #[doc(hidden)]
 pub struct SerializeStruct<'a, W, F> {
-    kind: StructKind,
+    inner: SerializeBodyStruct,
     ser: &'a mut Serializer<W, F>,
 }
 
-impl<'a, W, F> SerializeStruct<'a, W, F> {
-    fn new(kind: StructKind, ser: &'a mut Serializer<W, F>) -> Self {
-        SerializeStruct { kind, ser }
-    }
-}
-
 impl<'a, W, F> ser::SerializeStruct for SerializeStruct<'a, W, F>
 where
     W: io::Write,
@@ -1293,36 +520,11 @@ where
     where
         T: ?Sized + Serialize,
     {
-        match self.kind {
-            StructKind::Attribute => match key {
-                "key" => self.ser.serialize_attribute_key(value),
-                "expr" => {
-                    self.ser.serialize_attribute_value(value)?;
-                    self.ser.formatter.end_attribute(&mut self.ser.writer)?;
-                    Ok(())
-                }
-                _ => Ok(()),
-            },
-            StructKind::Block => match key {
-                "identifier" => {
-                    self.ser.formatter.begin_block(&mut self.ser.writer)?;
-                    value.serialize(IdentifierSerializer::new(self.ser))
-                }
-                "labels" => value.serialize(BlockLabelSerializer::new(self.ser)),
-                "body" => {
-                    self.ser.formatter.begin_block_body(&mut self.ser.writer)?;
-                    value.serialize(&mut *self.ser)?;
-                    self.ser.formatter.end_block(&mut self.ser.writer)?;
-                    Ok(())
-                }
-                _ => Ok(()),
-            },
-            StructKind::Custom => self.ser.serialize_attribute(key, value),
-        }
+        self.inner.serialize_field(key, value)
     }
 
     fn end(self) -> Result<()> {
-        Ok(())
+        self.inner.end().and_then(|body| self.ser.format(body))
     }
 }
 
diff --git a/src/structure/body.rs b/src/structure/body.rs
index 6a7dc6d..0179bdc 100644
--- a/src/structure/body.rs
+++ b/src/structure/body.rs
@@ -193,6 +193,15 @@ impl From<Body> for Map<String, Value> {
     }
 }
 
+impl<T> From<T> for Body
+where
+    T: Into<Structure>,
+{
+    fn from(value: T) -> Body {
+        Body(vec![value.into()])
+    }
+}
+
 impl<S> FromIterator<S> for Body
 where
     S: Into<Structure>,
diff --git a/src/structure/expression.rs b/src/structure/expression.rs
index 204e068..eb1b525 100644
--- a/src/structure/expression.rs
+++ b/src/structure/expression.rs
@@ -253,6 +253,12 @@ impl RawExpression {
     pub fn into_inner(self) -> String {
         self.0
     }
+
+    /// Returns the `RawExpression` as a `&str`. If you want to represent the `RawExpression` as
+    /// an interpolated string, use `.to_string()` instead.
+    pub fn as_str(&self) -> &str {
+        &self.0
+    }
 }
 
 impl From<String> for RawExpression {
diff --git a/src/structure/ser/attribute.rs b/src/structure/ser/attribute.rs
new file mode 100644
index 0000000..ade2e6a
--- /dev/null
+++ b/src/structure/ser/attribute.rs
@@ -0,0 +1,329 @@
+use super::{expression::ExpressionSerializer, StringSerializer};
+use crate::{serialize_unsupported, Attribute, Error, Expression, Object, ObjectKey, Result};
+use serde::ser::{self, Serialize};
+
+pub struct AttributeSerializer;
+
+impl ser::Serializer for AttributeSerializer {
+    type Ok = Attribute;
+    type Error = Error;
+
+    type SerializeSeq = SerializeAttributeSeq;
+    type SerializeTuple = SerializeAttributeSeq;
+    type SerializeTupleStruct = SerializeAttributeSeq;
+    type SerializeTupleVariant = SerializeAttributeTupleVariant;
+    type SerializeMap = SerializeAttributeMap;
+    type SerializeStruct = SerializeAttributeStruct;
+    type SerializeStructVariant = SerializeAttributeStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Attribute>
+    where
+        T: ?Sized + Serialize,
+    {
+        Ok(Attribute::new(
+            variant,
+            value.serialize(ExpressionSerializer)?,
+        ))
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeAttributeSeq::new())
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeAttributeTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeAttributeMap::new())
+    }
+
+    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeAttributeStruct::new())
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeAttributeStructVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeAttributeSeq {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeSeq {
+    pub fn new() -> Self {
+        SerializeAttributeSeq {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            self.key = Some(value.serialize(StringSerializer)?);
+        } else if self.expr.is_none() {
+            self.expr = Some(value.serialize(ExpressionSerializer)?);
+        } else {
+            return Err(ser::Error::custom("expected sequence with 2 elements"));
+        }
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (_, _) => Err(ser::Error::custom("expected sequence with 2 elements")),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeAttributeSeq {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeAttributeTupleVariant {
+    key: String,
+    vec: Vec<Expression>,
+}
+
+impl SerializeAttributeTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeAttributeTupleVariant {
+            key: variant.to_owned(),
+            vec: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeAttributeTupleVariant {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.key, self.vec))
+    }
+}
+
+pub struct SerializeAttributeMap {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeMap {
+    pub fn new() -> Self {
+        SerializeAttributeMap {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeAttributeMap {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            self.key = Some(key.serialize(StringSerializer)?);
+            Ok(())
+        } else {
+            Err(ser::Error::custom("expected map with 1 entry"))
+        }
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.key.is_none() {
+            panic!("serialize_value called before serialize_key");
+        }
+
+        self.expr = Some(value.serialize(ExpressionSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (Some(_), None) => Err(ser::Error::custom("attribute value missing")),
+            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
+        }
+    }
+}
+
+pub struct SerializeAttributeStruct {
+    key: Option<String>,
+    expr: Option<Expression>,
+}
+
+impl SerializeAttributeStruct {
+    pub fn new() -> Self {
+        SerializeAttributeStruct {
+            key: None,
+            expr: None,
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeAttributeStruct {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match key {
+            "key" => self.key = Some(value.serialize(StringSerializer)?),
+            "expr" => self.expr = Some(value.serialize(ExpressionSerializer)?),
+            _ => {
+                return Err(ser::Error::custom(
+                    "expected struct with fields `key` and `expr`",
+                ))
+            }
+        };
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.key, self.expr) {
+            (Some(key), Some(expr)) => Ok(Attribute::new(key, expr)),
+            (_, _) => Err(ser::Error::custom(
+                "expected struct with fields `key` and `expr`",
+            )),
+        }
+    }
+}
+
+pub struct SerializeAttributeStructVariant {
+    key: String,
+    map: Object<ObjectKey, Expression>,
+}
+
+impl SerializeAttributeStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeAttributeStructVariant {
+            key: variant.to_owned(),
+            map: Object::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeAttributeStructVariant {
+    type Ok = Attribute;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(ObjectKey::identifier(key), expr);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Attribute::new(self.key, self.map))
+    }
+}
diff --git a/src/structure/ser/block.rs b/src/structure/ser/block.rs
new file mode 100644
index 0000000..adcab4e
--- /dev/null
+++ b/src/structure/ser/block.rs
@@ -0,0 +1,440 @@
+use super::{
+    body::BodySerializer, expression::ExpressionSerializer, structure::StructureSerializer,
+    IdentifierSerializer, SeqSerializer, StringSerializer,
+};
+use crate::{serialize_unsupported, Attribute, Block, BlockLabel, Body, Error, Result, Structure};
+use serde::ser::{self, Impossible, Serialize};
+use std::fmt::Display;
+
+pub struct BlockSerializer;
+
+impl ser::Serializer for BlockSerializer {
+    type Ok = Block;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBlockSeq;
+    type SerializeTuple = SerializeBlockSeq;
+    type SerializeTupleStruct = SerializeBlockSeq;
+    type SerializeTupleVariant = SerializeBlockVariant;
+    type SerializeMap = SerializeBlockMap;
+    type SerializeStruct = SerializeBlockStruct;
+    type SerializeStructVariant = SerializeBlockVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Block>
+    where
+        T: ?Sized + Serialize,
+    {
+        Ok(Block {
+            identifier: variant.to_owned(),
+            labels: Vec::new(),
+            body: value.serialize(BodySerializer)?,
+        })
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBlockSeq::new())
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeBlockVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeBlockMap::new())
+    }
+
+    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeBlockStruct::new())
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeBlockVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeBlockSeq {
+    identifier: Option<String>,
+    labels: Option<Vec<BlockLabel>>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockSeq {
+    pub fn new() -> Self {
+        SerializeBlockSeq {
+            identifier: None,
+            labels: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            self.identifier = Some(value.serialize(StringSerializer)?);
+        } else if self.labels.is_none() {
+            self.labels = Some(value.serialize(SeqSerializer::new(BlockLabelSerializer))?);
+        } else if self.body.is_none() {
+            self.body = Some(value.serialize(BodySerializer)?);
+        } else {
+            return Err(ser::Error::custom("expected sequence with 3 elements"));
+        }
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.labels, self.body) {
+            (Some(ident), Some(labels), Some(body)) => Ok(Block::new(ident, labels, body)),
+            (_, _, _) => Err(ser::Error::custom("expected sequence with 3 elements")),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeBlockSeq {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeBlockVariant {
+    identifier: String,
+    structures: Vec<Structure>,
+}
+
+impl SerializeBlockVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBlockVariant {
+            identifier: variant.to_owned(),
+            structures: Vec::with_capacity(len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeBlockVariant {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.structures.push(value.serialize(StructureSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeStructVariant::end(self)
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeBlockVariant {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.structures.push(Attribute::new(key, expr).into());
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Block {
+            identifier: self.identifier,
+            labels: Vec::new(),
+            body: Body(self.structures),
+        })
+    }
+}
+
+pub struct SerializeBlockMap {
+    identifier: Option<String>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockMap {
+    pub fn new() -> Self {
+        SerializeBlockMap {
+            identifier: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeBlockMap {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            self.identifier = Some(key.serialize(StringSerializer)?);
+            Ok(())
+        } else {
+            Err(ser::Error::custom("expected map with 1 entry"))
+        }
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        if self.identifier.is_none() {
+            panic!("serialize_value called before serialize_key");
+        }
+
+        self.body = Some(value.serialize(BodySerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.body) {
+            (Some(ident), Some(body)) => Ok(Block::new(ident, Vec::<BlockLabel>::new(), body)),
+            (_, _) => Err(ser::Error::custom("expected map with 1 entry")),
+        }
+    }
+}
+
+pub struct SerializeBlockStruct {
+    identifier: Option<String>,
+    labels: Option<Vec<BlockLabel>>,
+    body: Option<Body>,
+}
+
+impl SerializeBlockStruct {
+    pub fn new() -> Self {
+        SerializeBlockStruct {
+            identifier: None,
+            labels: None,
+            body: None,
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeBlockStruct {
+    type Ok = Block;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match key {
+            "identifier" => self.identifier = Some(value.serialize(StringSerializer)?),
+            "labels" => {
+                self.labels = Some(value.serialize(SeqSerializer::new(BlockLabelSerializer))?)
+            }
+            "body" => self.body = Some(value.serialize(BodySerializer)?),
+            _ => {
+                return Err(ser::Error::custom(
+                    "expected struct with fields `identifier`, `body` and optional `labels`",
+                ))
+            }
+        };
+
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match (self.identifier, self.body) {
+            (Some(ident), Some(body)) => {
+                Ok(Block::new(ident, self.labels.unwrap_or_default(), body))
+            }
+            (_, _) => Err(ser::Error::custom(
+                "expected struct with fields `identifier`, `body` and optional `labels`",
+            )),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct BlockLabelSerializer;
+
+impl ser::Serializer for BlockLabelSerializer {
+    type Ok = BlockLabel;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<BlockLabel, Error>;
+    type SerializeTuple = Impossible<BlockLabel, Error>;
+    type SerializeTupleStruct = Impossible<BlockLabel, Error>;
+    type SerializeTupleVariant = Impossible<BlockLabel, Error>;
+    type SerializeMap = Impossible<BlockLabel, Error>;
+    type SerializeStruct = Impossible<BlockLabel, Error>;
+    type SerializeStructVariant = Impossible<BlockLabel, Error>;
+
+    serialize_unsupported! {
+        bool f32 f64 bytes unit unit_struct none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(BlockLabel::string(value))
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(BlockLabel::identifier(variant))
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        // Specialization for the `BlockLabel` type itself.
+        match (name, variant) {
+            ("$hcl::block_label", "Identifier") => Ok(BlockLabel::Identifier(
+                value.serialize(IdentifierSerializer)?,
+            )),
+            ("$hcl::block_label", "String") => {
+                Ok(BlockLabel::String(value.serialize(StringSerializer)?))
+            }
+            (_, _) => value.serialize(self),
+        }
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(BlockLabel::String(value.to_string()))
+    }
+}
diff --git a/src/structure/ser/body.rs b/src/structure/ser/body.rs
new file mode 100644
index 0000000..b55da88
--- /dev/null
+++ b/src/structure/ser/body.rs
@@ -0,0 +1,306 @@
+use super::{
+    attribute::SerializeAttributeStruct,
+    block::SerializeBlockStruct,
+    expression::ExpressionSerializer,
+    structure::{
+        SerializeStructureStructVariant, SerializeStructureTupleVariant, StructureSerializer,
+    },
+    IdentifierSerializer,
+};
+use crate::{serialize_unsupported, Attribute, Body, Error, Result, Structure};
+use serde::ser::{self, Serialize, SerializeMap};
+
+pub struct BodySerializer;
+
+impl ser::Serializer for BodySerializer {
+    type Ok = Body;
+    type Error = Error;
+
+    type SerializeSeq = SerializeBodySeq;
+    type SerializeTuple = SerializeBodySeq;
+    type SerializeTupleStruct = SerializeBodySeq;
+    type SerializeTupleVariant = SerializeBodyTupleVariant;
+    type SerializeMap = SerializeBodyMap;
+    type SerializeStruct = SerializeBodyStruct;
+    type SerializeStructVariant = SerializeBodyStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Body>
+    where
+        T: ?Sized + Serialize,
+    {
+        match name {
+            "$hcl::body" => value.serialize(self),
+            _ => StructureSerializer
+                .serialize_newtype_variant(name, variant_index, variant, value)
+                .map(Into::into),
+        }
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeBodySeq::new(len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeBodyTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeBodyMap::new(len))
+    }
+
+    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeBodyStruct::new(name, len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeBodyStructVariant::new(variant, len))
+    }
+}
+
+pub struct SerializeBodySeq {
+    vec: Vec<Structure>,
+}
+
+impl SerializeBodySeq {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeBodySeq {
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(StructureSerializer)?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Body(self.vec))
+    }
+}
+
+impl ser::SerializeTuple for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeBodySeq {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeBodyTupleVariant {
+    inner: SerializeStructureTupleVariant,
+}
+
+impl SerializeBodyTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBodyTupleVariant {
+            inner: SerializeStructureTupleVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeBodyTupleVariant {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub struct SerializeBodyMap {
+    structures: Vec<Structure>,
+    next_key: Option<String>,
+}
+
+impl SerializeBodyMap {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeBodyMap {
+            structures: Vec::with_capacity(len.unwrap_or(0)),
+            next_key: None,
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeBodyMap {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.next_key = Some(key.serialize(IdentifierSerializer)?.into_inner());
+        Ok(())
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        let key = self.next_key.take();
+        let key = key.expect("serialize_value called before serialize_key");
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.structures.push(Attribute::new(key, expr).into());
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        Ok(Body(self.structures))
+    }
+}
+
+pub enum SerializeBodyStruct {
+    Attribute(SerializeAttributeStruct),
+    Block(SerializeBlockStruct),
+    Other(SerializeBodyMap),
+}
+
+impl SerializeBodyStruct {
+    pub fn new(name: &'static str, len: usize) -> Self {
+        match name {
+            "$hcl::attribute" => SerializeBodyStruct::Attribute(SerializeAttributeStruct::new()),
+            "$hcl::block" => SerializeBodyStruct::Block(SerializeBlockStruct::new()),
+            _ => SerializeBodyStruct::Other(SerializeBodyMap::new(Some(len))),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeBodyStruct {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        match self {
+            SerializeBodyStruct::Attribute(ser) => ser.serialize_field(key, value),
+            SerializeBodyStruct::Block(ser) => ser.serialize_field(key, value),
+            SerializeBodyStruct::Other(ser) => ser.serialize_entry(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Body> {
+        match self {
+            SerializeBodyStruct::Attribute(ser) => ser.end().map(Into::into),
+            SerializeBodyStruct::Block(ser) => ser.end().map(Into::into),
+            SerializeBodyStruct::Other(ser) => ser.end(),
+        }
+    }
+}
+
+pub struct SerializeBodyStructVariant {
+    inner: SerializeStructureStructVariant,
+}
+
+impl SerializeBodyStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeBodyStructVariant {
+            inner: SerializeStructureStructVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeBodyStructVariant {
+    type Ok = Body;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(key, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
diff --git a/src/structure/ser.rs b/src/structure/ser/expression.rs
similarity index 77%
rename from src/structure/ser.rs
rename to src/structure/ser/expression.rs
index 35edf58..9a91b50 100644
--- a/src/structure/ser.rs
+++ b/src/structure/ser/expression.rs
@@ -1,3 +1,4 @@
+use super::{IdentifierSerializer, StringSerializer};
 use crate::{serialize_unsupported, Error, Expression, Object, ObjectKey, Result};
 use serde::ser::{self, Impossible};
 use std::fmt::Display;
@@ -93,16 +94,20 @@ impl ser::Serializer for ExpressionSerializer {
         self.serialize_str(variant)
     }
 
-    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
     where
         T: ?Sized + ser::Serialize,
     {
-        value.serialize(self)
+        if name == "$hcl::raw_expression" {
+            Ok(Expression::Raw(value.serialize(StringSerializer)?.into()))
+        } else {
+            value.serialize(self)
+        }
     }
 
     fn serialize_newtype_variant<T>(
         self,
-        _name: &'static str,
+        name: &'static str,
         _variant_index: u32,
         variant: &'static str,
         value: &T,
@@ -110,9 +115,13 @@ impl ser::Serializer for ExpressionSerializer {
     where
         T: ?Sized + ser::Serialize,
     {
-        let mut object = Object::new();
-        object.insert(ObjectKey::string(variant), to_expression(&value)?);
-        Ok(Expression::Object(object))
+        if name == "$hcl::expression" {
+            value.serialize(self)
+        } else {
+            let mut object = Object::new();
+            object.insert(ObjectKey::identifier(variant), value.serialize(self)?);
+            Ok(Expression::Object(object))
+        }
     }
 
     fn serialize_none(self) -> Result<Self::Ok> {
@@ -127,9 +136,7 @@ impl ser::Serializer for ExpressionSerializer {
     }
 
     fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        Ok(SerializeExpressionSeq {
-            vec: Vec::with_capacity(len.unwrap_or(0)),
-        })
+        Ok(SerializeExpressionSeq::new(len))
     }
 
     fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
@@ -151,17 +158,11 @@ impl ser::Serializer for ExpressionSerializer {
         variant: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleVariant> {
-        Ok(SerializeExpressionTupleVariant {
-            name: variant.to_owned(),
-            vec: Vec::with_capacity(len),
-        })
+        Ok(SerializeExpressionTupleVariant::new(variant, len))
     }
 
-    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Ok(SerializeExpressionMap {
-            map: Object::new(),
-            next_key: None,
-        })
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeExpressionMap::new(len))
     }
 
     fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
@@ -173,12 +174,9 @@ impl ser::Serializer for ExpressionSerializer {
         _name: &'static str,
         _variant_index: u32,
         variant: &'static str,
-        _len: usize,
+        len: usize,
     ) -> Result<Self::SerializeStructVariant> {
-        Ok(SerializeExpressionStructVariant {
-            name: variant.to_owned(),
-            map: Object::new(),
-        })
+        Ok(SerializeExpressionStructVariant::new(variant, len))
     }
 
     fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
@@ -188,10 +186,19 @@ impl ser::Serializer for ExpressionSerializer {
         Ok(Expression::String(value.to_string()))
     }
 }
+
 pub struct SerializeExpressionSeq {
     vec: Vec<Expression>,
 }
 
+impl SerializeExpressionSeq {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeExpressionSeq {
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
 impl ser::SerializeSeq for SerializeExpressionSeq {
     type Ok = Expression;
     type Error = Error;
@@ -200,7 +207,7 @@ impl ser::SerializeSeq for SerializeExpressionSeq {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(to_expression(&value)?);
+        self.vec.push(value.serialize(ExpressionSerializer)?);
         Ok(())
     }
 
@@ -242,10 +249,19 @@ impl serde::ser::SerializeTupleStruct for SerializeExpressionSeq {
 }
 
 pub struct SerializeExpressionTupleVariant {
-    name: String,
+    name: ObjectKey,
     vec: Vec<Expression>,
 }
 
+impl SerializeExpressionTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeExpressionTupleVariant {
+            name: ObjectKey::from(variant),
+            vec: Vec::with_capacity(len),
+        }
+    }
+}
+
 impl ser::SerializeTupleVariant for SerializeExpressionTupleVariant {
     type Ok = Expression;
     type Error = Error;
@@ -254,13 +270,13 @@ impl ser::SerializeTupleVariant for SerializeExpressionTupleVariant {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.vec.push(to_expression(&value)?);
+        self.vec.push(value.serialize(ExpressionSerializer)?);
         Ok(())
     }
 
     fn end(self) -> Result<Self::Ok> {
         let mut object = Object::new();
-        object.insert(ObjectKey::String(self.name), Expression::Array(self.vec));
+        object.insert(self.name, self.vec.into());
         Ok(Expression::Object(object))
     }
 }
@@ -270,6 +286,15 @@ pub struct SerializeExpressionMap {
     next_key: Option<ObjectKey>,
 }
 
+impl SerializeExpressionMap {
+    pub fn new(len: Option<usize>) -> Self {
+        SerializeExpressionMap {
+            map: Object::with_capacity(len.unwrap_or(0)),
+            next_key: None,
+        }
+    }
+}
+
 impl ser::SerializeMap for SerializeExpressionMap {
     type Ok = Expression;
     type Error = Error;
@@ -288,7 +313,8 @@ impl ser::SerializeMap for SerializeExpressionMap {
     {
         let key = self.next_key.take();
         let key = key.expect("serialize_value called before serialize_key");
-        self.map.insert(key, to_expression(&value)?);
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(key, expr);
         Ok(())
     }
 
@@ -314,10 +340,19 @@ impl ser::SerializeStruct for SerializeExpressionMap {
 }
 
 pub struct SerializeExpressionStructVariant {
-    name: String,
+    name: ObjectKey,
     map: Object<ObjectKey, Expression>,
 }
 
+impl SerializeExpressionStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeExpressionStructVariant {
+            name: ObjectKey::from(variant),
+            map: Object::with_capacity(len),
+        }
+    }
+}
+
 impl ser::SerializeStructVariant for SerializeExpressionStructVariant {
     type Ok = Expression;
     type Error = Error;
@@ -326,19 +361,19 @@ impl ser::SerializeStructVariant for SerializeExpressionStructVariant {
     where
         T: ?Sized + ser::Serialize,
     {
-        self.map
-            .insert(ObjectKey::string(key), to_expression(&value)?);
+        let expr = value.serialize(ExpressionSerializer)?;
+        self.map.insert(ObjectKey::from(key), expr);
         Ok(())
     }
 
     fn end(self) -> Result<Self::Ok> {
         let mut object = Object::new();
-        object.insert(ObjectKey::String(self.name), Expression::Object(self.map));
+        object.insert(self.name, self.map.into());
         Ok(Expression::Object(object))
     }
 }
 
-struct ObjectKeySerializer;
+pub struct ObjectKeySerializer;
 
 impl ser::Serializer for ObjectKeySerializer {
     type Ok = ObjectKey;
@@ -353,7 +388,7 @@ impl ser::Serializer for ObjectKeySerializer {
     type SerializeStructVariant = Impossible<ObjectKey, Error>;
 
     serialize_unsupported! {
-        bool f32 f64 bytes unit unit_struct newtype_variant none
+        bool f32 f64 bytes unit unit_struct none
         some seq tuple tuple_struct tuple_variant map struct struct_variant
     }
 
@@ -413,6 +448,28 @@ impl ser::Serializer for ObjectKeySerializer {
         value.serialize(self)
     }
 
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        // Specialization for the `ObjectKey` type itself.
+        match (name, variant) {
+            ("$hcl::object_key", "Identifier") => Ok(ObjectKey::Identifier(
+                value.serialize(IdentifierSerializer)?,
+            )),
+            ("$hcl::object_key", "RawExpression") => Ok(ObjectKey::RawExpression(
+                value.serialize(StringSerializer)?.into(),
+            )),
+            (_, _) => value.serialize(self),
+        }
+    }
+
     fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
     where
         T: ?Sized + Display,
diff --git a/src/structure/ser/mod.rs b/src/structure/ser/mod.rs
new file mode 100644
index 0000000..8f9fe72
--- /dev/null
+++ b/src/structure/ser/mod.rs
@@ -0,0 +1,294 @@
+//! Serializer impls for HCL structure types.
+
+mod attribute;
+mod block;
+pub(crate) mod body;
+mod expression;
+mod structure;
+#[cfg(test)]
+mod tests;
+
+pub use self::expression::to_expression;
+use super::Identifier;
+use crate::{serialize_unsupported, Error, Result};
+use serde::ser::{self, Impossible, Serialize};
+use std::fmt::Display;
+
+pub struct IdentifierSerializer;
+
+impl ser::Serializer for IdentifierSerializer {
+    type Ok = Identifier;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<Identifier, Error>;
+    type SerializeTuple = Impossible<Identifier, Error>;
+    type SerializeTupleStruct = Impossible<Identifier, Error>;
+    type SerializeTupleVariant = Impossible<Identifier, Error>;
+    type SerializeMap = Impossible<Identifier, Error>;
+    type SerializeStruct = Impossible<Identifier, Error>;
+    type SerializeStructVariant = Impossible<Identifier, Error>;
+
+    serialize_unsupported! {
+        i8 i16 i32 i64 u8 u16 u32 u64
+        bool f32 f64 bytes unit unit_struct newtype_variant none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(Identifier(value.to_string()))
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(Identifier(value.to_owned()))
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(Identifier(variant.to_owned()))
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(Identifier(value.to_string()))
+    }
+}
+
+pub struct StringSerializer;
+
+impl ser::Serializer for StringSerializer {
+    type Ok = String;
+    type Error = Error;
+
+    type SerializeSeq = Impossible<String, Error>;
+    type SerializeTuple = Impossible<String, Error>;
+    type SerializeTupleStruct = Impossible<String, Error>;
+    type SerializeTupleVariant = Impossible<String, Error>;
+    type SerializeMap = Impossible<String, Error>;
+    type SerializeStruct = Impossible<String, Error>;
+    type SerializeStructVariant = Impossible<String, Error>;
+
+    serialize_unsupported! {
+        bool f32 f64 bytes unit unit_struct newtype_variant none
+        some seq tuple tuple_struct tuple_variant map struct struct_variant
+    }
+
+    fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_char(self, value: char) -> Result<Self::Ok> {
+        Ok(value.to_string())
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+        Ok(value.to_owned())
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Self::Ok> {
+        Ok(variant.to_owned())
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Display,
+    {
+        Ok(value.to_string())
+    }
+}
+
+pub struct SeqSerializer<S> {
+    inner: S,
+}
+
+impl<S> SeqSerializer<S> {
+    pub fn new(inner: S) -> Self {
+        SeqSerializer { inner }
+    }
+}
+
+impl<S> ser::Serializer for SeqSerializer<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    type SerializeSeq = SerializeSeq<S>;
+    type SerializeTuple = SerializeSeq<S>;
+    type SerializeTupleStruct = SerializeSeq<S>;
+    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
+    type SerializeMap = Impossible<Self::Ok, Self::Error>;
+    type SerializeStruct = Impossible<Self::Ok, Self::Error>;
+    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit newtype_variant unit_struct unit_variant
+        tuple_variant map struct struct_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(
+        self,
+        _name: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok, Self::Error>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+        Ok(SerializeSeq::new(self.inner, len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+        self.serialize_seq(Some(len))
+    }
+}
+
+pub struct SerializeSeq<S>
+where
+    S: ser::Serializer,
+{
+    inner: S,
+    vec: Vec<S::Ok>,
+}
+
+impl<S> SerializeSeq<S>
+where
+    S: ser::Serializer,
+{
+    fn new(inner: S, len: Option<usize>) -> Self {
+        SerializeSeq {
+            inner,
+            vec: Vec::with_capacity(len.unwrap_or(0)),
+        }
+    }
+}
+
+impl<S> ser::SerializeSeq for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.vec.push(value.serialize(self.inner.clone())?);
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        Ok(self.vec)
+    }
+}
+
+impl<S> ser::SerializeTuple for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl<S> serde::ser::SerializeTupleStruct for SerializeSeq<S>
+where
+    S: ser::Serializer + Clone,
+{
+    type Ok = Vec<S::Ok>;
+    type Error = S::Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok, Self::Error> {
+        ser::SerializeSeq::end(self)
+    }
+}
diff --git a/src/structure/ser/structure.rs b/src/structure/ser/structure.rs
new file mode 100644
index 0000000..99d08b6
--- /dev/null
+++ b/src/structure/ser/structure.rs
@@ -0,0 +1,313 @@
+use super::{
+    attribute::{
+        AttributeSerializer, SerializeAttributeMap, SerializeAttributeSeq,
+        SerializeAttributeStruct, SerializeAttributeStructVariant, SerializeAttributeTupleVariant,
+    },
+    block::{BlockSerializer, SerializeBlockSeq, SerializeBlockStruct},
+};
+use crate::{serialize_unsupported, Error, Result, Structure};
+use serde::ser::{self, Serialize, SerializeMap};
+
+pub struct StructureSerializer;
+
+impl ser::Serializer for StructureSerializer {
+    type Ok = Structure;
+    type Error = Error;
+
+    type SerializeSeq = SerializeStructureSeq;
+    type SerializeTuple = SerializeStructureSeq;
+    type SerializeTupleStruct = SerializeStructureSeq;
+    type SerializeTupleVariant = SerializeStructureTupleVariant;
+    type SerializeMap = SerializeStructureMap;
+    type SerializeStruct = SerializeStructureStruct;
+    type SerializeStructVariant = SerializeStructureStructVariant;
+
+    serialize_unsupported! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        char str bytes none unit unit_struct unit_variant
+    }
+
+    fn serialize_some<T>(self, value: &T) -> Result<Structure>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Structure>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T>(
+        self,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        // Specialization for the `Structure` type itself.
+        match (name, variant) {
+            ("$hcl::structure", "Block") => Ok(Structure::Block(value.serialize(BlockSerializer)?)),
+            ("$hcl::structure", "Attribute") => {
+                Ok(Structure::Attribute(value.serialize(AttributeSerializer)?))
+            }
+            (_, _) => AttributeSerializer
+                .serialize_newtype_variant(name, variant_index, variant, value)
+                .map(Into::into),
+        }
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(SerializeStructureSeq::new(len))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(SerializeStructureTupleVariant::new(variant, len))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(SerializeStructureMap::new())
+    }
+
+    fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
+        Ok(SerializeStructureStruct::new(name))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(SerializeStructureStructVariant::new(variant, len))
+    }
+}
+
+pub enum SerializeStructureSeq {
+    Attribute(SerializeAttributeSeq),
+    Block(SerializeBlockSeq),
+}
+
+impl SerializeStructureSeq {
+    fn new(len: Option<usize>) -> Self {
+        // Specialization for the `Block` type.
+        if let Some(3) = len {
+            SerializeStructureSeq::Block(SerializeBlockSeq::new())
+        } else {
+            SerializeStructureSeq::Attribute(SerializeAttributeSeq::new())
+        }
+    }
+}
+
+impl ser::SerializeSeq for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureSeq::Attribute(attr) => attr.serialize_element(value),
+            SerializeStructureSeq::Block(block) => block.serialize_element(value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureSeq::Attribute(attr) => attr.end().map(Into::into),
+            SerializeStructureSeq::Block(block) => block.end().map(Into::into),
+        }
+    }
+}
+
+impl ser::SerializeTuple for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+impl ser::SerializeTupleStruct for SerializeStructureSeq {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        ser::SerializeSeq::end(self)
+    }
+}
+
+pub struct SerializeStructureTupleVariant {
+    inner: SerializeAttributeTupleVariant,
+}
+
+impl SerializeStructureTupleVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeStructureTupleVariant {
+            inner: SerializeAttributeTupleVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeTupleVariant for SerializeStructureTupleVariant {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub struct SerializeStructureMap {
+    inner: SerializeAttributeMap,
+}
+
+impl SerializeStructureMap {
+    fn new() -> Self {
+        SerializeStructureMap {
+            inner: SerializeAttributeMap::new(),
+        }
+    }
+}
+
+impl ser::SerializeMap for SerializeStructureMap {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_key(key)
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_value(value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}
+
+pub enum SerializeStructureStruct {
+    Attribute(SerializeAttributeStruct),
+    Block(SerializeBlockStruct),
+    Other(SerializeStructureMap),
+}
+
+impl SerializeStructureStruct {
+    fn new(name: &'static str) -> Self {
+        // Specialization for the `Attribute` and `Block` types.
+        match name {
+            "$hcl::attribute" => {
+                SerializeStructureStruct::Attribute(SerializeAttributeStruct::new())
+            }
+            "$hcl::block" => SerializeStructureStruct::Block(SerializeBlockStruct::new()),
+            _ => SerializeStructureStruct::Other(SerializeStructureMap::new()),
+        }
+    }
+}
+
+impl ser::SerializeStruct for SerializeStructureStruct {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        match self {
+            SerializeStructureStruct::Attribute(ser) => ser.serialize_field(key, value),
+            SerializeStructureStruct::Block(ser) => ser.serialize_field(key, value),
+            SerializeStructureStruct::Other(ser) => ser.serialize_entry(key, value),
+        }
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        match self {
+            SerializeStructureStruct::Attribute(ser) => ser.end().map(Into::into),
+            SerializeStructureStruct::Block(ser) => ser.end().map(Into::into),
+            SerializeStructureStruct::Other(ser) => ser.end(),
+        }
+    }
+}
+
+pub struct SerializeStructureStructVariant {
+    inner: SerializeAttributeStructVariant,
+}
+
+impl SerializeStructureStructVariant {
+    pub fn new(variant: &'static str, len: usize) -> Self {
+        SerializeStructureStructVariant {
+            inner: SerializeAttributeStructVariant::new(variant, len),
+        }
+    }
+}
+
+impl ser::SerializeStructVariant for SerializeStructureStructVariant {
+    type Ok = Structure;
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + ser::Serialize,
+    {
+        self.inner.serialize_field(key, value)
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.inner.end().map(Into::into)
+    }
+}


Test patch:
diff --git a/src/ser/tests.rs b/src/ser/tests.rs
index dab8d6f..b4f4002 100644
--- a/src/ser/tests.rs
+++ b/src/ser/tests.rs
@@ -159,6 +159,8 @@ fn serialize_array() {
         },
         {
             "bar": "baz",
+        },
+        {
             "qux": {
                 "foo": "bar",
                 "baz": "qux"
diff --git a/src/structure/ser/tests.rs b/src/structure/ser/tests.rs
new file mode 100644
index 0000000..344ac74
--- /dev/null
+++ b/src/structure/ser/tests.rs
@@ -0,0 +1,151 @@
+use super::{
+    attribute::AttributeSerializer,
+    block::{BlockLabelSerializer, BlockSerializer},
+    body::BodySerializer,
+    expression::ExpressionSerializer,
+    *,
+};
+use crate::{Attribute, Block, BlockLabel, Body, Expression, Map};
+use serde::Serialize;
+use std::fmt::Debug;
+
+#[track_caller]
+fn test_identity<S, T>(ser: S, value: T)
+where
+    S: ser::Serializer<Ok = T>,
+    T: ser::Serialize + PartialEq + Debug,
+{
+    assert_eq!(value, value.serialize(ser).unwrap());
+}
+
+#[track_caller]
+fn test_serialize<S, G, E>(ser: S, given: G, expected: E)
+where
+    S: ser::Serializer<Ok = E>,
+    G: ser::Serialize,
+    E: PartialEq + Debug,
+{
+    assert_eq!(expected, given.serialize(ser).unwrap());
+}
+
+#[test]
+fn identity() {
+    test_identity(BodySerializer, Body::default());
+    test_identity(
+        BodySerializer,
+        Body::builder()
+            .add_attribute(("foo", "bar"))
+            .add_block(Block::builder("baz").build())
+            .build(),
+    );
+    test_identity(AttributeSerializer, Attribute::new("foo", "bar"));
+    test_identity(
+        AttributeSerializer,
+        Attribute::new("foo", vec!["bar", "baz"]),
+    );
+    test_identity(
+        BlockSerializer,
+        Block::builder("foo")
+            .add_label("bar")
+            .add_attribute(("baz", "qux"))
+            .build(),
+    );
+    test_identity(BlockLabelSerializer, BlockLabel::string("foo"));
+    test_identity(BlockLabelSerializer, BlockLabel::identifier("foo"));
+    test_identity(ExpressionSerializer, Expression::String("bar".into()));
+    test_identity(
+        ExpressionSerializer,
+        Expression::from_iter([("foo", "bar")]),
+    );
+}
+
+#[test]
+fn custom() {
+    #[derive(Serialize)]
+    struct CustomAttr {
+        key: &'static str,
+        #[serde(rename = "expr")]
+        value: &'static str,
+    }
+    test_serialize(
+        AttributeSerializer,
+        CustomAttr {
+            key: "foo",
+            value: "bar",
+        },
+        Attribute::new("foo", "bar"),
+    );
+    test_serialize(
+        AttributeSerializer,
+        ("foo", "bar"),
+        Attribute::new("foo", "bar"),
+    );
+
+    test_serialize(
+        BlockSerializer,
+        {
+            let mut map = Map::new();
+            map.insert("foo", (("bar", "baz"), ("qux", "foo")));
+            map
+        },
+        Block::builder("foo")
+            .add_attribute(("bar", "baz"))
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    #[derive(Serialize)]
+    struct CustomBlock {
+        #[serde(rename = "identifier")]
+        ident: &'static str,
+        body: Map<&'static str, &'static str>,
+    }
+
+    test_serialize(
+        BlockSerializer,
+        CustomBlock {
+            ident: "foo",
+            body: {
+                let mut map = Map::new();
+                map.insert("bar", "baz");
+                map.insert("qux", "foo");
+                map
+            },
+        },
+        Block::builder("foo")
+            .add_attribute(("bar", "baz"))
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    #[derive(Serialize)]
+    struct CustomLabeledBlock {
+        #[serde(rename = "identifier")]
+        ident: &'static str,
+        labels: [&'static str; 2],
+        body: Map<&'static str, &'static str>,
+    }
+
+    test_serialize(
+        BlockSerializer,
+        CustomLabeledBlock {
+            ident: "foo",
+            labels: ["bar", "baz"],
+            body: {
+                let mut map = Map::new();
+                map.insert("qux", "foo");
+                map
+            },
+        },
+        Block::builder("foo")
+            .add_labels(["bar", "baz"])
+            .add_attribute(("qux", "foo"))
+            .build(),
+    );
+
+    test_serialize(
+        ExpressionSerializer,
+        Some(1u8),
+        Expression::Number(1u8.into()),
+    );
+}