1#![forbid(unsafe_code)]
4#![warn(
5 clippy::cargo,
6 missing_docs,
7 clippy::pedantic,
9 future_incompatible,
10 rust_2018_idioms,
11)]
12#![cfg_attr(doc, deny(rustdoc::all))]
13
14use attribute_derive::{Attribute, ConvertParsed};
15use manyhow::{bail, error_message, manyhow, JoinToTokensError, Result};
16use proc_macro2::{Span, TokenStream};
17use proc_macro_crate::{crate_name, FoundCrate};
18use quote::{quote_spanned, ToTokens};
19use quote_use::{
20 format_ident_namespaced as format_ident, parse_quote_use as parse_quote, quote_use as quote,
21};
22use syn::punctuated::Punctuated;
23use syn::spanned::Spanned;
24use syn::{
25 parse, Data, DataEnum, DataStruct, DeriveInput, Expr, Field, Fields, FieldsNamed,
26 FieldsUnnamed, Ident, Index, Path, Token, Type, TypePath, Variant,
27};
28
29mod view;
30
31fn core_path() -> Path {
36 match crate_name("bonsaidb")
37 .or_else(|_| crate_name("bonsaidb_server"))
38 .or_else(|_| crate_name("bonsaidb_local"))
39 .or_else(|_| crate_name("bonsaidb_client"))
40 {
41 Ok(FoundCrate::Name(name)) => {
42 let ident = Ident::new(&name, Span::call_site());
43 parse_quote!(::#ident::core)
44 }
45 Ok(FoundCrate::Itself) => parse_quote!(crate::core),
46 Err(_) => match crate_name("bonsaidb_core") {
47 Ok(FoundCrate::Name(name)) => {
48 let ident = Ident::new(&name, Span::call_site());
49 parse_quote!(::#ident)
50 }
51 Ok(FoundCrate::Itself) => parse_quote!(crate),
52 Err(_) => match () {
53 () if cfg!(feature = "omnibus-path") => parse_quote!(::bonsaidb::core),
54 () if cfg!(feature = "server-path") => parse_quote!(::bonsaidb_server::core),
55 () if cfg!(feature = "local-path") => parse_quote!(::bonsaidb_local::core),
56 () if cfg!(feature = "client-path") => parse_quote!(::bonsaidb_client::core),
57 _ => parse_quote!(::bonsaidb_core),
58 },
59 },
60 }
61}
62
63#[derive(Attribute)]
64#[attribute(ident = collection)]
65struct CollectionAttribute {
66 authority: Option<Expr>,
67 #[attribute(example = "\"name\"")]
68 name: String,
69 #[attribute(optional, example = "[SomeView, AnotherView]")]
70 views: Vec<Type>,
71 #[attribute(example = "Format or None")]
72 serialization: Option<Path>,
73 #[attribute(example = "Some(KeyId::Master)")]
74 encryption_key: Option<Expr>,
75 encryption_required: bool,
76 encryption_optional: bool,
77 #[attribute(example = "u64")]
78 primary_key: Option<Type>,
79 #[attribute(example = "self.0 or something(self)")]
80 natural_id: Option<Expr>,
81 #[attribute(example = "bosaidb::core")]
82 core: Option<Path>,
83}
84
85#[manyhow]
88#[proc_macro_derive(Collection, attributes(collection, natural_id))]
89pub fn collection_derive(input: proc_macro::TokenStream) -> Result {
90 let DeriveInput {
91 attrs,
92 ident,
93 generics,
94 data,
95 ..
96 } = parse(input)?;
97
98 let CollectionAttribute {
99 authority,
100 name,
101 views,
102 serialization,
103 mut primary_key,
104 mut natural_id,
105 core,
106 encryption_key,
107 encryption_required,
108 encryption_optional,
109 } = CollectionAttribute::from_attributes(&attrs)?;
110
111 if let Data::Struct(DataStruct { fields, .. }) = data {
112 let mut previous: Option<syn::Attribute> = None;
113 for (
114 idx,
115 Field {
116 attrs, ident, ty, ..
117 },
118 ) in fields.into_iter().enumerate()
119 {
120 if let Some(attr) = attrs
121 .into_iter()
122 .find(|attr| attr.path().is_ident("natural_id"))
123 {
124 if let Some(previous) = &previous {
125 bail!(error_message!(attr,
126 "marked multiple fields as `natural_id`";
127 note="currently only one field can be marked as `natural_id`";
128 help="use `#[collection(natural_id=...)]` on the struct instead")
129 .join(error_message!(previous, "previous `natural_id`")));
130 }
131 if let Some(natural_id) = &natural_id {
132 bail!(error_message!(attr, "field marked as `natural_id` while `natural_id` expression is specified as well";
133 help = "remove `#[natural_id]` attribute on field")
134 .join(error_message!(natural_id, "`natural_id` expression is specified here")));
135 }
136 previous = Some(attr);
137 let ident = if let Some(ident) = ident {
138 quote!(#ident)
139 } else {
140 let idx = Index::from(idx);
141 quote_spanned!(ty.span()=> #idx)
142 };
143 natural_id = Some(parse_quote!(Some(Clone::clone(&self.#ident))));
144 if primary_key.is_none() {
145 primary_key = Some(ty);
146 }
147 }
148 }
149 };
150
151 if encryption_required && encryption_key.is_none() {
152 bail!("If `collection(encryption_required)` is set you need to provide an encryption key via `collection(encryption_key = EncryptionKey)`")
153 }
154
155 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
156
157 let core = core.unwrap_or_else(core_path);
158
159 let primary_key = primary_key.unwrap_or_else(|| parse_quote!(u64));
160
161 let serialization = if matches!(&serialization, Some(serialization) if serialization.is_ident("None"))
162 {
163 if let Some(natural_id) = natural_id {
164 bail!(
165 natural_id,
166 "`natural_id` must be manually implemented when using `serialization = None`"
167 );
168 }
169
170 TokenStream::new()
171 } else {
172 let natural_id = natural_id.map(|natural_id| {
173 quote!(
174 fn natural_id(&self) -> Option<Self::PrimaryKey> {
175 #[allow(clippy::clone_on_copy)]
176 #natural_id
177 }
178 )
179 });
180
181 if let Some(serialization) = serialization {
182 let serialization = if serialization.is_ident("Key") {
183 quote!(#core::key::KeyFormat)
184 } else {
185 quote!(#serialization)
186 };
187 quote! {
188 impl #impl_generics #core::schema::SerializedCollection for #ident #ty_generics #where_clause {
189 type Contents = #ident #ty_generics;
190 type Format = #serialization;
191
192 fn format() -> Self::Format {
193 #serialization::default()
194 }
195
196 #natural_id
197 }
198 }
199 } else {
200 quote! {
201 impl #impl_generics #core::schema::DefaultSerialization for #ident #ty_generics #where_clause {
202 #natural_id
203 }
204 }
205 }
206 };
207
208 let name = authority.map_or_else(
209 || quote!(#core::schema::Qualified::private(#name)),
210 |authority| quote!(#core::schema::Qualified::new(#authority, #name)),
211 );
212
213 let encryption = encryption_key.map(|encryption_key| {
214 let encryption = if encryption_required || !encryption_optional {
215 encryption_key.into_token_stream()
216 } else {
217 quote! {
218 if #core::ENCRYPTION_ENABLED {
219 #encryption_key
220 } else {
221 None
222 }
223 }
224 };
225 quote! {
226 fn encryption_key() -> Option<#core::document::KeyId> {
227 #encryption
228 }
229 }
230 });
231
232 Ok(quote! {
233 impl #impl_generics #core::schema::Collection for #ident #ty_generics #where_clause {
234 type PrimaryKey = #primary_key;
235
236 fn collection_name() -> #core::schema::CollectionName {
237 #name
238 }
239 fn define_views(schema: &mut #core::schema::Schematic) -> Result<(), #core::Error> {
240 #( schema.define_view(#views)?; )*
241 Ok(())
242 }
243 #encryption
244 }
245 #serialization
246 })
247}
248#[manyhow]
253#[proc_macro_derive(View, attributes(view))]
254pub fn view_derive(input: proc_macro::TokenStream) -> Result {
255 view::derive(parse(input)?)
256}
257#[manyhow]
259#[proc_macro_derive(ViewSchema, attributes(view_schema))]
263pub fn view_schema_derive(input: proc_macro::TokenStream) -> Result {
264 view::derive_schema(parse(input)?)
265}
266
267#[derive(Attribute)]
268#[attribute(ident = schema)]
269struct SchemaAttribute {
270 #[attribute(example = "\"name\"")]
271 name: String,
272 #[attribute(example = "\"authority\"")]
273 authority: Option<Expr>,
274 #[attribute(optional, example = "[SomeCollection, AnotherCollection]")]
275 collections: Vec<Type>,
276 #[attribute(optional, example = "[SomeSchema, AnotherSchema]")]
277 include: Vec<Type>,
278 #[attribute(example = "bosaidb::core")]
279 core: Option<Path>,
280}
281
282#[manyhow]
287#[proc_macro_derive(Schema, attributes(schema))]
288pub fn schema_derive(input: proc_macro::TokenStream) -> Result {
289 let DeriveInput {
290 attrs,
291 ident,
292 generics,
293 ..
294 } = parse(input)?;
295
296 let SchemaAttribute {
297 name,
298 authority,
299 collections,
300 include,
301 core,
302 } = SchemaAttribute::from_attributes(&attrs)?;
303
304 let core = core.unwrap_or_else(core_path);
305 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
306
307 let name = authority.map_or_else(
308 || quote!(#core::schema::Qualified::private(#name)),
309 |authority| quote!(#core::schema::Qualified::new(#authority, #name)),
310 );
311
312 Ok(quote! {
313 impl #impl_generics #core::schema::Schema for #ident #ty_generics #where_clause {
314 fn schema_name() -> #core::schema::SchemaName {
315 #name
316 }
317
318 fn define_collections(
319 schema: &mut #core::schema::Schematic
320 ) -> Result<(), #core::Error> {
321 #( schema.define_collection::<#collections>()?; )*
322
323 #( <#include as #core::schema::Schema>::define_collections(schema)?; )*
324
325 Ok(())
326 }
327 }
328 })
329}
330
331#[derive(Attribute)]
332#[attribute(ident = key)]
333struct KeyAttribute {
334 #[attribute(example = "bosaidb::core")]
335 core: Option<Path>,
336 #[attribute(default = NullHandling::Escape, example = "escape")]
337 null_handling: NullHandling,
338 can_own_bytes: bool,
339 #[attribute(example = "u8")]
340 enum_repr: Option<Type>,
341 #[attribute(example = "\"name\"")]
342 name: Option<String>,
343}
344
345enum NullHandling {
346 Escape,
347 Allow,
348 Deny,
349}
350
351impl ConvertParsed for NullHandling {
352 type Type = Ident;
353
354 fn convert(value: Self::Type) -> syn::Result<Self> {
355 if value == "escape" {
356 Ok(NullHandling::Escape)
357 } else if value == "allow" {
358 Ok(NullHandling::Allow)
359 } else if value == "deny" {
360 Ok(NullHandling::Deny)
361 } else {
362 Err(syn::Error::new(
363 Span::call_site(),
364 "only `escape`, `allow`, and `deny` are allowed for `null_handling`",
365 ))
366 }
367 }
368}
369
370#[manyhow]
374#[proc_macro_derive(Key, attributes(key))]
375pub fn key_derive(input: proc_macro::TokenStream) -> Result {
376 let DeriveInput {
377 attrs,
378 ident,
379 generics,
380 data,
381 ..
382 } = parse(input)?;
383
384 let repr = attrs.iter().find_map(|attr| {
386 attr.path()
387 .is_ident("repr")
388 .then(|| attr.parse_args::<Ident>().ok())
389 .flatten()
390 .and_then(|ident| {
391 matches!(
392 ident.to_string().as_ref(),
393 "u8" | "u16"
394 | "u32"
395 | "u64"
396 | "u128"
397 | "usize"
398 | "i8"
399 | "i16"
400 | "i32"
401 | "i64"
402 | "i128"
403 | "isize"
404 )
405 .then(|| ident)
406 })
407 });
408
409 let KeyAttribute {
410 core,
411 null_handling,
412 enum_repr,
413 can_own_bytes,
414 name,
415 } = KeyAttribute::from_attributes(&attrs)?;
416
417 let name = name.map_or_else(
418 || quote!(std::any::type_name::<Self>()),
419 |name| quote!(#name),
420 );
421
422 if matches!(data, Data::Struct(_)) && enum_repr.is_some() {
423 bail!(enum_repr, "`enum_repr` is only usable with enums")
425 }
426
427 let repr: Type = enum_repr.unwrap_or_else(|| {
428 Type::Path(TypePath {
429 qself: None,
430 path: repr.unwrap_or_else(|| format_ident!("isize")).into(),
431 })
432 });
433
434 let (encoder_constructor, decoder_constructor) = match null_handling {
435 NullHandling::Escape => (quote!(default), quote!(default_for)),
436 NullHandling::Allow => (quote!(allowing_null_bytes), quote!(allowing_null_bytes)),
437 NullHandling::Deny => (quote!(denying_null_bytes), quote!(denying_null_bytes)),
438 };
439
440 let core = core.unwrap_or_else(core_path);
441 let (_, ty_generics, _) = generics.split_for_impl();
442 let mut generics = generics.clone();
443 let lifetimes: Vec<_> = generics.lifetimes().cloned().collect();
444 let where_clause = generics.make_where_clause();
445 for lifetime in lifetimes {
446 where_clause.predicates.push(parse_quote!($'key: #lifetime));
447 }
448 generics
449 .params
450 .push(syn::GenericParam::Lifetime(parse_quote!($'key)));
451 let (impl_generics, _, where_clause) = generics.split_for_impl();
452
453 if let Some((name, ty, map)) = match &data {
458 Data::Struct(DataStruct {
459 fields: Fields::Named(FieldsNamed { named, .. }),
460 ..
461 }) if named.len() == 1 => {
462 let name = &named[0].ident;
463 Some((
464 quote!(#name),
465 named[0].ty.clone(),
466 quote!(|value| Self { #name: value }),
467 ))
468 }
469 Data::Struct(DataStruct {
470 fields: Fields::Unnamed(FieldsUnnamed { unnamed, .. }),
471 ..
472 }) if unnamed.len() == 1 => Some((quote!(0), unnamed[0].ty.clone(), quote!(Self))),
473 _ => None,
474 } {
475 return Ok(quote! {
476 # use std::{borrow::Cow, io::{self, ErrorKind}};
477 # use #core::key::{ByteSource, KeyVisitor, IncorrectByteLength, Key, KeyEncoding};
478
479 impl #impl_generics Key<$'key> for #ident #ty_generics #where_clause {
480 const CAN_OWN_BYTES: bool = <#ty>::CAN_OWN_BYTES;
481
482 fn from_ord_bytes<$'b>(bytes: ByteSource<$'key, $'b>) -> Result<Self, Self::Error> {
483 <#ty>::from_ord_bytes(bytes).map(#map)
484 }
485 }
486
487 impl #impl_generics KeyEncoding<Self> for #ident #ty_generics #where_clause {
488 type Error = <#ty as KeyEncoding>::Error;
489
490 const LENGTH: Option<usize> = <#ty>::LENGTH;
491
492 fn describe<Visitor>(visitor: &mut Visitor)
493 where
494 Visitor: KeyVisitor,
495 {
496 <#ty>::describe(visitor)
497 }
498
499 fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
500 self.#name.as_ord_bytes()
501 }
502 }
503 });
504 }
505
506 let (encode_fields, decode_fields, describe, composite_kind, field_count): (
507 TokenStream,
508 TokenStream,
509 TokenStream,
510 TokenStream,
511 usize,
512 ) = match data {
513 Data::Struct(DataStruct { fields, .. }) => {
514 let (encode_fields, decode_fields, describe, field_count) = match fields {
515 Fields::Named(FieldsNamed { named, .. }) => {
516 let field_count = named.len();
517 let (encode_fields, (decode_fields, describe)): (
518 TokenStream,
519 (TokenStream, TokenStream),
520 ) = named
521 .into_iter()
522 .map(|Field { ident, ty, .. }| {
523 let ident = ident.expect("named fields have idents");
524 (
525 quote!($encoder.encode(&self.#ident)?;),
526 (
527 quote!(#ident: $decoder.decode()?,),
528 quote!(<#ty>::describe(visitor);),
529 ),
530 )
531 })
532 .unzip();
533 (
534 encode_fields,
535 quote!( Self { #decode_fields }),
536 describe,
537 field_count,
538 )
539 }
540 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
541 let field_count = unnamed.len();
542 let (encode_fields, (decode_fields, describe)): (
543 TokenStream,
544 (TokenStream, TokenStream),
545 ) = unnamed
546 .into_iter()
547 .enumerate()
548 .map(|(idx, field)| {
549 let ty = field.ty;
550 let idx = Index::from(idx);
551 (
552 quote!($encoder.encode(&self.#idx)?;),
553 (
554 quote!($decoder.decode()?,),
555 quote!(<#ty>::describe(visitor);),
556 ),
557 )
558 })
559 .unzip();
560 (
561 encode_fields,
562 quote!(Self(#decode_fields)),
563 describe,
564 field_count,
565 )
566 }
567 Fields::Unit => {
568 return Ok(quote! {
569 # use std::{borrow::Cow, io::{self, ErrorKind}};
570 # use #core::key::{ByteSource, KeyVisitor, IncorrectByteLength, Key, KeyKind, KeyEncoding};
571
572 impl #impl_generics Key<$'key> for #ident #ty_generics #where_clause {
573 const CAN_OWN_BYTES: bool = false;
574
575 fn from_ord_bytes<$'b>(bytes: ByteSource<$'key, $'b>) -> Result<Self, Self::Error> {
576 Ok(Self)
577 }
578 }
579
580 impl #impl_generics KeyEncoding<Self> for #ident #ty_generics #where_clause {
581 type Error = std::convert::Infallible;
582
583 const LENGTH: Option<usize> = Some(0);
584
585 fn describe<Visitor>(visitor: &mut Visitor)
586 where
587 Visitor: KeyVisitor,
588 {
589 visitor.visit_type(KeyKind::Unit);
590 }
591
592 fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
593 Ok(Cow::Borrowed(&[]))
594 }
595 }
596 })
597 }
598 };
599 (
600 encode_fields,
601 quote!(let $self_ = #decode_fields;),
602 describe,
603 quote!(#core::key::CompositeKind::Struct(std::borrow::Cow::Borrowed(#name))),
604 field_count,
605 )
606 }
607 Data::Enum(DataEnum { variants, .. }) => {
608 let mut prev_ident = None;
609 let field_count = variants.len();
610 let all_variants_are_empty = variants.iter().all(|variant| variant.fields.is_empty());
611
612 let (consts, (encode_variants, (decode_variants, describe))): (
613 TokenStream,
614 (TokenStream, (TokenStream, TokenStream)),
615 ) = variants
616 .into_iter()
617 .enumerate()
618 .map(
619 |(
620 idx,
621 Variant {
622 fields,
623 ident,
624 discriminant,
625 ..
626 },
627 )| {
628 let discriminant = discriminant.map_or_else(
629 || {
630 prev_ident
631 .as_ref()
632 .map_or_else(|| quote!(0), |ident| quote!(#ident + 1))
633 },
634 |(_, expr)| expr.to_token_stream(),
635 );
636
637 let const_ident = format_ident!("$discriminant{idx}");
638 let const_ = quote!(const #const_ident: #repr = #discriminant;);
639
640 let ret = (
641 const_,
642 match fields {
643 Fields::Named(FieldsNamed { named, .. }) => {
644 let (idents, (encode_fields, (decode_fields, describe))): (
645 Punctuated<_, Token![,]>,
646 (TokenStream, (TokenStream, TokenStream)),
647 ) = named
648 .into_iter()
649 .map(|Field { ident, ty, .. }| {
650 let ident = ident.expect("named fields have idents");
651 (
652 ident.clone(),
653 (
654 quote!($encoder.encode(#ident)?;),
655 (
656 quote!(#ident: $decoder.decode()?,),
657 quote!(<#ty>::describe(visitor);),
658 ),
659 ),
660 )
661 })
662 .unzip();
663 (
664 quote! {
665 Self::#ident{#idents} => {
666 $encoder.encode(&#const_ident)?;
667 #encode_fields
668 },
669 },
670 (
671 quote! {
672 #const_ident => Self::#ident{#decode_fields},
673 },
674 describe,
675 ),
676 )
677 }
678 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
679 let (idents, (encode_fields, (decode_fields, describe))): (
680 Punctuated<_, Token![,]>,
681 (TokenStream, (TokenStream, TokenStream)),
682 ) = unnamed
683 .into_iter()
684 .enumerate()
685 .map(|(idx, field)| {
686 let ident = format_ident!("$field_{idx}");
687 let ty = field.ty;
688 (
689 ident.clone(),
690 (
691 quote!($encoder.encode(#ident)?;),
692 (
693 quote!($decoder.decode()?,),
694 quote!(<#ty>::describe(visitor);),
695 ),
696 ),
697 )
698 })
699 .unzip();
700 (
701 quote! {
702 Self::#ident(#idents) => {
703 $encoder.encode(&#const_ident)?;
704 #encode_fields
705 },
706 },
707 (
708 quote! {
709 #const_ident => Self::#ident(#decode_fields),
710 },
711 describe,
712 ),
713 )
714 }
715 Fields::Unit => {
716 let encode = if all_variants_are_empty {
717 quote!(Self::#ident => #const_ident.as_ord_bytes(),)
718 } else {
719 quote!(Self::#ident => $encoder.encode(&#const_ident)?,)
720 };
721 (
722 encode,
723 (
724 quote!(#const_ident => Self::#ident,),
725 quote!(visitor.visit_type(#core::key::KeyKind::Unit);),
726 ),
727 )
728 }
729 },
730 );
731 prev_ident = Some(const_ident);
732 ret
733 },
734 )
735 .unzip();
736
737 if all_variants_are_empty {
738 return Ok(quote! {
742 # use std::{borrow::Cow, io::{self, ErrorKind}};
743 # use #core::key::{ByteSource, CompositeKeyDecoder, KeyVisitor, CompositeKeyEncoder, CompositeKeyError, Key, KeyEncoding};
744
745 impl #impl_generics Key<$'key> for #ident #ty_generics #where_clause {
746 const CAN_OWN_BYTES: bool = false;
747
748 fn from_ord_bytes<$'b>(mut $bytes: ByteSource<$'key, $'b>) -> Result<Self, Self::Error> {
749 #consts
750 Ok(match <#repr>::from_ord_bytes($bytes).map_err(#core::key::CompositeKeyError::new)? {
751 #decode_variants
752 _ => return Err(#core::key::CompositeKeyError::from(io::Error::from(
753 ErrorKind::InvalidData,
754 )))
755 })
756 }
757 }
758
759 impl #impl_generics KeyEncoding<Self> for #ident #ty_generics #where_clause {
760 type Error = CompositeKeyError;
761
762 const LENGTH: Option<usize> = <#repr as KeyEncoding>::LENGTH;
763
764 fn describe<Visitor>(visitor: &mut Visitor)
765 where
766 Visitor: KeyVisitor,
767 {
768 <#repr>::describe(visitor);
769 }
770
771 fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
772 #consts
773 match self {
774 #encode_variants
775 }.map_err(#core::key::CompositeKeyError::new)
776 }
777 }
778 });
779 }
780
781 (
783 quote! {
784 #consts
785 match self{
786 #encode_variants
787 }
788 },
789 quote! {
790 # use std::io::{self, ErrorKind};
791 #consts
792 let $self_ = match $decoder.decode::<#repr>()? {
793 #decode_variants
794 _ => return Err(#core::key::CompositeKeyError::from(io::Error::from(
795 ErrorKind::InvalidData,
796 )))
797 };
798 },
799 describe,
800 quote!(#core::key::CompositeKind::Tuple),
801 field_count,
802 )
803 }
804 Data::Union(_) => bail!("unions are not supported"),
805 };
806
807 Ok(quote! {
808 # use std::{borrow::Cow, io::{self, ErrorKind}};
809 # use #core::key::{ByteSource, CompositeKeyDecoder, KeyVisitor, CompositeKeyEncoder, CompositeKeyError, Key, KeyEncoding};
810
811 impl #impl_generics Key<$'key> for #ident #ty_generics #where_clause {
812 const CAN_OWN_BYTES: bool = #can_own_bytes;
813
814 fn from_ord_bytes<$'b>(mut $bytes: ByteSource<$'key, $'b>) -> Result<Self, Self::Error> {
815
816 let mut $decoder = CompositeKeyDecoder::#decoder_constructor($bytes);
817
818 #decode_fields
819
820 $decoder.finish()?;
821
822 Ok($self_)
823 }
824 }
825
826 impl #impl_generics KeyEncoding<Self> for #ident #ty_generics #where_clause {
827 type Error = CompositeKeyError;
828
829 const LENGTH: Option<usize> = None;
831
832 fn describe<Visitor>(visitor: &mut Visitor)
833 where
834 Visitor: KeyVisitor,
835 {
836 visitor.visit_composite(#composite_kind, #field_count);
837 #describe
838 }
839
840 fn as_ord_bytes(&self) -> Result<Cow<'_, [u8]>, Self::Error> {
841 let mut $encoder = CompositeKeyEncoder::#encoder_constructor();
842
843 #encode_fields
844
845 Ok(Cow::Owned($encoder.finish()))
846 }
847 }
848 })
849}
850
851#[derive(Attribute)]
852#[attribute(ident = api)]
853struct ApiAttribute {
854 #[attribute(example = "\"name\"")]
855 name: String,
856 #[attribute(example = "\"authority\"")]
857 authority: Option<Expr>,
858 #[attribute(example = "ResponseType")]
859 response: Option<Type>,
860 #[attribute(example = "ErrorType")]
861 error: Option<Type>,
862 #[attribute(example = "bosaidb::core")]
863 core: Option<Path>,
864}
865
866#[manyhow]
871#[proc_macro_derive(Api, attributes(api))]
872pub fn api_derive(input: proc_macro::TokenStream) -> Result {
873 let DeriveInput {
874 attrs,
875 ident,
876 generics,
877 ..
878 } = parse(input)?;
879
880 let ApiAttribute {
881 name,
882 authority,
883 response,
884 error,
885 core,
886 } = ApiAttribute::from_attributes(&attrs)?;
887
888 let core = core.unwrap_or_else(core_path);
889 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
890
891 let name = authority.map_or_else(
892 || quote!(#core::schema::Qualified::private(#name)),
893 |authority| quote!(#core::schema::Qualified::new(#authority, #name)),
894 );
895
896 let response = response.unwrap_or_else(|| parse_quote!(()));
897 let error = error.unwrap_or_else(|| parse_quote!(#core::api::Infallible));
898
899 Ok(quote! {
900 # use #core::api::{Api, ApiName};
901
902 impl #impl_generics Api for #ident #ty_generics #where_clause {
903 type Response = #response;
904 type Error = #error;
905
906 fn name() -> ApiName {
907 #name
908 }
909 }
910 })
911}
912
913fn files_path() -> Path {
918 match crate_name("bonsaidb") {
919 Ok(FoundCrate::Name(name)) => {
920 let ident = Ident::new(&name, Span::call_site());
921 parse_quote!(::#ident::files)
922 }
923 Ok(FoundCrate::Itself) => parse_quote!(crate::files),
924 Err(_) => match crate_name("bonsaidb_files") {
925 Ok(FoundCrate::Name(name)) => {
926 let ident = Ident::new(&name, Span::call_site());
927 parse_quote!(::#ident)
928 }
929 Ok(FoundCrate::Itself) => parse_quote!(crate),
930 Err(_) if cfg!(feature = "omnibus-path") => parse_quote!(::bonsaidb::files),
931 Err(_) => parse_quote!(::bonsaidb_core),
932 },
933 }
934}
935
936#[derive(Attribute)]
937#[attribute(ident = file_config)]
938struct FileConfigAttribute {
939 #[attribute(example = "MetadataType")]
940 metadata: Option<Type>,
941 #[attribute(example = "65_536")]
942 block_size: Option<usize>,
943 #[attribute(example = "\"authority\"")]
944 authority: Option<Expr>,
945 #[attribute(example = "\"files\"")]
946 files_name: Option<String>,
947 #[attribute(example = "\"blocks\"")]
948 blocks_name: Option<String>,
949 #[attribute(example = "bosaidb::core")]
950 core: Option<Path>,
951 #[attribute(example = "bosaidb::files")]
952 files: Option<Path>,
953}
954
955#[manyhow]
960#[proc_macro_derive(FileConfig, attributes(file_config))]
961pub fn file_config_derive(input: proc_macro::TokenStream) -> Result {
962 let DeriveInput {
963 attrs,
964 ident,
965 generics,
966 ..
967 } = parse(input)?;
968
969 let FileConfigAttribute {
970 metadata,
971 block_size,
972 authority,
973 files_name,
974 blocks_name,
975 core,
976 files,
977 } = FileConfigAttribute::from_attributes(&attrs)?;
978
979 let core = core.unwrap_or_else(core_path);
980 let files = files.unwrap_or_else(files_path);
981 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
982
983 let (files_name, blocks_name) = match (authority, files_name, blocks_name) {
984 (None, None, None) => (
985 quote!(#files::BonsaiFiles::files_name()),
986 quote!(#files::BonsaiFiles::blocks_name()),
987 ),
988 (Some(authority), Some(files_name), Some(blocks_name)) => (
989 quote!(#core::schema::Qualified::new(#authority, #files_name)),
990 quote!(#core::schema::Qualified::new(#authority, #blocks_name)),
991 ),
992 (None, Some(files_name), Some(blocks_name)) => (
993 quote!(#core::schema::Qualified::private(#files_name)),
994 quote!(#core::schema::Qualified::private(#blocks_name)),
995 ),
996 (Some(_), ..) => bail!(
997 "if `authority` is specified, `files_name` and `blocks_name need to be provided as well"
998 ),
999 (_, Some(_), _) => {
1000 bail!("if `files_name` is specified, `blocks_name` needs to be provided as well")
1001 }
1002 (_, _, Some(_)) => {
1003 bail!("if `blocks_name` is specified, `files_name` needs to be provided as well")
1004 }
1005 };
1006
1007 let metadata = metadata
1008 .unwrap_or_else(|| parse_quote!(<#files::BonsaiFiles as #files::FileConfig>::Metadata));
1009 let block_size = block_size.map_or_else(
1010 || quote!(<#files::BonsaiFiles as #files::FileConfig>::BLOCK_SIZE),
1011 |block_size| quote!(#block_size),
1012 );
1013
1014 Ok(quote! {
1015 # use #files::FileConfig;
1016 # use #core::schema::CollectionName;
1017
1018 impl #impl_generics FileConfig for #ident #ty_generics #where_clause {
1019 type Metadata = #metadata;
1020 const BLOCK_SIZE: usize = #block_size;
1021
1022 fn files_name() -> CollectionName {
1023 #files_name
1024 }
1025
1026 fn blocks_name() -> CollectionName {
1027 #blocks_name
1028 }
1029 }
1030 })
1031}
1032
1033#[test]
1034fn ui() {
1035 use trybuild::TestCases;
1036
1037 TestCases::new().compile_fail("tests/ui/*/*.rs");
1038}