1use proc_macro2::TokenStream as TokenStream2;
6use quote::quote;
7
8use crate::utils::{self, FieldInfo};
9use syn::spanned::Spanned;
10use syn::{Data, DeriveInput, Error};
11
12pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
13 if !utils::ReprInfo::compute(&input.attrs).cpacked_or_transparent() {
14 return Error::new(
15 input.span(),
16 "derive(ULE) must be applied to a #[repr(C, packed)] or #[repr(transparent)] type",
17 )
18 .to_compile_error();
19 }
20 if input.generics.type_params().next().is_some()
21 || input.generics.lifetimes().next().is_some()
22 || input.generics.const_params().next().is_some()
23 {
24 return Error::new(
25 input.generics.span(),
26 "derive(ULE) must be applied to a struct without any generics",
27 )
28 .to_compile_error();
29 }
30 let struc = if let Data::Struct(ref s) = input.data {
31 if s.fields.iter().next().is_none() {
32 return Error::new(
33 input.span(),
34 "derive(ULE) must be applied to a non-empty struct",
35 )
36 .to_compile_error();
37 }
38 s
39 } else {
40 return Error::new(input.span(), "derive(ULE) must be applied to a struct")
41 .to_compile_error();
42 };
43
44 let fields = FieldInfo::make_list(struc.fields.iter());
45 let (validators, remaining_offset) = generate_ule_validators(&fields);
46
47 let name = &input.ident;
48
49 quote! {
59 unsafe impl zerovec::ule::ULE for #name {
60 #[inline]
61 fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> {
62 const SIZE: usize = ::core::mem::size_of::<#name>();
63 #[allow(clippy::modulo_one)]
64 if bytes.len() % SIZE != 0 {
65 return Err(zerovec::ZeroVecError::length::<Self>(bytes.len()));
66 }
67 #[allow(clippy::indexing_slicing)] for chunk in bytes.chunks_exact(SIZE) {
70 #validators
71 debug_assert_eq!(#remaining_offset, SIZE);
72 }
73 Ok(())
74 }
75 }
76 }
77}
78
79pub(crate) fn generate_ule_validators(
83 fields: &[FieldInfo],
84 ) -> (TokenStream2, syn::Ident) {
86 utils::generate_per_field_offsets(fields, false, |field, prev_offset_ident, size_ident| {
87 let ty = &field.field.ty;
88 quote! {
89 #[allow(clippy::indexing_slicing)] <#ty as zerovec::ule::ULE>::validate_byte_slice(&bytes[#prev_offset_ident .. #prev_offset_ident + #size_ident])?;
91 }
92 })
93}
94
95pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
97 fields
98 .iter()
99 .map(|f| {
100 let ty = &f.field.ty;
101 let ty = quote!(<#ty as zerovec::ule::AsULE>::ULE);
102 let setter = f.setter();
103 let vis = &f.field.vis;
104 quote!(#vis #setter #ty)
105 })
106 .collect::<Vec<_>>()
107}