Skip to main content

bincode_derive_next/
lib.rs

1//! Procedural macros for the bincode-next serialization library.
2//!
3//! This crate provides derive macros for `Encode`, `Decode`, `BorrowDecode`, `BitPacked`, `ZeroCopy`, `Fingerprint`, and `StaticSize`.
4
5#![allow(dead_code)]
6#![allow(unused_must_use)]
7
8mod attribute;
9mod derive_bit_packed;
10mod derive_enum;
11mod derive_fingerprint;
12mod derive_static_size;
13mod derive_struct;
14mod derive_zerocopy;
15
16use attribute::ContainerAttributes;
17use virtue::prelude::AttributeAccess;
18use virtue::prelude::Body;
19#[allow(unused_imports)]
20use virtue::prelude::Error;
21use virtue::prelude::Parse;
22use virtue::prelude::Result;
23use virtue::prelude::TokenStream;
24
25#[proc_macro_derive(Encode, attributes(bincode))]
26pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
27    derive_encode_inner(input).unwrap_or_else(|e| e.into_token_stream())
28}
29
30/// # Errors
31///
32/// Returns an error if the input cannot be parsed or if the code generation fails.
33fn derive_encode_inner(input: TokenStream) -> Result<TokenStream> {
34    let parse = Parse::new(input)?;
35    let (mut generator, attributes, body) = parse.into_generator();
36    let attributes = attributes
37        .get_attribute::<ContainerAttributes>()?
38        .unwrap_or_default();
39
40    match body {
41        | Body::Struct(body) => {
42            derive_struct::DeriveStruct {
43                fields: body.fields,
44                attributes,
45            }
46            .generate_encode(&mut generator)?;
47        },
48        | Body::Enum(body) => {
49            derive_enum::DeriveEnum {
50                variants: body.variants,
51                attributes,
52            }
53            .generate_encode(&mut generator)?;
54        },
55    }
56
57    generator.export_to_file("bincode_next", "Encode");
58    generator.finish()
59}
60
61#[proc_macro_derive(Decode, attributes(bincode))]
62pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
63    derive_decode_inner(input).unwrap_or_else(|e| e.into_token_stream())
64}
65
66/// # Errors
67///
68/// Returns an error if the input cannot be parsed or if the code generation fails.
69fn derive_decode_inner(input: TokenStream) -> Result<TokenStream> {
70    let parse = Parse::new(input)?;
71    let (mut generator, attributes, body) = parse.into_generator();
72    let attributes = attributes
73        .get_attribute::<ContainerAttributes>()?
74        .unwrap_or_default();
75
76    match body {
77        | Body::Struct(body) => {
78            derive_struct::DeriveStruct {
79                fields: body.fields,
80                attributes,
81            }
82            .generate_decode(&mut generator)?;
83        },
84        | Body::Enum(body) => {
85            derive_enum::DeriveEnum {
86                variants: body.variants,
87                attributes,
88            }
89            .generate_decode(&mut generator)?;
90        },
91    }
92
93    generator.export_to_file("bincode_next", "Decode");
94    generator.finish()
95}
96
97#[proc_macro_derive(BorrowDecode, attributes(bincode))]
98pub fn derive_borrow_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
99    derive_borrow_decode_inner(input).unwrap_or_else(|e| e.into_token_stream())
100}
101
102/// # Errors
103///
104/// Returns an error if the input cannot be parsed or if the code generation fails.
105fn derive_borrow_decode_inner(input: TokenStream) -> Result<TokenStream> {
106    let parse = Parse::new(input)?;
107    let (mut generator, attributes, body) = parse.into_generator();
108    let attributes = attributes
109        .get_attribute::<ContainerAttributes>()?
110        .unwrap_or_default();
111
112    match body {
113        | Body::Struct(body) => {
114            derive_struct::DeriveStruct {
115                fields: body.fields,
116                attributes,
117            }
118            .generate_borrow_decode(&mut generator)?;
119        },
120        | Body::Enum(body) => {
121            derive_enum::DeriveEnum {
122                variants: body.variants,
123                attributes,
124            }
125            .generate_borrow_decode(&mut generator)?;
126        },
127    }
128
129    generator.export_to_file("bincode_next", "BorrowDecode");
130    generator.finish()
131}
132
133#[proc_macro_derive(BitPacked, attributes(bincode))]
134pub fn derive_bit_packed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
135    derive_bit_packed_inner(input).unwrap_or_else(|e| e.into_token_stream())
136}
137
138/// # Errors
139///
140/// Returns an error if the input cannot be parsed or if the code generation fails.
141fn derive_bit_packed_inner(input: TokenStream) -> Result<TokenStream> {
142    let parse = Parse::new(input)?;
143    let (mut generator, attributes, body) = parse.into_generator();
144    let attributes = attributes
145        .get_attribute::<ContainerAttributes>()?
146        .unwrap_or_default();
147
148    match body {
149        | Body::Struct(body) => {
150            derive_bit_packed::DeriveBitPacked {
151                fields: body.fields,
152                attributes,
153            }
154            .generate(&mut generator)?;
155        },
156        | Body::Enum(body) => {
157            derive_bit_packed::DeriveBitPackedEnum {
158                variants: body.variants,
159                attributes,
160            }
161            .generate(&mut generator)?;
162        },
163    }
164
165    generator.export_to_file("bincode_next", "BitPacked");
166    generator.finish()
167}
168
169#[cfg(feature = "zero-copy")]
170#[proc_macro_derive(ZeroCopy, attributes(bincode))]
171pub fn derive_zerocopy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
172    derive_zerocopy_inner(input).unwrap_or_else(|e| e.into_token_stream())
173}
174
175#[cfg(feature = "zero-copy")]
176fn derive_zerocopy_inner(input: TokenStream) -> Result<TokenStream> {
177    let parse = Parse::new(input)?;
178
179    let visibility = match &parse {
180        | Parse::Struct { visibility, .. } => visibility.clone(),
181        | Parse::Enum { visibility, .. } => visibility.clone(),
182        | _ => unreachable!(),
183    };
184    let (mut generator, attributes, body) = parse.into_generator();
185    let repr = attributes
186        .get_attribute::<attribute::ReprAttributes>()?
187        .unwrap_or_default();
188
189    let attributes = attributes
190        .get_attribute::<ContainerAttributes>()?
191        .unwrap_or_default();
192
193    match body {
194        | Body::Struct(body) => {
195            if !repr.is_c && !repr.is_transparent {
196                return Err(Error::custom(
197                    "ZeroCopy structs must have #[repr(C)] or #[repr(transparent)]",
198                ));
199            }
200            derive_zerocopy::DeriveZeroCopy {
201                fields: body.fields,
202                variants: None,
203                attributes,
204                repr,
205                visibility,
206            }
207            .generate(&mut generator)?;
208        },
209        | Body::Enum(body) => {
210            if !repr.is_c
211                && !repr.is_u8
212                && !repr.is_u16
213                && !repr.is_u32
214                && !repr.is_u64
215                && !repr.is_i8
216                && !repr.is_i16
217                && !repr.is_i32
218                && !repr.is_i64
219            {
220                return Err(Error::custom(
221                    "ZeroCopy enums must have #[repr(C)] or a primitive repr like #[repr(u8)]",
222                ));
223            }
224            derive_zerocopy::DeriveZeroCopy {
225                fields: None,
226                variants: Some(body.variants),
227                attributes,
228                repr,
229                visibility,
230            }
231            .generate(&mut generator)?;
232        },
233    }
234
235    generator.export_to_file("bincode_next", "ZeroCopy");
236    generator.finish()
237}
238
239#[cfg(feature = "static-size")]
240#[proc_macro_derive(StaticSize, attributes(bincode, static_size))]
241pub fn derive_static_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
242    derive_static_size_inner(input).unwrap_or_else(|e| e.into_token_stream())
243}
244
245#[cfg(feature = "static-size")]
246fn derive_static_size_inner(input: TokenStream) -> Result<TokenStream> {
247    let parse = Parse::new(input)?;
248    let (mut generator, attributes, body) = parse.into_generator();
249    let attributes = attributes
250        .get_attribute::<ContainerAttributes>()?
251        .unwrap_or_default();
252
253    match body {
254        | Body::Struct(body) => {
255            derive_static_size::DeriveStaticSize {
256                fields: body.fields,
257                variants: None,
258                attributes,
259            }
260            .generate(&mut generator)?;
261        },
262        | Body::Enum(body) => {
263            derive_static_size::DeriveStaticSize {
264                fields: None,
265                variants: Some(body.variants),
266                attributes,
267            }
268            .generate(&mut generator)?;
269        },
270    }
271
272    generator.export_to_file("bincode_next", "StaticSize");
273    generator.finish()
274}
275
276#[proc_macro_derive(Fingerprint, attributes(bincode))]
277pub fn derive_fingerprint(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
278    derive_fingerprint_inner(input).unwrap_or_else(|e| e.into_token_stream())
279}
280
281fn derive_fingerprint_inner(input: TokenStream) -> Result<TokenStream> {
282    let parse = Parse::new(input)?;
283    let type_name = match &parse {
284        | Parse::Struct { name, .. } | Parse::Enum { name, .. } => name.to_string(),
285        | _ => unreachable!(),
286    };
287    let (mut generator, attributes, body) = parse.into_generator();
288    let attributes = attributes
289        .get_attribute::<ContainerAttributes>()?
290        .unwrap_or_default();
291
292    match body {
293        | Body::Struct(body) => {
294            derive_fingerprint::DeriveFingerprint {
295                fields: body.fields,
296                variants: None,
297                attributes,
298            }
299            .generate(&mut generator, &type_name)?;
300        },
301        | Body::Enum(body) => {
302            derive_fingerprint::DeriveFingerprint {
303                fields: None,
304                variants: Some(body.variants),
305                attributes,
306            }
307            .generate(&mut generator, &type_name)?;
308        },
309    }
310
311    generator.export_to_file("bincode_next", "Fingerprint");
312    generator.finish()
313}