use proc_macro::{Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Ident, LitStr};
#[proc_macro_derive(Serialize_unit_struct)]
pub fn serialize_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let name_str = LitStr::new(&name.to_string(), name.span());
let serialize_impl = quote! {
impl serde::Serialize for #name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serde::Serialize::serialize(#name_str, serializer)
}
}
};
TokenStream::from(serialize_impl)
}
#[proc_macro_derive(Deserialize_unit_struct, attributes(serde_unit_struct))]
pub fn deserialize_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let name_str = LitStr::new(&name.to_string(), name.span());
let mut aliases: Vec<LitStr> = Vec::new();
for attr in &input.attrs {
if attr.path().is_ident("serde_unit_struct") {
if let Err(e) = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("alias") {
let value: LitStr = meta.value()?.parse()?;
aliases.push(value);
Ok(())
} else {
Err(meta.error("unknown serde_unit_struct attribute"))
}
}) {
return TokenStream::from(e.to_compile_error());
}
}
}
let error_msg = LitStr::new(
&format!("expected unit struct {}", name),
Span::call_site().into(),
);
let visitor = Ident::new(
&format!("SerdeUnitStructDerive{}Visitor", name),
Span::call_site().into(),
);
let value_check = quote! {
value == #name_str #(|| value == #aliases)*
};
let deserialize_impl = quote! {
struct #visitor;
impl<'de> serde::de::Visitor<'de> for #visitor {
type Value = #name;
fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
fmt.write_str(#error_msg)
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if #value_check {
Ok(#name)
} else {
Err(E::custom(#error_msg))
}
}
}
impl<'de> serde::Deserialize<'de> for #name {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(#visitor)
}
}
};
TokenStream::from(deserialize_impl)
}