use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::DeriveInput;
use crate::{
shared::{
meta_struct, utils,
z_struct::{self, analyze_struct_fields},
zero_copy_new::{generate_config_struct, generate_init_mut_impl},
},
zero_copy,
};
pub fn derive_zero_copy_mut_impl(fn_input: TokenStream) -> syn::Result<proc_macro2::TokenStream> {
let input: DeriveInput = syn::parse(fn_input)?;
utils::validate_repr_c_required(&input.attrs, "ZeroCopyMut")?;
let hasher = false;
let (name, z_struct_name, z_struct_meta_name, fields) = utils::process_input(&input)?;
let Some(fields) = fields else {
let z_struct_name_mut = format_ident!("{}Mut", z_struct_name);
let zero_copy_struct_inner_impl_mut =
zero_copy::generate_zero_copy_struct_inner::<true>(name, &z_struct_name_mut)?;
let z_struct_def_mut = quote::quote! {
pub type #z_struct_name_mut<'a> = &'a mut #name;
};
let deserialize_impl_mut = quote::quote! {
impl<'a> ::light_zero_copy::traits::ZeroCopyAtMut<'a> for #name {
type ZeroCopyAtMut = #z_struct_name_mut<'a>;
fn zero_copy_at_mut(bytes: &'a mut [u8]) -> ::core::result::Result<(Self::ZeroCopyAtMut, &'a mut [u8]), ::light_zero_copy::errors::ZeroCopyError> {
let unit: &'a mut #name = Box::leak(Box::new(#name));
Ok((unit, bytes))
}
}
};
let config_name = quote::format_ident!("{}Config", name);
let config_struct = quote::quote! {
pub type #config_name = ();
};
let init_mut_impl = quote::quote! {
impl<'a> ::light_zero_copy::traits::ZeroCopyNew<'a> for #name {
type ZeroCopyConfig = #config_name;
type Output = #z_struct_name_mut<'a>;
fn byte_len(_config: &Self::ZeroCopyConfig) -> Result<usize, ::light_zero_copy::errors::ZeroCopyError> {
Ok(0)
}
fn new_zero_copy(
bytes: &'a mut [u8],
_config: Self::ZeroCopyConfig,
) -> Result<(Self::Output, &'a mut [u8]), ::light_zero_copy::errors::ZeroCopyError> {
let unit: &'a mut #name = Box::leak(Box::new(#name));
Ok((unit, bytes))
}
}
};
return Ok(quote::quote! {
#config_struct
#z_struct_def_mut
const _: () = {
#zero_copy_struct_inner_impl_mut
#deserialize_impl_mut
#init_mut_impl
};
});
};
let (meta_fields, struct_fields) = utils::process_fields(fields);
let meta_struct_def_mut = if !meta_fields.is_empty() {
meta_struct::generate_meta_struct::<true>(&z_struct_meta_name, &meta_fields, hasher)?
} else {
quote! {}
};
let z_struct_def_mut = z_struct::generate_z_struct::<true>(
&z_struct_name,
&z_struct_meta_name,
&struct_fields,
&meta_fields,
hasher,
)?;
let zero_copy_struct_inner_impl_mut = zero_copy::generate_zero_copy_struct_inner::<true>(
name,
&format_ident!("{}Mut", z_struct_name),
)?;
let deserialize_impl_mut = zero_copy::generate_deserialize_impl::<true>(
name,
&z_struct_name,
&z_struct_meta_name,
&struct_fields,
meta_fields.is_empty(),
quote! {},
)?;
let struct_field_types = analyze_struct_fields(&struct_fields)?;
let field_strategies: Vec<_> = struct_field_types
.iter()
.map(crate::shared::zero_copy_new::analyze_field_strategy)
.collect();
let has_dynamic_fields = !field_strategies.iter().all(|strategy| {
matches!(
strategy,
crate::shared::zero_copy_new::FieldStrategy::FixedSize
)
});
let (config_struct, init_mut_impl) = if has_dynamic_fields {
let config = generate_config_struct(name, &struct_field_types)?;
let init_impl = generate_init_mut_impl(name, &meta_fields, &struct_fields)?;
(config, Some(init_impl))
} else {
let config_name = quote::format_ident!("{}Config", name);
let unit_config = Some(quote! {
pub type #config_name = ();
});
let init_impl = generate_init_mut_impl(name, &meta_fields, &struct_fields)?;
(unit_config, Some(init_impl))
};
let expanded = quote! {
#config_struct
#meta_struct_def_mut
#z_struct_def_mut
const _: () = {
#[allow(unused_imports)]
use ::core::{mem::size_of, ops::Deref};
#[allow(unused_imports)]
use ::light_zero_copy::{
errors::ZeroCopyError,
slice_mut::ZeroCopySliceMutBorsh,
};
#[allow(unused_imports)]
use ::zerocopy::{
little_endian::{U16, U32, U64},
FromBytes, Immutable, IntoBytes, KnownLayout, Ref, Unaligned,
};
#zero_copy_struct_inner_impl_mut
#deserialize_impl_mut
#init_mut_impl
};
};
Ok(expanded)
}