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}