Expand description
Serialize a Rust data structure into HCL data.
This module provides the Serializer
type and the convienince functions to_string
,
to_vec
and to_writer
for serializing data to HCL.
Furthermore, the Block
and LabeledBlock
wrapper types, and the
block
, labeled_block
and doubly_labeled_block
functions can be
used to construct HCL block structures from custom types. See the type and function level
documentation for usage examples.
If you want to serialize the data structures provided by this crate (e.g. Body
) consider
using the functionality in the format
module instead because it is more
efficient.
§Supported top-level types
The Serializer
supports serialization to HCL for types that are either structured like
maps or sequences of maps. For example, at the top level a struct with one or more named
fields is supported, while a newtype struct wrapping a primitive type like u8
is not.
Other example of supported top-level types:
- tuple or newtype structs wrapping a map-like type
- enums with newtype or tuple variants wrapping map-like types, or struct variants
Please note that these restrictions only apply to the top-level type that is serialized. Nested fields can have any type that is serializable.
§Serializing a custom type
The following example will serialize the data as a deeply nested HCL attribute.
use serde::Serialize;
#[derive(Serialize)]
struct User {
age: u8,
username: &'static str,
email: &'static str,
}
#[derive(Serialize)]
struct Data {
users: Vec<User>,
}
let data = Data {
users: vec![
User {
age: 34,
username: "johndoe",
email: "johndoe@example.com",
},
User {
age: 27,
username: "janedoe",
email: "janedoe@example.com",
},
],
};
let expected = r#"
users = [
{
"age" = 34
"username" = "johndoe"
"email" = "johndoe@example.com"
},
{
"age" = 27
"username" = "janedoe"
"email" = "janedoe@example.com"
}
]
"#.trim_start();
let serialized = hcl::to_string(&data)?;
assert_eq!(serialized, expected);
§Serializing context-aware HCL
If you need full control over the way data is serialized to HCL, you can make use of the Body
type which can be constructed using the builder pattern.
The following example uses HCL blocks to format the same data from above in a different way.
use hcl::{Block, Body};
let body = Body::builder()
.add_block(
Block::builder("user")
.add_label("johndoe")
.add_attribute(("age", 34))
.add_attribute(("email", "johndoe@example.com"))
.build(),
)
.add_block(
Block::builder("user")
.add_label("janedoe")
.add_attribute(("age", 27))
.add_attribute(("email", "janedoe@example.com"))
.build(),
)
.build();
let expected = r#"
user "johndoe" {
age = 34
email = "johndoe@example.com"
}
user "janedoe" {
age = 27
email = "janedoe@example.com"
}
"#.trim_start();
let serialized = hcl::to_string(&body)?;
assert_eq!(serialized, expected);
The same result could be acheived using the block!
macro:
use serde::Serialize;
#[derive(Serialize)]
struct User {
age: u8,
username: &'static str,
email: &'static str,
}
let users = vec![
User {
age: 34,
username: "johndoe",
email: "johndoe@example.com",
},
User {
age: 27,
username: "janedoe",
email: "janedoe@example.com",
},
];
let body: hcl::Body = users
.into_iter()
.map(|user| {
hcl::block! {
user (user.username) {
age = (user.age)
email = (user.email)
}
}
})
.collect();
let expected = r#"
user "johndoe" {
age = 34
email = "johndoe@example.com"
}
user "janedoe" {
age = 27
email = "janedoe@example.com"
}
"#
.trim_start();
let serialized = hcl::to_string(&body)?;
assert_eq!(serialized, expected);
§Serializing HCL blocks using a custom type
An example to serialize a terraform configuration block using a custom type and the
LabeledBlock
and Block
marker types from this module:
use hcl::expr::{Expression, Traversal, Variable};
use indexmap::{indexmap, IndexMap};
use serde::Serialize;
#[derive(Serialize)]
struct Config {
#[serde(
rename = "resource",
serialize_with = "hcl::ser::labeled_block"
)]
resources: Resources,
}
#[derive(Serialize)]
struct Resources {
#[serde(
rename = "aws_sns_topic_subscription",
serialize_with = "hcl::ser::labeled_block"
)]
aws_sns_topic_subscriptions: IndexMap<String, AwsSnsTopicSubscription>,
}
#[derive(Serialize)]
struct AwsSnsTopicSubscription {
topic_arn: Traversal,
protocol: Expression,
endpoint: Traversal,
}
let subscription = AwsSnsTopicSubscription {
topic_arn: Traversal::builder(Variable::new("aws_sns_topic").unwrap())
.attr("my-topic")
.attr("arn")
.build(),
protocol: "sqs".into(),
endpoint: Traversal::builder(Variable::new("aws_sqs_queue").unwrap())
.attr("my-queue")
.attr("arn")
.build()
};
let config = Config {
resources: Resources {
aws_sns_topic_subscriptions: indexmap! {
"my-subscription".into() => subscription,
},
},
};
let expected = r#"
resource "aws_sns_topic_subscription" "my-subscription" {
topic_arn = aws_sns_topic.my-topic.arn
protocol = "sqs"
endpoint = aws_sqs_queue.my-queue.arn
}
"#.trim_start();
let serialized = hcl::to_string(&config).unwrap();
assert_eq!(serialized, expected);
Structs§
- A transparent wrapper type which hints the
Serializer
to serializeT
as an HCL block. - A transparent wrapper type which hints the
Serializer
to serializeT
as a labeled HCL block. - A structure for serializing Rust values into HCL.
Functions§
- Hints the
Serializer
to serializeT
as an HCL block. - Hints the
Serializer
to serializeT
as an HCL block with two labels. - Hints the
Serializer
to serializeT
as a labeled HCL block. - Serialize the given value as an HCL string.
- Serialize the given value as an HCL byte vector.
- Serialize the given value as HCL into the IO stream.