1use proc_macro::TokenStream;
7use quote::quote;
8use syn::{parse_macro_input, DeriveInput, Lit};
9
10#[proc_macro_derive(ObjectStore, attributes(store, index))]
47pub fn derive_object_store(input: TokenStream) -> TokenStream {
48 let input = parse_macro_input!(input as DeriveInput);
49 let name = &input.ident;
50 let store_name = name.to_string();
51
52 let mut key_path: Option<String> = None;
53 let mut auto_increment = false;
54 let mut store_name = store_name; for attr in &input.attrs {
57 if attr.path().is_ident("store") {
58 let _ = attr.parse_nested_meta(|meta| {
59 if meta.path.is_ident("key_path") {
60 let value: Lit = meta.value()?.parse()?;
61 if let Lit::Str(s) = value {
62 key_path = Some(s.value());
63 }
64 } else if meta.path.is_ident("auto_increment") {
65 auto_increment = true;
66 } else if meta.path.is_ident("name") {
67 let value: Lit = meta.value()?.parse()?;
68 if let Lit::Str(s) = value {
69 store_name = s.value();
70 }
71 }
72 Ok(())
73 });
74 }
75 }
76
77 let key_path = key_path.expect("#[store(key_path = \"...\")] is required");
78
79 let mut indexes = Vec::new();
80
81 if let syn::Data::Struct(data) = &input.data {
82 for field in &data.fields {
83 let field_name = field
84 .ident
85 .as_ref()
86 .map(|i| i.to_string())
87 .expect("named fields only");
88
89 for attr in &field.attrs {
90 if attr.path().is_ident("index") {
91 let mut unique = false;
92 let mut index_name = field_name.clone();
93
94 let _ = attr.parse_nested_meta(|meta| {
95 if meta.path.is_ident("unique") {
96 unique = true;
97 } else if meta.path.is_ident("name") {
98 let value: Lit = meta.value()?.parse()?;
99 if let Lit::Str(s) = value {
100 index_name = s.value();
101 }
102 }
103 Ok(())
104 });
105
106 indexes.push((index_name, field_name.clone(), unique));
107 }
108 }
109 }
110 }
111
112 let index_calls: Vec<_> = indexes
113 .iter()
114 .map(|(idx_name, field_name, unique)| {
115 let u = *unique;
116 quote! {
117 .with_index(#idx_name, &[#field_name], #u)
118 }
119 })
120 .collect();
121
122 let auto_inc = if auto_increment {
123 quote! { .with_auto_increment() }
124 } else {
125 quote! {}
126 };
127
128 let expanded = quote! {
129 impl flowdb::jsondb::ObjectStore for #name {
130 fn store_def() -> flowdb::jsondb::StoreSchema {
131 flowdb::jsondb::StoreSchema::new(#store_name, #key_path)
132 #(#index_calls)*
133 #auto_inc
134 }
135 }
136 };
137
138 expanded.into()
139}