Skip to main content

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::{DeriveInput, parse_macro_input},
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///
29/// When the type has `#[wincode(context = "ContextType")]`, this implements
30/// `SchemaReadContext` instead. Fields marked with `#[wincode(context)]` receive
31/// that context; unmarked fields continue to use `SchemaRead`.
32///
33/// See the `wincode` crate's derive-attribute documentation for details and examples.
34#[proc_macro_derive(SchemaRead, attributes(wincode))]
35pub fn derive_schema_read(input: TokenStream) -> TokenStream {
36    let input = parse_macro_input!(input as DeriveInput);
37    match schema_read::generate(input) {
38        Ok(tokens) => tokens.into(),
39        Err(e) => e.write_errors().into(),
40    }
41}
42
43/// Include placement initialization helpers for structs.
44///
45/// This generates an `UninitBuilder` for the given struct, providing convenience
46/// methods that can avoid a lot of boilerplate when implementing custom
47/// `SchemaRead` implementations. In particular, it provides methods that
48/// deal with projecting subfields of structs into `MaybeUninit`s. Without this,
49/// one would have to write a litany of `&mut *(&raw mut (*dst_ptr).field).cast()` to
50/// access MaybeUninit struct fields. It also provides initialization helpers and
51/// drop tracking logic.
52///
53/// For example:
54/// ```ignore
55/// #[derive(UninitBuilder)]
56/// struct Message {
57///     payload: Vec<u8>,
58///     bytes: [u8; 32],
59/// }
60///
61/// unsafe impl<'de, C: Config> SchemaRead<'de, C> for Message {
62///     type Dst = Self;
63///
64///     fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
65///         let msg_builder = MessageUninitBuilder::<C>::from_maybe_uninit_mut(dst);
66///         // Deserializes a `Vec<u8>` into the `payload` slot of `Message` and marks the field
67///         // as initialized. If the subsequent `read_bytes` call fails, the `payload` field will
68///         // be dropped.
69///         msg_builder.read_payload(reader.by_ref())?;
70///         msg_builder.read_bytes(reader)?;
71///         msg_builder.finish();
72///     }
73/// }
74/// ```
75///
76/// We cannot do this for enums, given the lack of facilities for placement initialization.
77#[proc_macro_derive(UninitBuilder, attributes(wincode))]
78pub fn derive_uninit_builder(input: TokenStream) -> TokenStream {
79    let input = parse_macro_input!(input as DeriveInput);
80    match uninit_builder::generate(input) {
81        Ok(tokens) => tokens.into(),
82        Err(e) => e.write_errors().into(),
83    }
84}