Skip to content

Commit 57f879c

Browse files
authored
feat: Expose UninitBuilder as derive macro; deprecate struct_extensions (#169)
1 parent e3b878b commit 57f879c

File tree

6 files changed

+521
-418
lines changed

6 files changed

+521
-418
lines changed

wincode-derive/src/common.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ pub(crate) struct SchemaArgs {
597597
#[darling(default)]
598598
pub(crate) no_suppress_unused: bool,
599599
/// Specifies whether to generate placement initialization struct helpers on `SchemaRead` implementations.
600+
///
601+
/// DEPRECATED; use `#[derive(UninitBuilder)]` instead.
600602
#[darling(default)]
601603
pub(crate) struct_extensions: bool,
602604
/// Specifies the encoding to use for enum discriminants.

wincode-derive/src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod assert_zero_copy;
1212
mod common;
1313
mod schema_read;
1414
mod schema_write;
15+
mod uninit_builder;
1516

1617
/// Implement `SchemaWrite` for a struct or enum.
1718
#[proc_macro_derive(SchemaWrite, attributes(wincode))]
@@ -32,3 +33,46 @@ pub fn derive_schema_read(input: TokenStream) -> TokenStream {
3233
Err(e) => e.write_errors().into(),
3334
}
3435
}
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(&mut reader)?;
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))]
72+
pub 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+
}

0 commit comments

Comments
 (0)