use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
pub struct LoaderRelation<'a> {
pub field_name: &'a Ident,
pub target: &'a Ident,
pub kind: LoaderKind,
}
pub enum LoaderKind {
HasMany,
HasOne,
}
pub fn emit(model_name: &Ident, rels: &[LoaderRelation<'_>]) -> TokenStream {
let module_name = format_ident!("{}", model_name.to_string().to_case(Case::Snake));
let arms = rels.iter().map(|r| {
let fname = r.field_name;
let fname_str = r.field_name.to_string();
let target = r.target;
match r.kind {
LoaderKind::HasMany | LoaderKind::HasOne => quote! {
#fname_str => {
let bucketed = ::prax_query::relations::executor::load_has_many::<
E,
#model_name,
#target,
#module_name::#fname::Relation,
>(engine, parents).await?;
for p in parents.iter_mut() {
use ::prax_query::traits::ModelWithPk as _;
let key = ::prax_query::relations::executor::filter_value_key_public(&p.pk_value());
if let Some(children) = bucketed.get(&key) {
p.#fname = children.clone();
}
}
Ok(())
}
},
}
});
quote! {
impl<E: ::prax_query::traits::QueryEngine>
::prax_query::traits::ModelRelationLoader<E> for #model_name
{
fn load_relation<'a>(
engine: &'a E,
parents: &'a mut [Self],
spec: &'a ::prax_query::relations::IncludeSpec,
) -> ::prax_query::traits::BoxFuture<'a, ::prax_query::error::QueryResult<()>>
{
Box::pin(async move {
match spec.relation_name.as_str() {
#(#arms)*
_ => Err(::prax_query::error::QueryError::internal(format!(
"unknown relation '{}' on {}",
spec.relation_name,
stringify!(#model_name),
))),
}
})
}
}
}
}