1use proc_macro::TokenStream; 2use quote::{format_ident, quote}; 3use syn::{Field, ItemStruct}; 4 5pub fn deserialize_default_on_error(input: TokenStream) -> TokenStream { 6 let Ok(input) = syn::parse::<ItemStruct>(input) else { 7 panic!("macro can only be applied to struct definitions"); 8 }; 9 10 assert!( 11 input.generics.params.is_empty(), 12 "macro does not support structs with generic parameters" 13 ); 14 15 let ItemStruct { attrs: struct_attrs, vis: struct_vis, ident: struct_ident, fields, .. } = 16 &input; 17 18 let mut deserialize_fn_definitions = Vec::new(); 19 let mut new_fields = Vec::new(); 20 21 for field in fields { 22 let Some(field_ident) = &field.ident else { 23 panic!("macro does not support structs with unnamed fields"); 24 }; 25 26 let Field { attrs: field_attrs, vis: field_vis, ty: field_ty, .. } = &field; 27 28 let deserialize_fn_ident = 29 format_ident!("__deserialize_{struct_ident}_{field_ident}_default_on_error"); 30 31 let cfg_attrs: Vec<_> = 32 field_attrs.iter().filter(|attr| attr.path().is_ident("cfg")).collect(); 33 deserialize_fn_definitions.push(quote! { 34 #(#cfg_attrs)* 35 fn #deserialize_fn_ident<'de, D>(deserializer: D) -> ::std::result::Result<#field_ty, D::Error> 36 where 37 D: ::serde::Deserializer<'de>, 38 { 39 let value = <#field_ty as ::serde::Deserialize<'de>>::deserialize(deserializer) 40 .unwrap_or_else(|err| { 41 ::log::error!("error deserializing field '{}': {err}", ::std::stringify!(#field_ident)); 42 <#struct_ident as ::std::default::Default>::default().#field_ident 43 }); 44 45 ::std::result::Result::Ok(value) 46 } 47 }); 48 49 let deserialize_fn_ident_str = deserialize_fn_ident.to_string(); 50 new_fields.push(quote! { 51 #(#field_attrs)* 52 #[serde(deserialize_with = #deserialize_fn_ident_str)] 53 #field_vis #field_ident: #field_ty, 54 }); 55 } 56 57 quote! { 58 #(#deserialize_fn_definitions)* 59 60 #(#struct_attrs)* 61 #struct_vis struct #struct_ident { 62 #(#new_fields)* 63 } 64 } 65 .into() 66}