use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields};
use crate::{parser_helper, CatchallType};
pub(crate) fn impl_derive_deserialize_int_map(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
let fields = match input.data {
Data::Struct(data) => match data.fields {
Fields::Named(fields) => fields.named,
_ => panic!("Deserialize_Int_Map applied to invalid struct type"),
},
_ => panic!("Deserialize_Int_Map applied to invalid type"),
};
let quotes_list = fields.iter().map(|field| {
let ident = field.ident.as_ref().expect("No identifier");
let (non_option_ty, is_optional) = parser_helper::get_field_type_and_optionality(field);
let (matcher, catchall_type) = parser_helper::get_field_matcher_and_catchall_type(field);
let attr_placeholder = if catchall_type.is_some() {
quote! {
let mut #ident: #non_option_ty = #non_option_ty::new();
}
} else {
quote! {
let mut #ident: core::option::Option<#non_option_ty> = None;
}
};
let attr_matcher = if catchall_type.is_some() {
quote! {}
} else {
quote! {
#matcher => {
if #ident.is_some() {
return Err(serde::de::Error::duplicate_field(stringify!(#ident)));
}
#ident = Some(map.next_value()?);
}
}
};
let catchall_attr_matcher = match catchall_type {
None => quote! {},
Some(CatchallType::Fields) => {
panic!("TODO: CatchallType::Fields implementation");
}
Some(CatchallType::Unknown) =>
quote! {
if #ident.handles_key(catchall) {
#ident.fill_value(catchall, map.next_value()?);
continue;
}
}
};
let attr_installer = if is_optional || catchall_type.is_some() {
quote! {
#ident,
}
} else {
quote! {
#ident: #ident.ok_or_else(|| serde::de::Error::missing_field(stringify!(#ident)))?,
}
};
(attr_placeholder, attr_matcher, catchall_attr_matcher, attr_installer)
})
.collect::<Vec<(_, _, _, _)>>();
let (attr_placeholders, attr_matchers, catchall_attr_matchers, attr_installers) =
crate::utils::list_to_tuple_4(quotes_list);
let mut attr_matchers = attr_matchers;
attr_matchers.push(quote! {
catchall => {
#(#catchall_attr_matchers)*
return Err(serde::de::Error::custom(format!("Field found with unknown field ID: {}", catchall)));
},
});
let attr_matchers = attr_matchers;
let res = TokenStream::from(quote! {
impl<'de> serde::Deserialize<'de> for #ident {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde_int_map::UnknownKeyHandler;
struct OurVisitor;
impl<'de> serde::de::Visitor<'de> for OurVisitor {
type Value = #ident;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a map")
}
fn visit_map<V>(self, mut map: V) -> core::result::Result<#ident, V::Error>
where
V: serde::de::MapAccess<'de>,
{
#(#attr_placeholders)*
while let Some(_int_map_key) = map.next_key::<u32>()? {
match _int_map_key {
#(#attr_matchers)*
}
}
Ok(#ident {
#(#attr_installers)*
})
}
}
deserializer.deserialize_map(OurVisitor)
}
}
});
#[cfg(feature = "print_tokenstreams")]
println!("Deserialization tokenstream for {}: {}", ident, res);
res
}