use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
use super::{FilterCategory, filter_wrapper_ident};
use crate::generators::pascal_ident;
pub struct WhereField {
pub name: Ident,
pub column: String,
pub category: Option<FilterCategory>,
pub nullable: bool,
pub relation_target_where_input: Option<Ident>,
pub is_to_many: bool,
}
pub struct WhereInputTokens {
pub struct_tokens: TokenStream,
pub impl_tokens: TokenStream,
}
pub fn generate(
model_ident: &Ident,
module_name: &Ident,
fields: &[WhereField],
) -> WhereInputTokens {
let where_input_ident = format_ident!("{}WhereInput", model_ident);
let scalar_field_decls: Vec<TokenStream> = fields
.iter()
.filter(|f| f.category.is_some())
.map(|f| {
let name = &f.name;
let cat = f
.category
.expect("category is_some() was checked by the preceding filter");
let wrapper = filter_wrapper_ident(cat, f.nullable);
quote! {
pub #name: ::core::option::Option<::prax_query::inputs::#wrapper>
}
})
.collect();
let relation_field_decls: Vec<TokenStream> = fields
.iter()
.filter(|f| f.relation_target_where_input.is_some())
.map(|f| {
let name = &f.name;
let target = f.relation_target_where_input.as_ref().expect("relation");
if f.is_to_many {
quote! {
pub #name: ::core::option::Option<
::prax_query::inputs::ListRelationFilter<#target>
>
}
} else {
quote! {
pub #name: ::core::option::Option<
::prax_query::inputs::SingleRelationFilter<#target>
>
}
}
})
.collect();
let scalar_lowerings: Vec<TokenStream> = fields
.iter()
.filter(|f| f.category.is_some())
.map(|f| {
let name = &f.name;
let col = &f.column;
quote! {
if let ::core::option::Option::Some(__inner) = self.#name {
use ::prax_query::inputs::ScalarFilter as _;
let __f = __inner.into_filter(#col);
if !matches!(__f, ::prax_query::filter::Filter::None) {
parts.push(__f);
}
}
}
})
.collect();
let relation_lowerings: Vec<TokenStream> = fields
.iter()
.filter(|f| f.relation_target_where_input.is_some())
.map(|f| {
let name = &f.name;
let meta_ident = {
let pascal_rel = pascal_ident(&f.name.to_string());
format_ident!("{}{}FilterMeta", model_ident, pascal_rel)
};
quote! {
if let ::core::option::Option::Some(__inner) = self.#name {
use ::prax_query::inputs::LowerRelationFilter as _;
let __f = __inner.lower::<#meta_ident>();
if !matches!(__f, ::prax_query::filter::Filter::None) {
parts.push(__f);
}
}
}
})
.collect();
let struct_tokens = quote! {
#[derive(Debug, Clone, Default, ::serde::Serialize, ::serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct #where_input_ident {
#(#scalar_field_decls,)*
#(#relation_field_decls,)*
pub and: ::core::option::Option<::std::vec::Vec<#where_input_ident>>,
pub or: ::core::option::Option<::std::vec::Vec<#where_input_ident>>,
pub not: ::core::option::Option<::std::boxed::Box<#where_input_ident>>,
}
};
let impl_tokens = quote! {
impl ::prax_query::inputs::WhereInput for #module_name::#where_input_ident {
type Model = #model_ident;
fn into_ir(self) -> ::prax_query::filter::Filter {
let mut parts: ::std::vec::Vec<::prax_query::filter::Filter> =
::std::vec::Vec::new();
#(#scalar_lowerings)*
#(#relation_lowerings)*
if let ::core::option::Option::Some(ands) = self.and {
let inner: ::std::vec::Vec<::prax_query::filter::Filter> = ands
.into_iter()
.map(|w| <#module_name::#where_input_ident as
::prax_query::inputs::WhereInput>::into_ir(w))
.collect();
parts.push(::prax_query::filter::Filter::and(inner));
}
if let ::core::option::Option::Some(ors) = self.or {
let inner: ::std::vec::Vec<::prax_query::filter::Filter> = ors
.into_iter()
.map(|w| <#module_name::#where_input_ident as
::prax_query::inputs::WhereInput>::into_ir(w))
.collect();
parts.push(::prax_query::filter::Filter::or(inner));
}
if let ::core::option::Option::Some(n) = self.not {
parts.push(::prax_query::filter::Filter::Not(::std::boxed::Box::new(
<#module_name::#where_input_ident as
::prax_query::inputs::WhereInput>::into_ir(*n),
)));
}
match parts.len() {
0 => ::prax_query::filter::Filter::None,
1 => parts.into_iter().next().unwrap(),
_ => ::prax_query::filter::Filter::and(parts),
}
}
}
};
WhereInputTokens {
struct_tokens,
impl_tokens,
}
}