memorypack_derive/
lib.rs

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}