Skip to main content

ostd_pod_macros/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3#![doc = include_str!("../README.md")]
4
5use proc_macro::TokenStream;
6
7mod pod_derive;
8mod pod_union;
9
10/// An attribute macro that replaces `#[derive(Pod)]` with the corresponding zerocopy traits.
11#[proc_macro_attribute]
12pub fn derive(attrs: TokenStream, input: TokenStream) -> TokenStream {
13    pod_derive::expand_derive(attrs, input)
14}
15
16/// An attribute macro that enables safe usage of unions as POD types.
17///
18/// Rust's built-in unions cannot directly derive `zerocopy::IntoBytes` because unions require
19/// field-by-field initialization and access. The `#[pod_union]` macro solves this by
20/// transforming a union into a safe wrapper struct.
21///
22/// # Implementation details
23///
24/// When you write:
25///
26/// ```rust
27/// use ostd_pod_macros::pod_union;
28///
29/// #[repr(C)]
30/// #[pod_union]
31/// #[derive(Clone, Copy)]
32/// pub union Data {
33///     value: u64,
34///     bytes: [u8; 4],
35/// }
36/// ```
37///
38/// The `#[pod_union]` macro internally generates something equivalent to:
39///
40/// ```rust
41/// use ostd_pod::array_helper::{ArrayFactory, ArrayManufacture, U64Array};
42/// use ostd_pod::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Pod};
43///
44/// // Internal private union
45/// #[repr(C)]
46/// #[derive(FromBytes, KnownLayout, Immutable)]
47/// union __Data__ {
48///     value: u64,
49///     bytes: [u8; 4],
50/// }
51///
52/// // Public wrapper struct that provides safe access
53/// #[repr(transparent)]
54/// #[derive(FromBytes, KnownLayout, Immutable, IntoBytes)]
55/// pub struct Data(<ArrayFactory<
56///        { align_of::<__Data__>() },
57///        { size_of::<__Data__>() / (align_of::<__Data__>()) },
58///    > as ArrayManufacture>::Array);
59///
60/// impl Data {
61///     // Field accessor methods
62///     pub fn value(&self) -> &u64 {
63///         u64::ref_from_bytes(&self.0.as_bytes()[..8]).unwrap()
64///     }
65///     pub fn value_mut(&mut self) -> &mut u64 {
66///         u64::mut_from_bytes(&mut self.0.as_mut_bytes()[..8]).unwrap()
67///     }
68///     pub fn bytes(&self) -> &[u8; 4] {
69///         <[u8; 4]>::ref_from_bytes(&self.0.as_bytes()[..4]).unwrap()
70///     }
71///     pub fn bytes_mut(&mut self) -> &mut [u8; 4] {
72///         <[u8; 4]>::mut_from_bytes(&mut self.0.as_mut_bytes()[..4]).unwrap()
73///     }
74///
75///     // Initializer methods
76///     pub fn new_value(value: u64) -> Self {
77///         let mut slf = Self::new_zeroed();
78///         *slf.value_mut() = value;
79///         slf
80///     }
81///     pub fn new_bytes(bytes: [u8; 4]) -> Self {
82///         let mut slf = Self::new_zeroed();
83///         *slf.bytes_mut() = bytes;
84///         slf
85///     }
86/// }
87/// ```
88#[proc_macro_attribute]
89pub fn pod_union(_attr: TokenStream, item: TokenStream) -> TokenStream {
90    let input = syn::parse_macro_input!(item as syn::DeriveInput);
91    pod_union::expand_pod_union(input).into()
92}