es-entity-macros 0.10.36

Proc macros for es-entity
Documentation
mod begin;
mod combo_cursor;
mod create_all_fn;
mod create_fn;
mod delete_fn;
mod error_types;
mod find_all_fn;
mod find_by_fn;
mod list_by_fn;
mod list_for_filters_fn;
mod list_for_fn;
mod nested;
mod options;
mod persist_events_batch_fn;
mod persist_events_fn;
mod populate_nested;
mod post_hydrate_hook;
mod post_persist_hook;
mod update_all_fn;
mod update_fn;

use darling::{FromDeriveInput, ToTokens};
use proc_macro2::TokenStream;
use quote::{TokenStreamExt, quote};

use options::RepositoryOptions;

pub fn derive(ast: syn::DeriveInput) -> darling::Result<proc_macro2::TokenStream> {
    let opts = RepositoryOptions::from_derive_input(&ast)?;
    opts.columns.validate_list_for_by_columns()?;
    let repo = EsRepo::from(&opts);
    Ok(quote!(#repo))
}
pub struct EsRepo<'a> {
    repo: &'a syn::Ident,
    generics: &'a syn::Generics,
    persist_events_fn: persist_events_fn::PersistEventsFn<'a>,
    persist_events_batch_fn: persist_events_batch_fn::PersistEventsBatchFn<'a>,
    update_fn: update_fn::UpdateFn<'a>,
    update_all_fn: update_all_fn::UpdateAllFn<'a>,
    create_fn: create_fn::CreateFn<'a>,
    create_all_fn: create_all_fn::CreateAllFn<'a>,
    delete_fn: delete_fn::DeleteFn<'a>,
    find_by_fns: Vec<find_by_fn::FindByFn<'a>>,
    find_all_fn: find_all_fn::FindAllFn<'a>,
    post_hydrate_hook: post_hydrate_hook::PostHydrateHook<'a>,
    post_persist_hook: post_persist_hook::PostPersistHook<'a>,
    begin: begin::Begin<'a>,
    list_by_fns: Vec<list_by_fn::ListByFn<'a>>,
    list_for_fns: Vec<list_for_fn::ListForFn<'a>>,
    nested_fns: Vec<syn::Ident>,
    nested_include_deleted_fns: Vec<syn::Ident>,
    nested: Vec<nested::Nested<'a>>,
    populate_nested: Option<populate_nested::PopulateNested<'a>>,
    error_types: error_types::ErrorTypes<'a>,
    opts: &'a RepositoryOptions,
}

impl<'a> From<&'a RepositoryOptions> for EsRepo<'a> {
    fn from(opts: &'a RepositoryOptions) -> Self {
        let find_by_fns = opts
            .columns
            .all_find_by()
            .map(|c| find_by_fn::FindByFn::new(c, opts))
            .collect();
        let list_by_fns = opts
            .columns
            .all_list_by()
            .map(|c| list_by_fn::ListByFn::new(c, opts))
            .collect();
        let list_for_fns = opts
            .columns
            .all_list_for()
            .flat_map(|for_col| {
                for_col
                    .list_for_by_columns()
                    .iter()
                    .filter_map(|by_name| {
                        opts.columns
                            .find_list_by(by_name)
                            .map(|by_col| list_for_fn::ListForFn::new(for_col, by_col, opts))
                    })
                    .collect::<Vec<_>>()
            })
            .collect();
        let populate_nested = opts
            .columns
            .parent()
            .map(|c| populate_nested::PopulateNested::new(c, opts));
        let nested_include_deleted_fns: Vec<_> = opts
            .all_nested()
            .map(|n| n.find_nested_include_deleted_fn_name())
            .collect();
        let (nested_fns, nested): (Vec<_>, Vec<_>) = opts
            .all_nested()
            .map(|n| (n.find_nested_fn_name(), nested::Nested::new(n, opts)))
            .unzip();

        Self {
            repo: &opts.ident,
            generics: &opts.generics,
            persist_events_fn: persist_events_fn::PersistEventsFn::from(opts),
            persist_events_batch_fn: persist_events_batch_fn::PersistEventsBatchFn::from(opts),
            update_fn: update_fn::UpdateFn::from(opts),
            update_all_fn: update_all_fn::UpdateAllFn::from(opts),
            create_fn: create_fn::CreateFn::from(opts),
            create_all_fn: create_all_fn::CreateAllFn::from(opts),
            delete_fn: delete_fn::DeleteFn::from(opts),
            find_by_fns,
            find_all_fn: find_all_fn::FindAllFn::from(opts),
            post_hydrate_hook: post_hydrate_hook::PostHydrateHook::from(opts),
            post_persist_hook: post_persist_hook::PostPersistHook::from(opts),
            begin: begin::Begin::from(opts),
            list_by_fns,
            list_for_fns,
            nested_fns,
            nested_include_deleted_fns,
            nested,
            populate_nested,
            error_types: error_types::ErrorTypes::new(opts),
            opts,
        }
    }
}

