#[allow(dead_code)]
macro_rules! impl_enum_string_serialization {
($enum_name:ident, $($variant:ident => $str:expr),*) => {
impl serde::Serialize for $enum_name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
$(
$enum_name::$variant => serializer.serialize_str($str),
)*
}
}
}
impl<'de> serde::Deserialize<'de> for $enum_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct EnumVisitor;
impl<'de> serde::de::Visitor<'de> for EnumVisitor {
type Value = $enum_name;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter.write_str(concat!("a string representing a ", stringify!($enum_name)))
}
fn visit_str<E>(self, value: &str) -> Result<$enum_name, E>
where
E: serde::de::Error,
{
match value {
$(
$str => Ok($enum_name::$variant),
)*
_ => Err(serde::de::Error::custom("invalid value for enum")),
}
}
}
deserializer.deserialize_str(EnumVisitor)
}
}
};
}
#[allow(unused)]
pub(crate) use impl_enum_string_serialization;
#[allow(dead_code)]
macro_rules! impl_enum_struct_serialization {
($enum_name:ident, $tag_field:ident, $( $variant:ident($struct:ident, $tag:expr) ),* ) => {
impl serde::Serialize for $enum_name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
$(
$enum_name::$variant(ref inner) => inner.serialize(serializer),
)*
}
}
}
impl<'de> serde::Deserialize<'de> for $enum_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
let tag = value.get(stringify!($tag_field)).and_then(serde_json::Value::as_str)
.ok_or_else(|| serde::de::Error::missing_field(stringify!($tag_field)))?;
match tag {
$(
$tag => serde_json::from_value(value.clone())
.map($enum_name::$variant)
.map_err(serde::de::Error::custom),
)*
_ => Err(serde::de::Error::custom(format!("unknown tag: {}", tag))),
}
}
}
$(
impl From<$struct> for $enum_name {
fn from(item: $struct) -> Self {
$enum_name::$variant(item)
}
}
)*
};
}
#[allow(unused)]
pub(crate) use impl_enum_struct_serialization;
#[allow(dead_code)]
macro_rules! impl_enum_bool_serialization {
($enum_name:ident, $true_variant:ident, $false_variant:ident) => {
impl serde::Serialize for $enum_name {
fn serialize<S>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
| $enum_name::$true_variant => {
serializer.serialize_bool(true)
},
| $enum_name::$false_variant => {
serializer.serialize_bool(false)
},
}
}
}
struct BoolVisitor;
impl<'de> serde::de::Visitor<'de> for BoolVisitor {
type Value = $enum_name;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter.write_str("a boolean")
}
fn visit_bool<E>(
self,
value: bool,
) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(if value {
$enum_name::$true_variant
} else {
$enum_name::$false_variant
})
}
}
impl<'de> serde::Deserialize<'de> for $enum_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_bool(BoolVisitor)
}
}
};
}
#[allow(unused)]
pub(crate) use impl_enum_bool_serialization;
#[allow(dead_code)]
macro_rules! impl_enum_with_string_or_array_serialization {
($enum_name:ident, $single_variant:ident($single_type:ty), $array_variant:ident($array_type:ty)) => {
impl serde::Serialize for $enum_name {
fn serialize<S>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
| $enum_name::$single_variant(ref single) => {
serde::Serialize::serialize(single, serializer)
},
| $enum_name::$array_variant(ref array) => {
let mut seq =
serializer.serialize_seq(Some(array.len()))?;
for element in array {
serde::ser::SerializeSeq::serialize_element(
&mut seq, element,
)?;
}
serde::ser::SerializeSeq::end(seq)
},
}
}
}
impl<'de> serde::Deserialize<'de> for $enum_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct EnumVisitor;
impl<'de> serde::de::Visitor<'de> for EnumVisitor {
type Value = $enum_name;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter.write_str(
"a single element or an array of elements",
)
}
fn visit_str<E>(
self,
value: &str,
) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok($enum_name::$single_variant(
value.to_string(),
))
}
fn visit_seq<A>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut array = Vec::new();
while let Some(element) = seq.next_element()? {
array.push(element);
}
Ok($enum_name::$array_variant(array))
}
}
deserializer.deserialize_any(EnumVisitor)
}
}
impl From<$single_type> for $enum_name {
fn from(item: $single_type) -> Self {
$enum_name::$single_variant(item)
}
}
impl From<Vec<$array_type>> for $enum_name {
fn from(array: Vec<$array_type>) -> Self {
$enum_name::$array_variant(array)
}
}
};
}
#[allow(unused)]
pub(crate) use impl_enum_with_string_or_array_serialization;
#[allow(dead_code)]
macro_rules! impl_display_for_serialize {
($t:ty) => {
impl std::fmt::Display for $t {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let json = serde_json::to_string_pretty(self)
.map_err(|_| std::fmt::Error)?;
write!(f, "{}", json)
}
}
};
}
#[allow(unused)]
pub(crate) use impl_display_for_serialize;
#[cfg(test)]
mod test {
use serde::{Deserialize, Serialize};
use super::*;
#[test]
fn enum_string_serialization() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestEnum {
A,
B,
C,
}
impl_enum_string_serialization!(
TestEnum,
A => "a",
B => "b",
C => "c"
);
let test = TestEnum::A;
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(serialized, "\"a\"");
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
}
#[test]
fn enum_struct_serialization() {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStructA {
tag: String,
value: u32,
}
impl TestStructA {
fn new(value: u32) -> Self {
Self {
tag: "a".to_string(),
value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStructB {
tag: String,
value: u32,
}
impl TestStructB {
fn new(value: u32) -> Self {
Self {
tag: "b".to_string(),
value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStructC {
tag: String,
value: u32,
other_value: u32,
}
impl TestStructC {
fn new(
value: u32,
other_value: u32,
) -> Self {
Self {
tag: "c".to_string(),
value,
other_value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum TestEnum {
A(TestStructA),
B(TestStructB),
C(TestStructC),
}
impl_enum_struct_serialization!(
TestEnum,
tag,
A(TestStructA, "a"),
B(TestStructB, "b"),
C(TestStructC, "c")
);
let test = TestEnum::A(TestStructA::new(42));
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(
serialized,
"{\"tag\":\"a\",\"value\":42}"
);
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
let test = TestEnum::B(TestStructB::new(42));
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(
serialized,
"{\"tag\":\"b\",\"value\":42}"
);
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
let test = TestEnum::C(TestStructC::new(42, 44));
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(
serialized,
"{\"tag\":\"c\",\"value\":42,\"other_value\":44}"
);
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
}
#[test]
fn enum_bool_serialization() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestEnum {
A, B, }
impl_enum_bool_serialization!(TestEnum, A, B);
let serialized = serde_json::to_string(&TestEnum::A).unwrap();
assert_eq!(serialized, "true");
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, TestEnum::A);
let serialized = serde_json::to_string(&TestEnum::B).unwrap();
assert_eq!(serialized, "false");
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, TestEnum::B);
}
#[test]
fn enum_struct_or_array_serialization() {
#[derive(Debug, Clone, PartialEq)]
enum TestEnum {
Single(String),
Array(Vec<TestStruct>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStruct {
value: u32,
}
impl_enum_with_string_or_array_serialization!(
TestEnum,
Single(String),
Array(TestStruct)
);
let test = TestEnum::Single("42".to_string());
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(serialized, "\"42\"");
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
let test = TestEnum::Array(vec![TestStruct {
value: 42,
}]);
let serialized = serde_json::to_string(&test).unwrap();
assert_eq!(serialized, "[{\"value\":42}]");
let deserialized: TestEnum = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, test);
}
#[test]
fn display_for_serialize() {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct TestStruct {
value: u32,
}
impl_display_for_serialize!(TestStruct);
let test = TestStruct {
value: 42,
};
assert_eq!(
test.to_string(),
"{\n \"value\": 42\n}"
);
}
}