use crate::{attr, bound};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
parse_quote, Data, DataEnum, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Result,
};
pub fn derive(input: DeriveInput) -> Result<TokenStream> {
match &input.data {
Data::Struct(data) => match attr::struct_kind(&input.attrs, data)? {
attr::StructKind::Transparent(fields) => derive_transparent_struct(&input, fields),
attr::StructKind::Named(fields) => derive_struct(&input, fields),
},
Data::Enum(enumeration) => derive_enum(&input, enumeration),
_ => Err(Error::new(Span::call_site(), "unsupported derive input")),
}
}
fn derive_transparent_struct(input: &DeriveInput, _fields: &FieldsUnnamed) -> Result<TokenStream> {
let ident = &input.ident;
let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
let bound = parse_quote!(microserde::Serialize);
let bounded_where_clause = bound::where_clause_with_bound(&input.generics, bound);
Ok(quote! {
impl #impl_generics microserde::Serialize for #ident #ty_generics #bounded_where_clause {
fn begin(&self) -> microserde::ser::Fragment {
self.0.begin()
}
}
})
}
fn derive_struct(input: &DeriveInput, fields: &FieldsNamed) -> Result<TokenStream> {
let ident = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let fieldname = &fields.named.iter().map(|f| &f.ident).collect::<Vec<_>>();
let fieldstr = fields
.named
.iter()
.map(attr::name_of_field)
.collect::<Result<Vec<_>>>()?;
let index = 0usize..;
let wrapper_generics = bound::with_lifetime_bound(&input.generics, "'__a");
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
let bound = parse_quote!(microserde::Serialize);
let bounded_where_clause = bound::where_clause_with_bound(&input.generics, bound);
Ok(quote! {
#[allow(non_local_definitions)]
const _: () = {
impl #impl_generics microserde::Serialize for #ident #ty_generics #bounded_where_clause {
fn begin(&self) -> microserde::ser::Fragment {
microserde::ser::Fragment::Map(microserde::export::Box::new(__Map {
data: self,
state: 0,
}))
}
}
struct __Map #wrapper_impl_generics #where_clause {
data: &'__a #ident #ty_generics,
state: microserde::export::usize,
}
impl #wrapper_impl_generics microserde::ser::Map for __Map #wrapper_ty_generics #bounded_where_clause {
fn next(&mut self) -> microserde::export::Option<(microserde::export::Cow<microserde::export::str>, &dyn microserde::Serialize)> {
let __state = self.state;
self.state = __state + 1;
match __state {
#(
#index => microserde::export::Some((
microserde::export::Cow::Borrowed(#fieldstr),
&self.data.#fieldname,
)),
)*
_ => microserde::export::None,
}
}
}
};
})
}
fn derive_enum(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream> {
if input.generics.lt_token.is_some() || input.generics.where_clause.is_some() {
return Err(Error::new(
Span::call_site(),
"Enums with generics are not supported",
));
}
let ident = &input.ident;
let var_idents = enumeration
.variants
.iter()
.map(|variant| match variant.fields {
Fields::Unit => Ok(&variant.ident),
_ => Err(Error::new_spanned(
variant,
"Invalid variant: only simple enum variants without fields are supported",
)),
})
.collect::<Result<Vec<_>>>()?;
let names = enumeration
.variants
.iter()
.map(attr::name_of_variant)
.collect::<Result<Vec<_>>>()?;
Ok(quote! {
#[allow(non_local_definitions)]
const _: () = {
impl microserde::Serialize for #ident {
fn begin(&self) -> microserde::ser::Fragment {
match self {
#(
#ident::#var_idents => {
microserde::ser::Fragment::Str(microserde::export::Cow::Borrowed(#names))
}
)*
}
}
}
};
})
}