use crate::imports::generate_deserialize_imports;
use convert_case::{Case, Casing};
use evenframe_core::derive::validator_parser::parse_field_validators_with_logic;
use quote::quote;
use syn::{Data, DeriveInput, Fields, spanned::Spanned};
pub fn generate_custom_deserialize(input: &DeriveInput) -> proc_macro2::TokenStream {
let struct_name = &input.ident;
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => &fields.named,
Fields::Unnamed(_) => {
return syn::Error::new(
input.span(),
"Custom deserialization is only supported for structs with named fields.\n\nExample:\nstruct MyStruct {\n field1: String,\n field2: i32,\n}"
).to_compile_error();
}
Fields::Unit => {
return syn::Error::new(
input.span(),
"Custom deserialization is not supported for unit structs.\n\nUnit structs have no fields to validate."
).to_compile_error();
}
},
Data::Enum(_) => {
return syn::Error::new(
input.span(),
"Custom deserialization is currently only implemented for structs, not enums.\n\nEnums should use the standard Serde derive."
).to_compile_error();
}
Data::Union(_) => {
return syn::Error::new(
input.span(),
"Custom deserialization is not supported for unions.\n\nUnions are not supported by Evenframe."
).to_compile_error();
}
};
if fields.is_empty() {
return syn::Error::new(
input.span(),
"Cannot generate custom deserialization for struct with no fields.\n\nEmpty structs should use the standard #[derive(Deserialize)]"
).to_compile_error();
}
let field_deserializations = fields.iter().map(|field| {
let field_name = match field.ident.as_ref() {
Some(ident) => ident,
None => {
return syn::Error::new(
field.span(),
"Internal error: Named field should have an identifier",
)
.to_compile_error();
}
};
let field_type = &field.ty;
let enum_variant = quote::format_ident!("{}", field_name.to_string().to_case(Case::Pascal));
let temp_var_name = format!("__temp_{}", field_name);
let (_, validation_logic_tokens) =
match parse_field_validators_with_logic(&field.attrs, &temp_var_name, Some(field_type))
{
Ok(tokens) => tokens,
Err(err) => {
return err.to_compile_error();
}
};
if !validation_logic_tokens.is_empty() {
let temp_var = quote::format_ident!("{}", temp_var_name);
quote! {
Field::#enum_variant => {
if #field_name.is_some() {
return Err(de::Error::duplicate_field(stringify!(#field_name)));
}
let mut #temp_var: #field_type = map.next_value()?;
#(#validation_logic_tokens)*
#field_name = Some(#temp_var);
}
}
} else {
quote! {
Field::#enum_variant => {
if #field_name.is_some() {
return Err(de::Error::duplicate_field(stringify!(#field_name)));
}
#field_name = Some(map.next_value()?);
}
}
}
});
let field_names: Vec<_> = fields.iter().filter_map(|f| f.ident.as_ref()).collect();
if field_names.len() != fields.len() {
return syn::Error::new(
input.span(),
"Internal error: Some fields are missing identifiers after validation",
)
.to_compile_error();
}
let enum_variants: Vec<_> = field_names
.iter()
.map(|name| quote::format_ident!("{}", name.to_string().to_case(Case::Pascal)))
.collect();
let imports = generate_deserialize_imports();
quote! {
const _: () = {
#imports
impl<'de> EvenframeDeserialize<'de> for #struct_name {
fn evenframe_deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
use ::serde::de::{self, Visitor, MapAccess};
use std::fmt;
enum Field {
#(#enum_variants,)*
}
impl<'de> ::serde::Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: ::serde::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("field identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
match value {
#(stringify!(#field_names) => Ok(Field::#enum_variants),)*
_ => Err(de::Error::unknown_field(value, &[#(stringify!(#field_names)),*])),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct StructVisitor;
impl<'de> Visitor<'de> for StructVisitor {
type Value = #struct_name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("struct ", stringify!(#struct_name)))
}
fn visit_map<V>(self, mut map: V) -> Result<#struct_name, V::Error>
where
V: MapAccess<'de>,
{
#(let mut #field_names = None;)*
while let Some(key) = map.next_key()? {
match key {
#(#field_deserializations)*
}
}
#(
let #field_names = #field_names.ok_or_else(|| de::Error::missing_field(stringify!(#field_names)))?;
)*
Ok(#struct_name {
#(#field_names,)*
})
}
}
const FIELDS: &'static [&'static str] = &[#(stringify!(#field_names)),*];
deserializer.deserialize_struct(stringify!(#struct_name), FIELDS, StructVisitor)
}
}
};
impl<'de> ::serde::Deserialize<'de> for #struct_name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
#imports
Self::evenframe_deserialize(deserializer)
}
}
}
}