zerocopy_derive/derive/
mod.rs1pub mod from_bytes;
4pub mod into_bytes;
5pub mod known_layout;
6pub mod project;
7pub mod try_from_bytes;
8pub mod unaligned;
9
10use proc_macro2::{Span, TokenStream};
11use quote::quote;
12use syn::{Data, Error};
13
14use crate::{
15 repr::StructUnionRepr,
16 util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
17};
18
19pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
20 Ok(match &ctx.ast.data {
21 Data::Struct(strct) => {
22 ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
23 }
24 Data::Enum(enm) => {
25 ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build()
26 }
27 Data::Union(unn) => {
28 ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
29 }
30 })
31}
32
33pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
34 let type_ident = &ctx.ast.ident;
40 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
41 let where_predicates = where_clause.map(|clause| &clause.predicates);
42 let zerocopy_crate = &ctx.zerocopy_crate;
43 let core = ctx.core_path();
44 Ok(quote! {
45 impl #impl_generics #core::hash::Hash for #type_ident #ty_generics
46 where
47 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
48 #where_predicates
49 {
50 fn hash<H: #core::hash::Hasher>(&self, state: &mut H) {
51 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self))
52 }
53
54 fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) {
55 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data))
56 }
57 }
58 })
59}
60
61pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
62 let type_ident = &ctx.ast.ident;
68 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
69 let where_predicates = where_clause.map(|clause| &clause.predicates);
70 let zerocopy_crate = &ctx.zerocopy_crate;
71 let core = ctx.core_path();
72 Ok(quote! {
73 impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics
74 where
75 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
76 #where_predicates
77 {
78 fn eq(&self, other: &Self) -> bool {
79 #core::cmp::PartialEq::eq(
80 #zerocopy_crate::IntoBytes::as_bytes(self),
81 #zerocopy_crate::IntoBytes::as_bytes(other),
82 )
83 }
84 }
85
86 impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics
87 where
88 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
89 #where_predicates
90 {
91 }
92 })
93}
94
95pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
96 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
97
98 match &ctx.ast.data {
99 Data::Struct(_) => {}
100 Data::Enum(_) | Data::Union(_) => {
101 return ctx
102 .error_or_skip(Error::new(Span::call_site(), "can only be applied to structs"));
103 }
104 };
105
106 if !(repr.is_c() || repr.is_transparent()) {
107 return ctx.error_or_skip(Error::new(
108 Span::call_site(),
109 "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable",
110 ));
111 }
112
113 let fields = ctx.ast.data.fields();
114 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
115 trailing_field
116 } else {
117 return ctx.error_or_skip(Error::new(Span::call_site(), "must at least one field"));
118 };
119
120 let zerocopy_crate = &ctx.zerocopy_crate;
121 let elem = quote!(<#trailing_field as #zerocopy_crate::SplitAt>::Elem);
122 let elem =
123 if repr.get_packed().is_some() { quote!(#zerocopy_crate::Unalign<#elem>) } else { elem };
124
125 Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
141 .inner_extras(quote! {
142 type Elem = #elem;
143 })
144 .build())
145}