use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::utils::{self, FieldInfo};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Error};
pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
if !utils::ReprInfo::compute(&input.attrs).cpacked_or_transparent() {
return Error::new(
input.span(),
"derive(ULE) must be applied to a #[repr(C, packed)] or #[repr(transparent)] type",
)
.to_compile_error();
}
if input.generics.type_params().next().is_some()
|| input.generics.lifetimes().next().is_some()
|| input.generics.const_params().next().is_some()
{
return Error::new(
input.generics.span(),
"derive(ULE) must be applied to a struct without any generics",
)
.to_compile_error();
}
let struc = if let Data::Struct(ref s) = input.data {
if s.fields.iter().next().is_none() {
return Error::new(
input.span(),
"derive(ULE) must be applied to a non-empty struct",
)
.to_compile_error();
}
s
} else {
return Error::new(input.span(), "derive(ULE) must be applied to a struct")
.to_compile_error();
};
let fields = FieldInfo::make_list(struc.fields.iter());
let (validators, remaining_offset) = generate_ule_validators(&fields);
let name = &input.ident;
quote! {
unsafe impl zerovec::ule::ULE for #name {
#[inline]
fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
const SIZE: usize = ::core::mem::size_of::<#name>();
#[expect(clippy::modulo_one)]
if bytes.len() % SIZE != 0 {
return Err(zerovec::ule::UleError::length::<Self>(bytes.len()));
}
#[expect(clippy::indexing_slicing)] for chunk in bytes.chunks_exact(SIZE) {
#validators
debug_assert_eq!(#remaining_offset, SIZE);
}
Ok(())
}
}
}
}
pub(crate) fn generate_ule_validators(
fields: &[FieldInfo],
) -> (TokenStream2, syn::Ident) {
utils::generate_per_field_offsets(fields, false, |field, prev_offset_ident, size_ident| {
let ty = &field.field.ty;
quote! {
if let Some(bytes) = bytes.get(#prev_offset_ident .. #prev_offset_ident + #size_ident) {
<#ty as zerovec::ule::ULE>::validate_bytes(bytes)?;
} else {
return Err(zerovec::ule::UleError::parse::<Self>());
}
}
})
}
pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
fields
.iter()
.map(|f| {
let ty = &f.field.ty;
let ty = quote!(<#ty as zerovec::ule::AsULE>::ULE);
let setter = f.setter();
let vis = &f.field.vis;
quote!(#vis #setter #ty)
})
.collect::<Vec<_>>()
}