impl ToTokens for EsRepo<'_> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let repo = &self.repo;
        let persist_events_fn = &self.persist_events_fn;
        let persist_events_batch_fn = &self.persist_events_batch_fn;
        let update_fn = &self.update_fn;
        let update_all_fn = &self.update_all_fn;
        let create_fn = &self.create_fn;
        let create_all_fn = &self.create_all_fn;
        let delete_fn = &self.delete_fn;
        let find_by_fns = &self.find_by_fns;
        let find_all_fn = &self.find_all_fn;
        let post_hydrate_hook = &self.post_hydrate_hook;
        let post_persist_hook = &self.post_persist_hook;
        let begin = &self.begin;
        let cursors = self.list_by_fns.iter().map(|l| l.cursor());
        let combo_cursor = combo_cursor::ComboCursor::new(
            self.opts,
            self.list_by_fns.iter().map(|l| l.cursor()).collect(),
        );
        let sort_by = combo_cursor.sort_by();
        let list_for_filters = list_for_filters_fn::ListForFiltersFn::new(
            self.opts,
            self.opts.columns.all_list_for().collect(),
            self.opts.columns.all_list_by().collect(),
            &combo_cursor,
        );
        let list_for_filters_struct = &list_for_filters.filters_struct;
        #[cfg(feature = "graphql")]
        let gql_combo_cursor = combo_cursor.gql_cursor();
        #[cfg(not(feature = "graphql"))]
        let gql_combo_cursor = TokenStream::new();
        #[cfg(feature = "graphql")]
        let gql_cursors: Vec<_> = self
            .list_by_fns
            .iter()
            .map(|l| l.cursor().gql_cursor())
            .collect();
        #[cfg(not(feature = "graphql"))]
        let gql_cursors: Vec<TokenStream> = Vec::new();
        let list_by_fns = &self.list_by_fns;
        let list_for_fns = &self.list_for_fns;

        let entity = self.opts.entity();
        let event = self.opts.event();
        let id = self.opts.id();

        let cursor_mod = self.opts.cursor_mod();
        let types_mod = self.opts.repo_types_mod();

        let nested_fns = &self.nested_fns;
        let nested_include_deleted_fns = &self.nested_include_deleted_fns;
        let nested = &self.nested;
        let populate_nested = &self.populate_nested;

        let pool_field = self.opts.pool_field();
        let es_query_flavor = if nested_fns.is_empty() {
            quote! {
                es_entity::EsQueryFlavorFlat
            }
        } else {
            quote! { es_entity::EsQueryFlavorNested }
        };

        let create_error = self.opts.create_error();
        let modify_error = self.opts.modify_error();
        let find_error = self.opts.find_error();
        let query_error = self.opts.query_error();
        let error_types = self.error_types.generate();
        let map_constraint_fn = self.error_types.generate_map_constraint_fn();

        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();

        tokens.append_all(quote! {
            pub mod #cursor_mod {
                use super::*;

                #(#cursors)*
                #(#gql_cursors)*

                #combo_cursor
                #gql_combo_cursor
            }

            mod #types_mod {

                use super::*;

                #[allow(non_camel_case_types)]
                pub(super) type Repo__Id = #id;
                #[allow(non_camel_case_types)]
                pub(super) type Repo__Event = #event;
                #[allow(non_camel_case_types)]
                pub(super) type Repo__Entity = #entity;
                #[allow(non_camel_case_types)]
                pub(super) type Repo__DbEvent = es_entity::GenericEvent<#id>;
            }

            #error_types

            #list_for_filters_struct
            #sort_by

             impl #impl_generics #repo #ty_generics #where_clause {
                #[inline(always)]
                pub fn pool(&self) -> &es_entity::db::Pool {
                    &self.#pool_field
                }

                #map_constraint_fn
                #begin
                #post_hydrate_hook
                #post_persist_hook
                #persist_events_fn
                #persist_events_batch_fn
                #create_fn
                #create_all_fn
                #update_fn
                #update_all_fn
                #delete_fn
                #(#find_by_fns)*
                #find_all_fn
                #list_for_filters
                #(#list_by_fns)*
                #(#list_for_fns)*
                #(#nested)*
            }

            #populate_nested

            impl #impl_generics es_entity::EsRepo for #repo #ty_generics #where_clause {
                type Entity = #entity;
                type CreateError = #create_error;
                type ModifyError = #modify_error;
                type FindError = #find_error;
                type QueryError = #query_error;
                type EsQueryFlavor = #es_query_flavor;

               #[inline(always)]
               async fn load_all_nested_in_op<OP, __EsErr>(
                   op: &mut OP, entities: &mut [#entity]
               ) -> Result<(), __EsErr>
                   where
                       OP: es_entity::AtomicOperation,
                       __EsErr: From<sqlx::Error> + From<es_entity::EntityHydrationError> + Send,
               {
                   #(Self::#nested_fns::<_, _, __EsErr>(op, entities).await?;)*
                   Ok(())
               }

               #[inline(always)]
               async fn load_all_nested_in_op_include_deleted<OP, __EsErr>(
                   op: &mut OP, entities: &mut [#entity]
               ) -> Result<(), __EsErr>
                   where
                       OP: es_entity::AtomicOperation,
                       __EsErr: From<sqlx::Error> + From<es_entity::EntityHydrationError> + Send,
               {
                   #(Self::#nested_include_deleted_fns::<_, _, __EsErr>(op, entities).await?;)*
                   Ok(())
               }
            }
        });
    }
}