1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::{quote, quote_spanned};
4use syn::spanned::Spanned;
5use syn::Token;
6
7#[proc_macro]
9pub fn table(input: TokenStream) -> TokenStream {
10 let parsed = syn::parse_macro_input!(input as TableInput);
11 parsed.generate().into()
12}
13
14#[proc_macro]
16pub fn database(input: TokenStream) -> TokenStream {
17 let parsed = syn::parse_macro_input!(input as DatabaseInput);
18 parsed.generate().into()
19}
20
21struct TableInput {
23 table_name: syn::Ident,
24 max_records: syn::Expr,
25 primary_key: syn::Ident,
26 secondary_index: Option<syn::Ident>,
27 fields: Vec<FieldDef>,
28}
29
30impl syn::parse::Parse for TableInput {
31 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
32 let table_name: syn::Ident = input.parse()?;
34 input.parse::<Token![,]>()?;
35
36 let max_records: syn::Expr = input.parse()?;
38 input.parse::<Token![,]>()?;
39
40 let _: syn::Ident = input.parse()?; input.parse::<Token![:]>()?;
43 let primary_key: syn::Ident = input.parse()?;
44 input.parse::<Token![,]>()?;
45
46 let mut secondary_index = None;
48 if input.peek(syn::Ident) {
49 let fork = input.fork();
50 let ident: syn::Ident = fork.parse()?;
51
52 if ident.to_string() == "secondary_index" {
53 let _: syn::Ident = input.parse()?; input.parse::<Token![:]>()?;
55 secondary_index = Some(input.parse()?);
56 input.parse::<Token![,]>()?;
57 }
58 }
59
60 let _: syn::Ident = input.parse()?; input.parse::<Token![:]>()?;
63
64 let content;
66 let _braces = syn::braced!(content in input);
67
68 let mut fields = Vec::new();
70
71 while !content.is_empty() {
73 let field: FieldDef = content.parse()?;
75 fields.push(field);
76
77 if content.peek(Token![,]) {
79 content.parse::<Token![,]>()?;
80 }
81
82 if content.is_empty() {
84 break;
85 }
86 }
87
88 Ok(Self {
89 table_name,
90 max_records,
91 primary_key,
92 secondary_index,
93 fields,
94 })
95 }
96}
97
98impl TableInput {
99 fn generate(&self) -> proc_macro2::TokenStream {
100 let table_name = &self.table_name;
101 let max_records = &self.max_records;
102
103 let mut current_offset = 0usize;
105 let mut total_record_size = 0usize;
106 let mut primary_key_index = None;
107 let mut secondary_index_index = None;
108
109 let mut field_defs = Vec::new();
111
112 for (index, field) in self.fields.iter().enumerate() {
113 let name = &field.name;
114 let data_type = field.data_type.clone();
115 let size = &field.size;
116
117 if field.name == self.primary_key {
119 primary_key_index = Some(index);
120 }
121
122 if let Some(secondary_key) = &self.secondary_index {
124 if field.name == *secondary_key {
125 secondary_index_index = Some(index);
126 }
127 }
128
129 if field.data_type.to_string() == "String" {
131 let string_size = match size {
134 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(int_lit), .. }) => {
135 int_lit.base10_parse::<usize>().unwrap()
136 },
137 _ => panic!("String field size must be a literal integer"),
138 };
139
140 field_defs.push(quote_spanned! {field.name.span() =>
141 remdb::types::FieldDef {
142 name: stringify!(#name),
143 data_type: remdb::types::DataType::#data_type,
144 size: #string_size,
145 offset: #current_offset,
146 }
147 });
148
149 current_offset += string_size;
150 total_record_size += string_size;
151 } else {
152 let type_size = match field.data_type.to_string().as_str() {
154 "Int8" => 1usize,
155 "Int16" => 2usize,
156 "Int32" => 4usize,
157 "Int64" => 8usize,
158 "Float32" => 4usize,
159 "Float64" => 8usize,
160 "Bool" => 1usize,
161 "Timestamp" => 8usize,
162 _ => panic!("Unsupported data type: {}", field.data_type),
163 };
164
165 field_defs.push(quote_spanned! {field.name.span() =>
166 remdb::types::FieldDef {
167 name: stringify!(#name),
168 data_type: remdb::types::DataType::#data_type,
169 size: #type_size,
170 offset: #current_offset,
171 }
172 });
173
174 current_offset += type_size;
175 total_record_size += type_size;
176 }
177 }
178
179 let primary_key = primary_key_index.unwrap_or(0);
180 let secondary_index = match secondary_index_index {
181 Some(index) => quote! {Some(#index)},
182 None => quote! {None},
183 };
184
185 if field_defs.is_empty() {
187 panic!("field_defs is empty! No fields were generated!");
188 }
189
190 println!("Generated {} fields for table {}", field_defs.len(), table_name);
192
193 quote! {
194 static #table_name: remdb::types::TableDef = remdb::types::TableDef {
195 id: 0,
196 name: stringify!(#table_name),
197 fields: &[
198 #(#field_defs),*
199 ],
200 primary_key: #primary_key,
201 secondary_index: #secondary_index,
202 record_size: #total_record_size as usize,
203 max_records: #max_records,
204 };
205
206 const _: () = assert!(#table_name.fields.len() > 0, "Fields array is empty!");
208 }
209 }
210}
211
212struct FieldDef {
214 name: syn::Ident,
215 data_type: syn::Ident,
216 size: syn::Expr,
217}
218
219impl syn::parse::Parse for FieldDef {
220 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
221 let name: syn::Ident = input.parse()?;
223 let name_span = name.span();
224
225 input.parse::<Token![:]>()?;
227
228 if input.peek(syn::Ident) {
232 let fork = input.fork();
233 let ident: syn::Ident = fork.parse()?;
234
235 if ident.to_string() == "str" && fork.peek(syn::token::Paren) {
236 let _: syn::Ident = input.parse()?; let content;
239 syn::parenthesized!(content in input);
240 let len: syn::Expr = content.parse()?;
241
242 return Ok(Self {
243 name,
244 data_type: syn::Ident::new("String", name_span),
245 size: len,
246 });
247 }
248 }
249
250 let mut type_str = String::new();
253 let mut is_done = false;
254
255 while !is_done && !input.is_empty() {
257 if input.peek(Token![,]) || input.peek(syn::token::Brace) {
259 is_done = true;
260 break;
261 }
262
263 let token = input.step(|cursor| {
265 let (token, rest) = cursor.token_tree()
266 .ok_or_else(|| syn::Error::new(cursor.span(), "Expected token"))?;
267 Ok((token.to_string(), rest))
268 })?;
269
270 type_str.push_str(&token);
271 }
272
273 type_str = type_str.trim().to_string();
274
275 if type_str.is_empty() {
276 return Err(syn::Error::new(input.span(), "Expected field type"));
277 }
278
279 let (data_type, size) = match type_str.as_str() {
281 "i8" => ("Int8", "1"),
282 "i16" => ("Int16", "2"),
283 "i32" => ("Int32", "4"),
284 "i64" => ("Int64", "8"),
285 "f32" => ("Float32", "4"),
286 "f64" => ("Float64", "8"),
287 "bool" => ("Bool", "1"),
288 "u64" => ("Timestamp", "8"),
289 _ => {
290 return Err(syn::Error::new(
291 input.span(),
292 format!("Unsupported field type: {}", type_str),
293 ));
294 }
295 };
296
297 Ok(Self {
298 name,
299 data_type: syn::Ident::new(data_type, name_span),
300 size: syn::parse_str(size)?,
301 })
302 }
303}
304
305
306
307struct DatabaseInput {
309 db_name: syn::Ident,
310 tables: Vec<syn::Ident>,
311 low_power: bool,
312 low_power_max_records: Option<usize>,
313}
314
315impl syn::parse::Parse for DatabaseInput {
316 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
317 let db_name: syn::Ident = input.parse()?;
318 input.parse::<Token![,]>()?;
319
320 let tables_keyword: syn::Ident = input.parse()?;
322 if tables_keyword.to_string() != "tables" {
323 return Err(syn::Error::new(
324 tables_keyword.span(),
325 "expected 'tables'",
326 ));
327 }
328 input.parse::<Token![:]>()?;
329
330 let content;
332 syn::bracketed!(content in input);
333
334 let mut tables = Vec::new();
335 let mut content = content;
336 while !content.is_empty() {
337 tables.push(content.parse()?);
338 if !content.is_empty() {
339 content.parse::<Token![,]>()?;
340 }
341 }
342
343 let mut low_power = false;
344 let mut low_power_max_records = None;
345
346 while !input.is_empty() {
348 if input.peek(Token![,]) {
350 input.parse::<Token![,]>()?;
351 }
352
353 if input.is_empty() {
355 break;
356 }
357
358 let param_name: syn::Ident = input.parse()?;
359 input.parse::<Token![:]>()?;
360
361 match param_name.to_string().as_str() {
362 "low_power" => {
363 let lit: syn::LitBool = input.parse()?;
364 low_power = lit.value;
365 },
366 "low_power_max_records" => {
367 let lit: syn::LitInt = input.parse()?;
368 let value = lit.base10_parse::<usize>().unwrap();
369 low_power_max_records = Some(value);
370 },
371 _ => {
372 return Err(syn::Error::new(
373 param_name.span(),
374 format!("Unexpected parameter: {}", param_name),
375 ));
376 }
377 }
378 }
379
380 Ok(Self {
381 db_name,
382 tables,
383 low_power,
384 low_power_max_records,
385 })
386 }
387}
388
389impl DatabaseInput {
390 fn generate(&self) -> proc_macro2::TokenStream {
391 let db_name = &self.db_name;
392 let tables = &self.tables;
393 let low_power = self.low_power;
394 let low_power_max_records = match &self.low_power_max_records {
395 Some(val) => quote! { Some(#val) },
396 None => quote! { None },
397 };
398
399 quote! {
400 static #db_name: remdb::config::DbConfig = remdb::config::DbConfig {
401 tables: &[
402 #(#tables),*
403 ],
404 total_memory: 0,
405 low_power_mode_supported: #low_power,
406 low_power_max_records: #low_power_max_records,
407 };
408 }
409 }
410}