wincode_derive/lib.rs
1//! Derive macros for `SchemaWrite` and `SchemaRead`.
2//!
3//! Note using this on packed structs is UB.
4//!
5//! Refer to the [`wincode`](https://docs.rs/wincode) crate for examples.
6use {
7 proc_macro::TokenStream,
8 syn::{parse_macro_input, DeriveInput},
9};
10
11mod assert_zero_copy;
12mod common;
13mod schema_read;
14mod schema_write;
15mod uninit_builder;
16
17/// Implement `SchemaWrite` for a struct or enum.
18#[proc_macro_derive(SchemaWrite, attributes(wincode))]
19pub fn derive_schema_write(input: TokenStream) -> TokenStream {
20 let input = parse_macro_input!(input as DeriveInput);
21 match schema_write::generate(input) {
22 Ok(tokens) => tokens.into(),
23 Err(e) => e.write_errors().into(),
24 }
25}
26
27/// Implement `SchemaRead` for a struct or enum.
28#[proc_macro_derive(SchemaRead, attributes(wincode))]
29pub fn derive_schema_read(input: TokenStream) -> TokenStream {
30 let input = parse_macro_input!(input as DeriveInput);
31 match schema_read::generate(input) {
32 Ok(tokens) => tokens.into(),
33 Err(e) => e.write_errors().into(),
34 }
35}
36
37/// Include placement initialization helpers for structs.
38///
39/// This generates an `UninitBuilder` for the given struct, providing convenience
40/// methods that can avoid a lot of boilerplate when implementing custom
41/// `SchemaRead` implementations. In particular, it provides methods that
42/// deal with projecting subfields of structs into `MaybeUninit`s. Without this,
43/// one would have to write a litany of `&mut *(&raw mut (*dst_ptr).field).cast()` to
44/// access MaybeUninit struct fields. It also provides initialization helpers and
45/// drop tracking logic.
46///
47/// For example:
48/// ```ignore
49/// #[derive(UninitBuilder)]
50/// struct Message {
51/// payload: Vec<u8>,
52/// bytes: [u8; 32],
53/// }
54///
55/// unsafe impl<'de, C: Config> SchemaRead<'de, C> for Message {
56/// type Dst = Self;
57///
58/// fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
59/// let msg_builder = MessageUninitBuilder::<C>::from_maybe_uninit_mut(dst);
60/// // Deserializes a `Vec<u8>` into the `payload` slot of `Message` and marks the field
61/// // as initialized. If the subsequent `read_bytes` call fails, the `payload` field will
62/// // be dropped.
63/// msg_builder.read_payload(reader.by_ref())?;
64/// msg_builder.read_bytes(reader)?;
65/// msg_builder.finish();
66/// }
67/// }
68/// ```
69///
70/// We cannot do this for enums, given the lack of facilities for placement initialization.
71#[proc_macro_derive(UninitBuilder, attributes(wincode))]
72pub fn derive_uninit_builder(input: TokenStream) -> TokenStream {
73 let input = parse_macro_input!(input as DeriveInput);
74 match uninit_builder::generate(input) {
75 Ok(tokens) => tokens.into(),
76 Err(e) => e.write_errors().into(),
77 }
78}