Skip to main content

ring_db_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
4
5/// Derive macro that implements [`ringdb::Payload`] for a struct.
6///
7/// The storage strategy is selected with the `#[payload(storage = "...")]`
8/// attribute:
9///
10/// | Value | Strategy | Requirement |
11/// |-------|----------|-------------|
12/// | `"serde"` (default) | bincode serialization, variable-size payloads | `T: Serialize + DeserializeOwned` |
13/// | `"pod"` | raw bytes, zero-copy `&T` fetch | `T: bytemuck::Pod` |
14///
15/// # Examples
16///
17/// ```ignore
18/// use serde::{Serialize, Deserialize};
19/// use ringdb::Payload;
20///
21/// // Serde storage (default) — supports any serializable type
22/// #[derive(Serialize, Deserialize, Payload)]
23/// struct GeoRecord { lat: f64, lon: f64, label: String }
24///
25/// // Pod storage — zero-copy &T fetch, requires fixed-size plain-old-data
26/// use bytemuck::{Pod, Zeroable};
27/// #[derive(Copy, Clone, Pod, Zeroable, Payload)]
28/// #[repr(C)]
29/// #[payload(storage = "pod")]
30/// struct GeoPoint { lat: f32, lon: f32, altitude: f32 }
31/// ```
32#[proc_macro_derive(Payload, attributes(payload))]
33pub fn derive_payload(input: TokenStream) -> TokenStream {
34    let input = parse_macro_input!(input as DeriveInput);
35    let name = &input.ident;
36    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
37
38    // Parse #[payload(storage = "pod"|"serde")]
39    let mut storage = String::from("serde");
40    for attr in &input.attrs {
41        if attr.path().is_ident("payload") {
42            let _ = attr.parse_nested_meta(|meta| {
43                if meta.path.is_ident("storage") {
44                    let value = meta.value()?;
45                    let s: syn::LitStr = value.parse()?;
46                    storage = s.value();
47                }
48                Ok(())
49            });
50        }
51    }
52
53    let expanded = match storage.as_str() {
54        "pod" => quote! {
55            impl #impl_generics ::ringdb::Payload for #name #ty_generics #where_clause {
56                type Store   = ::ringdb::__private::PodStore<Self>;
57                type Builder = ::ringdb::__private::PodStoreBuilder<Self>;
58
59                fn make_builder() -> ::ringdb::__private::Result<Self::Builder> {
60                    ::ringdb::__private::PodStoreBuilder::new()
61                }
62
63                fn load_store(dir: &::std::path::Path) -> ::ringdb::__private::Result<Self::Store> {
64                    ::ringdb::__private::PodStore::load(&dir.join("payloads.bin"))
65                }
66            }
67        },
68        _ => quote! {
69            impl #impl_generics ::ringdb::Payload for #name #ty_generics #where_clause {
70                type Store   = ::ringdb::__private::SerdeStore<Self>;
71                type Builder = ::ringdb::__private::SerdeStoreBuilder<Self>;
72
73                fn make_builder() -> ::ringdb::__private::Result<Self::Builder> {
74                    ::ringdb::__private::SerdeStoreBuilder::new()
75                }
76
77                fn load_store(dir: &::std::path::Path) -> ::ringdb::__private::Result<Self::Store> {
78                    ::ringdb::__private::SerdeStore::load(
79                        &dir.join("payloads.bin"),
80                        &dir.join("offsets.bin"),
81                    )
82                }
83            }
84        },
85    };
86
87    TokenStream::from(expanded)
88}