1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DeriveInput, parse_macro_input};
4
5mod attributes;
6mod circular;
7mod enums;
8mod helpers;
9mod regular;
10mod unions;
11mod version_tolerant;
12
13use attributes::AttributeFlags;
14use circular::{generate_circular_deserialize, generate_circular_serialize};
15use enums::{
16 generate_enum_deserialize_safe, generate_enum_deserialize_unsafe, generate_enum_serialize,
17 generate_flags_impls, generate_transparent_deserialize, generate_transparent_serialize,
18};
19use helpers::{has_explicit_discriminants, is_single_field_i32};
20use regular::{generate_deserialize, generate_serialize};
21use unions::{generate_union_deserialize, generate_union_serialize};
22use version_tolerant::{
23 generate_version_tolerant_deserialize, generate_version_tolerant_serialize,
24};
25
26#[proc_macro_derive(MemoryPackable, attributes(memorypack, tag))]
27pub fn derive_memorypack(input: TokenStream) -> TokenStream {
28 let input = parse_macro_input!(input as DeriveInput);
29 let name = &input.ident;
30 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
31
32 let attrs = AttributeFlags::parse(&input.attrs);
33
34 let (serialize_impl, deserialize_impl) = match &input.data {
35 Data::Struct(data_struct) if attrs.is_transparent && is_single_field_i32(data_struct) => (
36 generate_transparent_serialize(),
37 generate_transparent_deserialize(),
38 ),
39 Data::Struct(_) if attrs.is_circular => (
40 generate_circular_serialize(&input.data, true),
41 generate_circular_deserialize(&input.data, true),
42 ),
43 Data::Struct(_) if attrs.is_version_tolerant => (
44 generate_version_tolerant_serialize(&input.data),
45 generate_version_tolerant_deserialize(&input.data),
46 ),
47 Data::Struct(_) => (
48 generate_serialize(&input.data),
49 generate_deserialize(&input.data, attrs.is_zero_copy),
50 ),
51 Data::Enum(data_enum) if attrs.is_union => (
52 generate_union_serialize(data_enum),
53 generate_union_deserialize(name, data_enum),
54 ),
55 Data::Enum(data_enum) => {
56 let has_explicit = has_explicit_discriminants(data_enum);
57
58 if !attrs.has_repr_i32 && !has_explicit {
59 return syn::Error::new_spanned(
60 &input,
61 "C-like enums for MemoryPack must have either #[repr(i32)] or explicit discriminants"
62 ).to_compile_error().into();
63 }
64
65 let deserialize = if has_explicit {
66 generate_enum_deserialize_safe(data_enum)
67 } else {
68 generate_enum_deserialize_unsafe()
69 };
70
71 (generate_enum_serialize(), deserialize)
72 }
73 Data::Union(_) => {
74 return syn::Error::new_spanned(
75 &input,
76 "MemoryPackable cannot be derived for Rust unions",
77 )
78 .to_compile_error()
79 .into();
80 }
81 };
82
83 let flags_impl = if attrs.is_flags && attrs.is_transparent {
84 generate_flags_impls(name)
85 } else {
86 quote! {}
87 };
88
89 let zero_copy_impl = if attrs.is_zero_copy {
90 quote! {
91 impl<'a> memorypack::MemoryPackDeserializeZeroCopy<'a> for #name<'a> {
92 #[inline]
93 fn deserialize(reader: &mut memorypack::MemoryPackReader<'a>) -> Result<Self, memorypack::MemoryPackError> {
94 #deserialize_impl
95 }
96 }
97
98 impl<'a> memorypack::MemoryPackDeserialize for #name<'a> {
99 #[inline]
100 fn deserialize(reader: &mut memorypack::MemoryPackReader) -> Result<Self, memorypack::MemoryPackError> {
101 let reader_with_lifetime: &mut memorypack::MemoryPackReader<'a> = unsafe {
102 std::mem::transmute(reader)
103 };
104 <Self as memorypack::MemoryPackDeserializeZeroCopy<'a>>::deserialize(reader_with_lifetime)
105 }
106 }
107 }
108 } else {
109 quote! {}
110 };
111
112 let deserialize_regular_impl = if attrs.is_zero_copy {
113 quote! {}
114 } else {
115 quote! {
116 impl #impl_generics memorypack::MemoryPackDeserialize for #name #ty_generics #where_clause {
117 #[inline]
118 fn deserialize(reader: &mut memorypack::MemoryPackReader) -> Result<Self, memorypack::MemoryPackError> {
119 #deserialize_impl
120 }
121 }
122 }
123 };
124
125 let expanded = quote! {
126 impl #impl_generics memorypack::MemoryPackSerialize for #name #ty_generics #where_clause {
127 #[inline]
128 fn serialize(&self, writer: &mut memorypack::MemoryPackWriter) -> Result<(), memorypack::MemoryPackError> {
129 #serialize_impl
130 Ok(())
131 }
132 }
133
134 #deserialize_regular_impl
135
136 #zero_copy_impl
137
138 #flags_impl
139 };
140
141 expanded.into()
142}