use {
crate::common::{get_crate_name, SchemaArgs, StructRepr},
darling::{ast::Data, Error, Result},
proc_macro2::TokenStream,
quote::quote,
std::borrow::Cow,
syn::parse_quote,
};
pub(crate) fn assert_zero_copy(args: &SchemaArgs, repr: &StructRepr) -> Result<TokenStream> {
let Some(config) = &args.assert_zero_copy else {
return Ok(quote! {});
};
let crate_name = get_crate_name(args);
let ident = &args.ident;
let config_path = config
.0
.as_ref()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(parse_quote!(#crate_name::config::DefaultConfig)));
let zero_copy_field_asserts = match &args.data {
Data::Struct(fields) => fields.iter().map(|field| {
let target = field.target_resolved();
quote! { assert_field_zerocopy_impl::<#target>() }
}),
_ => return Err(Error::custom("`ZeroCopy` can only be derived for structs")),
};
if !repr.is_zero_copy_eligible() {
return Err(Error::custom(
"The struct representation is not eligible for zero-copy. Consider using \
#[repr(transparent)] or #[repr(C)] on the struct.",
));
}
Ok(quote! {
const _: () = {
use #crate_name::{config::ZeroCopy, SchemaRead, TypeMeta};
const _assert_schema_read_impl: fn() = || {
fn assert_schema_read_impl<'de, T: SchemaRead<'de, #config_path>>() {}
assert_schema_read_impl::<#ident>()
};
const _assert_struct_zerocopy_impl: fn() = || {
fn assert_struct_zerocopy_impl<T: ZeroCopy<#config_path>>() {}
assert_struct_zerocopy_impl::<#ident>()
};
const _assert_field_zerocopy_impl: fn() = || {
fn assert_field_zerocopy_impl<T: ZeroCopy<#config_path>>() {}
#(#zero_copy_field_asserts);*
};
const _assert_no_padding: () = {
if let TypeMeta::Static { size, .. } = <#ident as SchemaRead<'_, #config_path>>::TYPE_META {
if size != core::mem::size_of::<#ident>() {
panic!("wincode(assert_zero_copy) was applied to a type with padding");
}
} else {
panic!("wincode(assert_zero_copy) was applied to a type with `TypeMeta::Dynamic`");
}
};
};
})
}