bonsaidb_macros/
lib.rs

1//! Macros BonsaiDb.
2
3#![forbid(unsafe_code)]
4#![warn(
5    clippy::cargo,
6    missing_docs,
7    // clippy::missing_docs_in_private_items,
8    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
31// -----------------------------------------------------------------------------
32//     - Core Macros -
33// -----------------------------------------------------------------------------
34
35fn 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/// Derives the `bonsaidb::core::schema::Collection` trait.
86/// `#[collection(authority = "Authority", name = "Name", views = [a, b, c])]`
87#[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/// Derives the `bonsaidb::core::schema::View` trait.
249///
250/// `#[view(collection=CollectionType, key=KeyType, value=ValueType, name = "by-name")]`
251/// `name` and `value` are optional
252#[manyhow]
253#[proc_macro_derive(View, attributes(view))]
254pub fn view_derive(input: proc_macro::TokenStream) -> Result {
255    view::derive(parse(input)?)
256}
257/// Derives the `bonsaidb::core::schema::ViewSchema` trait.
258#[manyhow]
259/// `#[view_schema(version = 1, policy = Unique, view=ViewType, mapped_key=KeyType<'doc>)]`
260///
261/// All attributes are optional.
262#[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/// Derives the `bonsaidb::core::schema::Schema` trait.
283///
284/// `#[schema(name = "Name", authority = "Authority", collections = [A, B, C]), core = bonsaidb::core]`
285/// `authority`, `collections` and `core` are optional
286#[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/// Derives the `bonsaidb::core::key::Key` trait.
371///
372/// `#[key(null_handling = escape, enum_repr = u8, core = bonsaidb::core)]`, all parameters are optional
373#[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    // Only relevant if it is an enum, gets the representation to use for the variant key
385    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        // TODO better span when attribute-derive supports that
424        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    // Special case the implementation for 1
454    // field -- just pass through to the
455    // inner type so that this encoding is
456    // completely transparent.
457    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                // Special case: if no enum variants have embedded values,
739                // implement Key as a plain value, avoiding the composite key
740                // overhead.
741                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            // At least one variant has a value, which means we need to encode a composite field.
782            (
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            // TODO fixed width if possible
830            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/// Derives the `bonsaidb::core::api::Api` trait.
867///
868/// `#[api(name = "Name", authority = "Authority", response = ResponseType, error = ErrorType, core = bonsaidb::core)]`
869/// `authority`, `response`, `error` and `core` are optional
870#[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
913// -----------------------------------------------------------------------------
914//     - File Macros -
915// -----------------------------------------------------------------------------
916
917fn 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/// Derives the `bonsaidb::files::FileConfig` trait.
956///
957/// `#[api(metadata = MetadataType, block_size = 65_536, authority = "authority", files_name = "files", blocks_name = "blocks", core = bonsaidb::core, files = bosaidb::files)]`
958/// all arguments are optional
959#[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}