memmap3_macros/lib.rs
1mod codegen;
2mod validation;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{Fields, ItemStruct, Meta, parse_macro_input};
7
8/// Attribute macro for memory-mapped structs.
9///
10/// This macro transforms regular Rust types into mmap-safe types and generates
11/// the necessary trait implementation. Users write normal Rust code:
12///
13/// # Example
14///
15/// ```rust
16/// use memmap3::mmap_struct;
17/// use memmap3::MmapStruct;
18/// use std::sync::atomic::Ordering;
19///
20/// #[mmap_struct]
21/// struct Config {
22/// // Regular field
23/// version: u32,
24///
25/// // Atomic field - transformed to MmapAtomicU64
26/// #[mmap(atomic)]
27/// connection_count: u64,
28///
29/// // String field - auto-detected! [u8; N] defaults to string
30/// server_name: [u8; 256],
31///
32/// // Raw binary data - use #[mmap(raw)] to keep as byte array
33/// #[mmap(raw)]
34/// binary_data: [u8; 128],
35/// }
36///
37/// # fn main() -> std::io::Result<()> {
38/// // Usage:
39/// let mut config = MmapStruct::<Config>::create("/tmp/config.mmap")?;
40/// config.version = 1;
41/// config.connection_count.fetch_add(1, Ordering::SeqCst);
42/// config.server_name.set("localhost"); // Auto-detected as string!
43/// config.binary_data[0] = 0xFF; // Raw binary access
44/// # Ok(())
45/// # }
46/// ```
47///
48/// # Features
49///
50/// - Automatically adds `#[repr(C)]` for memory layout compatibility
51/// - Transforms `u64` with `#[mmap(atomic)]` to `MmapAtomicU64`
52/// - **Auto-detects** `[u8; N]` as `MmapString<N>` (use `#[mmap(raw)]` for binary)
53/// - Generates StructLayout implementation
54#[proc_macro_attribute]
55pub fn mmap_struct(_args: TokenStream, input: TokenStream) -> TokenStream {
56 let mut input = parse_macro_input!(input as ItemStruct);
57
58 // Validate the struct
59 if let Err(err) = validate_struct(&input) {
60 let error = syn::Error::new_spanned(&input, err).to_compile_error();
61 return TokenStream::from(quote! {
62 #input
63 #error
64 });
65 }
66
67 // Validate all fields are mmap-compatible
68 if let Err(err) = validation::validate_fields(&input) {
69 return TokenStream::from(err.to_compile_error());
70 }
71
72 // Add repr(C) if not present
73 add_repr_c(&mut input);
74
75 // Transform field types based on attributes and generate implementation
76 let transformed = codegen::transform_and_implement(input);
77
78 TokenStream::from(transformed)
79}
80
81/// Validate that the struct is suitable for mmap
82fn validate_struct(input: &ItemStruct) -> Result<(), String> {
83 // Check that it has named fields
84 match &input.fields {
85 Fields::Named(_) => Ok(()),
86 Fields::Unnamed(_) => Err("mmap_struct doesn't support tuple structs".into()),
87 Fields::Unit => Err("mmap_struct doesn't support unit structs".into()),
88 }
89}
90
91/// Add repr(C) if not already present
92fn add_repr_c(input: &mut ItemStruct) {
93 let has_repr_c = input.attrs.iter().any(|attr| {
94 if attr.path().is_ident("repr")
95 && let Meta::List(list) = &attr.meta
96 {
97 return list.tokens.to_string() == "C";
98 }
99 false
100 });
101
102 if !has_repr_c {
103 input.attrs.push(syn::parse_quote!(#[repr(C)]));
104 }
105}
106
107/// Helper attribute for field-level configurations in mmap_struct
108/// This is used to mark fields with special behaviors like #[mmap(raw)]
109#[proc_macro_attribute]
110pub fn mmap(_args: TokenStream, input: TokenStream) -> TokenStream {
111 // This is a helper attribute that's processed by mmap_struct
112 // We just pass through the input unchanged
113 input
114}