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