zerocopy_derive/derive/
mod.rs1pub mod from_bytes;
2pub mod into_bytes;
3pub mod known_layout;
4pub mod try_from_bytes;
5pub mod unaligned;
6
7use proc_macro2::{Span, TokenStream};
8use quote::quote;
9use syn::{Data, Error};
10
11use crate::{
12 repr::StructUnionRepr,
13 util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
14};
15
16pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream {
17 match &ctx.ast.data {
18 Data::Struct(strct) => {
19 ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
20 }
21 Data::Enum(enm) => {
22 ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build()
23 }
24 Data::Union(unn) => {
25 ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
26 }
27 }
28}
29
30pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
31 let type_ident = &ctx.ast.ident;
37 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
38 let where_predicates = where_clause.map(|clause| &clause.predicates);
39 let zerocopy_crate = &ctx.zerocopy_crate;
40 let core = ctx.core_path();
41 Ok(quote! {
42 impl #impl_generics #core::hash::Hash for #type_ident #ty_generics
43 where
44 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
45 #where_predicates
46 {
47 fn hash<H: #core::hash::Hasher>(&self, state: &mut H) {
48 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self))
49 }
50
51 fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) {
52 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data))
53 }
54 }
55 })
56}
57
58pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
59 let type_ident = &ctx.ast.ident;
65 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
66 let where_predicates = where_clause.map(|clause| &clause.predicates);
67 let zerocopy_crate = &ctx.zerocopy_crate;
68 let core = ctx.core_path();
69 Ok(quote! {
70 impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics
71 where
72 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
73 #where_predicates
74 {
75 fn eq(&self, other: &Self) -> bool {
76 #core::cmp::PartialEq::eq(
77 #zerocopy_crate::IntoBytes::as_bytes(self),
78 #zerocopy_crate::IntoBytes::as_bytes(other),
79 )
80 }
81 }
82
83 impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics
84 where
85 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
86 #where_predicates
87 {
88 }
89 })
90}
91
92pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
93 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
94
95 match &ctx.ast.data {
96 Data::Struct(_) => {}
97 Data::Enum(_) | Data::Union(_) => {
98 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
99 }
100 };
101
102 if repr.get_packed().is_some() {
103 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
104 }
105
106 if !(repr.is_c() || repr.is_transparent()) {
107 return Err(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 Err(Error::new(Span::call_site(), "must at least one field"));
118 };
119
120 let zerocopy_crate = &ctx.zerocopy_crate;
121 Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
126 .inner_extras(quote! {
127 type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem;
128 })
129 .build())
130}