1#![deny(clippy::all, clippy::pedantic, clippy::disallowed_methods)]
2#![allow(
3 clippy::must_use_candidate,
4 clippy::redundant_closure_for_method_calls,
5 clippy::similar_names,
6 clippy::too_many_lines
7)]
8
9use proc_macro::TokenStream;
10use quote::quote;
11use syn::{Data, DeriveInput, Fields, GenericParam, Ident, parse_macro_input};
12
13fn redb_crate_path_for(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
25 let mut path = None;
26 for attr in &input.attrs {
27 if !attr.path().is_ident("redb") {
28 continue;
29 }
30 attr.parse_nested_meta(|meta| {
31 if meta.path.is_ident("crate") {
32 let value: syn::LitStr = meta.value()?.parse()?;
33 path = Some(value.parse::<syn::Path>()?);
34 Ok(())
35 } else {
36 Err(meta.error("expected `#[redb(crate = \"...\")]`"))
37 }
38 })?;
39 }
40 Ok(path.map_or_else(|| quote! { ::redb }, |path| quote! { #path }))
41}
42
43#[proc_macro_derive(Key, attributes(redb))]
57pub fn derive_key(input: TokenStream) -> TokenStream {
58 let input = parse_macro_input!(input as DeriveInput);
59
60 match generate_key_impl(&input) {
61 Ok(tokens) => tokens.into(),
62 Err(err) => err.to_compile_error().into(),
63 }
64}
65
66fn generate_key_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
67 let Data::Struct(_) = &input.data else {
68 return Err(syn::Error::new_spanned(
69 input,
70 "Key can only be derived for structs",
71 ));
72 };
73
74 let name = &input.ident;
75 let generics = &input.generics;
76 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
77 let redb = redb_crate_path_for(input)?;
78
79 Ok(quote! {
80 impl #impl_generics #redb::Key for #name #ty_generics #where_clause {
81 fn compare(
82 data1: &[::std::primitive::u8],
83 data2: &[::std::primitive::u8],
84 ) -> ::std::cmp::Ordering {
85 let value1 = <Self as #redb::Value>::from_bytes(data1);
86 let value2 = <Self as #redb::Value>::from_bytes(data2);
87 ::std::cmp::Ord::cmp(&value1, &value2)
88 }
89 }
90 })
91}
92
93#[proc_macro_derive(Value, attributes(redb))]
111pub fn derive_value(input: TokenStream) -> TokenStream {
112 let input = parse_macro_input!(input as DeriveInput);
113
114 match generate_value_impl(&input) {
115 Ok(tokens) => tokens.into(),
116 Err(err) => err.to_compile_error().into(),
117 }
118}
119
120fn generate_value_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
121 let Data::Struct(data_struct) = &input.data else {
122 return Err(syn::Error::new_spanned(
123 input,
124 "Value can only be derived for structs",
125 ));
126 };
127
128 let name = &input.ident;
129 let generics = &input.generics;
130 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
131
132 let self_type = generate_self_type(name, generics)?;
133 let redb = redb_crate_path_for(input)?;
134
135 let type_name_impl = generate_type_name(name, &data_struct.fields, &redb);
136 let as_bytes_impl = generate_as_bytes(&data_struct.fields, &redb);
137 let from_bytes_impl = generate_from_bytes(name, &data_struct.fields, &redb);
138 let fixed_width_impl = generate_fixed_width(&data_struct.fields, &redb);
139 let niche_impl = generate_niche(&data_struct.fields, &redb);
140
141 Ok(quote! {
142 impl #impl_generics #redb::Value for #name #ty_generics #where_clause {
143 type SelfType<'__redb_a> = #self_type
144 where
145 Self: '__redb_a;
146 type AsBytes<'__redb_a> = ::std::vec::Vec<::std::primitive::u8>
147 where
148 Self: '__redb_a;
149
150 #niche_impl
151
152 fn fixed_width() -> ::std::option::Option<::std::primitive::usize> {
153 #fixed_width_impl
154 }
155
156 fn from_bytes<'__redb_a>(
157 data: &'__redb_a [::std::primitive::u8],
158 ) -> Self::SelfType<'__redb_a>
159 where
160 Self: '__redb_a,
161 {
162 #from_bytes_impl
163 }
164
165 fn as_bytes<'__redb_a, '__redb_b: '__redb_a>(
166 value: &'__redb_a Self::SelfType<'__redb_b>,
167 ) -> Self::AsBytes<'__redb_a>
168 where
169 Self: '__redb_b,
170 {
171 #as_bytes_impl
172 }
173
174 fn type_name() -> #redb::TypeName {
175 #type_name_impl
176 }
177 }
178 })
179}
180
181fn generate_self_type(
182 name: &syn::Ident,
183 generics: &syn::Generics,
184) -> syn::Result<proc_macro2::TokenStream> {
185 if generics.params.is_empty() {
186 Ok(quote! { #name })
187 } else {
188 let mut params = vec![];
189 for param in &generics.params {
190 match param {
191 GenericParam::Lifetime(_) => params.push(quote! { '__redb_a }),
192 GenericParam::Type(type_param) => {
193 return Err(syn::Error::new_spanned(
194 type_param,
195 "Value derivation is not implemented for structs with type parameters",
196 ));
197 }
198 GenericParam::Const(const_param) => {
199 return Err(syn::Error::new_spanned(
200 const_param,
201 "Value derivation is not implemented for structs with const parameters",
202 ));
203 }
204 }
205 }
206
207 Ok(quote! { #name<#(#params),*> })
208 }
209}
210
211fn render_field_type_name(
230 field_type: &syn::Type,
231 redb: &proc_macro2::TokenStream,
232) -> proc_macro2::TokenStream {
233 quote! {
234 {
235 let type_name = <#field_type as #redb::Value>::type_name();
236 if type_name == #redb::TypeName::new(type_name.name()) {
237 ::std::format!("{}#user", type_name.name())
238 } else {
239 ::std::borrow::ToOwned::to_owned(type_name.name())
240 }
241 }
242 }
243}
244
245fn generate_type_name(
246 struct_name: &Ident,
247 fields: &Fields,
248 redb: &proc_macro2::TokenStream,
249) -> proc_macro2::TokenStream {
250 match fields {
251 Fields::Named(fields_named) => {
252 let field_strings: Vec<_> = fields_named
253 .named
254 .iter()
255 .map(|field| {
256 let field_name = field.ident.as_ref().unwrap();
257 let rendered_type = render_field_type_name(&field.ty, redb);
258 quote! {
259 ::std::format!("{}: {}", ::std::stringify!(#field_name), #rendered_type)
260 }
261 })
262 .collect();
263
264 if field_strings.is_empty() {
265 quote! {
266 #redb::TypeName::new(&::std::format!("{} {{}}",
267 ::std::stringify!(#struct_name),
268 ))
269 }
270 } else {
271 quote! {
272 #redb::TypeName::new(&::std::format!("{} {{{}}}",
273 ::std::stringify!(#struct_name),
274 [#(#field_strings),*].join(", ")
275 ))
276 }
277 }
278 }
279 Fields::Unnamed(fields_unnamed) => {
280 let field_strings: Vec<_> = fields_unnamed
281 .unnamed
282 .iter()
283 .map(|field| render_field_type_name(&field.ty, redb))
284 .collect();
285
286 if field_strings.is_empty() {
287 quote! {
288 #redb::TypeName::new(&::std::format!("{}()",
289 ::std::stringify!(#struct_name),
290 ))
291 }
292 } else {
293 quote! {
294 #redb::TypeName::new(&::std::format!("{}({})",
295 ::std::stringify!(#struct_name),
296 [#(#field_strings),*].join(", ")
297 ))
298 }
299 }
300 }
301 Fields::Unit => {
302 quote! {
303 #redb::TypeName::new(::std::stringify!(#struct_name))
304 }
305 }
306 }
307}
308
309fn get_field_types(fields: &Fields) -> Vec<syn::Type> {
310 match fields {
311 Fields::Named(fields_named) => fields_named
312 .named
313 .iter()
314 .map(|field| &field.ty)
315 .cloned()
316 .collect(),
317 Fields::Unnamed(fields_unnamed) => fields_unnamed
318 .unnamed
319 .iter()
320 .map(|field| &field.ty)
321 .cloned()
322 .collect(),
323 Fields::Unit => vec![],
324 }
325}
326
327fn generate_niche(fields: &Fields, redb: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
332 let field_types = get_field_types(fields);
333 let [field_type] = field_types.as_slice() else {
334 return quote! {};
335 };
336 quote! {
337 const NICHE: ::std::option::Option<&'static [::std::primitive::u8]> =
338 <#field_type as #redb::Value>::NICHE;
339 }
340}
341
342fn generate_fixed_width(
343 fields: &Fields,
344 redb: &proc_macro2::TokenStream,
345) -> proc_macro2::TokenStream {
346 let field_types = get_field_types(fields);
347 quote! {
348 let mut total_width = 0usize;
349 #(
350 total_width += <#field_types as #redb::Value>::fixed_width()?;
351 )*
352 ::std::option::Option::Some(total_width)
353 }
354}
355
356fn generate_as_bytes(fields: &Fields, redb: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
357 let field_types = get_field_types(fields);
358 let field_accessors = match fields {
359 Fields::Named(fields_named) => fields_named
360 .named
361 .iter()
362 .map(|field| {
363 let name = &field.ident;
364 quote! { #name }
365 })
366 .collect(),
367 Fields::Unnamed(_) => (0..field_types.len())
368 .map(|i| {
369 let index = syn::Index::from(i);
370 quote! { #index }
371 })
372 .collect(),
373 Fields::Unit => Vec::new(),
374 };
375
376 let num_fields = field_types.len();
377
378 if num_fields == 0 {
379 quote! { ::std::vec::Vec::new() }
380 } else if num_fields == 1 {
381 let field_accessor = &field_accessors[0];
382 let field_type = &field_types[0];
383 quote! {
384 {
385 let field_bytes = <#field_type as #redb::Value>::as_bytes(&value.#field_accessor);
386 let bytes: &[::std::primitive::u8] = ::std::convert::AsRef::as_ref(&field_bytes);
387 bytes.to_vec()
388 }
389 }
390 } else {
391 let field_types_except_last = &field_types[..num_fields - 1];
392 let field_accessors_except_last = &field_accessors[..num_fields - 1];
393
394 quote! {
395 {
396 let mut result = ::std::vec::Vec::new();
397
398 #(
399 if <#field_types_except_last as #redb::Value>::fixed_width().is_none() {
400 let field_bytes = <#field_types_except_last as #redb::Value>::as_bytes(
401 &value.#field_accessors_except_last,
402 );
403 let bytes: &[::std::primitive::u8] =
404 ::std::convert::AsRef::as_ref(&field_bytes);
405 let len = bytes.len();
406 if len < 254 {
407 result.push(
408 <::std::primitive::u8 as ::std::convert::TryFrom<
409 ::std::primitive::usize,
410 >>::try_from(len)
411 .unwrap(),
412 );
413 } else if let ::std::result::Result::Ok(u16_len) =
414 <::std::primitive::u16 as ::std::convert::TryFrom<
415 ::std::primitive::usize,
416 >>::try_from(len)
417 {
418 result.push(254u8);
419 result.extend_from_slice(&u16_len.to_le_bytes());
420 } else {
421 let u32_len = <::std::primitive::u32 as ::std::convert::TryFrom<
422 ::std::primitive::usize,
423 >>::try_from(len)
424 .unwrap();
425 result.push(255u8);
426 result.extend_from_slice(&u32_len.to_le_bytes());
427 }
428 }
429 )*
430
431 #(
432 {
433 let field_bytes = <#field_types as #redb::Value>::as_bytes(
434 &value.#field_accessors,
435 );
436 let bytes: &[::std::primitive::u8] =
437 ::std::convert::AsRef::as_ref(&field_bytes);
438 result.extend_from_slice(bytes);
439 }
440 )*
441
442 result
443 }
444 }
445 }
446}
447
448fn generate_from_bytes(
449 name: &Ident,
450 fields: &Fields,
451 redb: &proc_macro2::TokenStream,
452) -> proc_macro2::TokenStream {
453 let field_types = get_field_types(fields);
454 let field_vars: Vec<_> = (0..field_types.len())
455 .map(|i| quote::format_ident!("field_{}", i))
456 .collect();
457 let num_fields = field_types.len();
458
459 let body = if num_fields == 0 {
460 quote! {}
461 } else if num_fields == 1 {
462 let field_var = &field_vars[0];
463 let field_type = &field_types[0];
464 quote! {
465 let #field_var = <#field_type as #redb::Value>::from_bytes(data);
466 }
467 } else {
468 let field_types_except_last = &field_types[..num_fields - 1];
469 let field_vars_except_last = &field_vars[..num_fields - 1];
470 let last_field_var = field_vars.last();
471 let last_field_type = field_types.last();
472
473 quote! {
474 let mut offset = 0usize;
475 let mut var_lengths = ::std::vec::Vec::new();
476
477 #(
478 if <#field_types_except_last as #redb::Value>::fixed_width().is_none() {
479 let (len, bytes_read) = match data[offset] {
480 0u8..=253u8 => (data[offset] as ::std::primitive::usize, 1usize),
481 254u8 => (
482 ::std::primitive::u16::from_le_bytes([
483 data[offset + 1],
484 data[offset + 2],
485 ]) as ::std::primitive::usize,
486 3usize,
487 ),
488 255u8 => (
489 ::std::primitive::u32::from_le_bytes([
490 data[offset + 1],
491 data[offset + 2],
492 data[offset + 3],
493 data[offset + 4],
494 ]) as ::std::primitive::usize,
495 5usize,
496 ),
497 };
498 var_lengths.push(len);
499 offset += bytes_read;
500 }
501 )*
502
503 let mut var_index = 0usize;
504 #(
505 let #field_vars_except_last = if let ::std::option::Option::Some(fixed_width) =
506 <#field_types_except_last as #redb::Value>::fixed_width()
507 {
508 let field_data = &data[offset..offset + fixed_width];
509 offset += fixed_width;
510 <#field_types_except_last as #redb::Value>::from_bytes(field_data)
511 } else {
512 let len = var_lengths[var_index];
513 let field_data = &data[offset..offset + len];
514 offset += len;
515 var_index += 1;
516 <#field_types_except_last as #redb::Value>::from_bytes(field_data)
517 };
518 )*
519
520 let #last_field_var = if let ::std::option::Option::Some(fixed_width) =
521 <#last_field_type as #redb::Value>::fixed_width()
522 {
523 let field_data = &data[offset..offset + fixed_width];
524 <#last_field_type as #redb::Value>::from_bytes(field_data)
525 } else {
526 <#last_field_type as #redb::Value>::from_bytes(&data[offset..])
527 };
528 }
529 };
530 match fields {
531 Fields::Named(fields_named) => {
532 let field_names: Vec<_> = fields_named
533 .named
534 .iter()
535 .map(|field| &field.ident)
536 .collect();
537
538 quote! {
539 {
540 #body
541 #name {
542 #(#field_names: #field_vars),*
543 }
544 }
545 }
546 }
547 Fields::Unnamed(_) => {
548 quote! {
549 {
550 #body
551 #name(#(#field_vars),*)
552 }
553 }
554 }
555 Fields::Unit => {
556 quote! { #name }
557 }
558 }
559}