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(())
}
}
});
}
}