use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
use crate::generators::inputs::{FilterCategory, scalar_payload_type};
pub struct CreateField {
pub name: Ident,
pub column: String,
pub category: FilterCategory,
pub nullable: bool,
pub has_default: bool,
pub enum_ident: Option<Ident>,
}
pub struct CreateInputTokens {
pub struct_tokens: TokenStream,
pub impl_tokens: TokenStream,
}
pub fn generate(
model_ident: &Ident,
module_name: &Ident,
fields: &[CreateField],
) -> CreateInputTokens {
let create_ident = format_ident!("{}CreateInput", model_ident);
let field_decls = fields.iter().filter_map(|f| {
let n = &f.name;
let payload = match &f.enum_ident {
Some(e) => quote! { #e },
None => scalar_payload_type(f.category)?,
};
Some(if f.nullable || f.has_default {
quote! { pub #n: ::core::option::Option<#payload> }
} else {
quote! { pub #n: #payload }
})
});
let create_ident_doc = format!(
"Create-time input for a `{}`.\n\n\
**Warning:** this type derives `Default`. Calling `{}::default()` \
produces zero-valued required scalar fields (`String::new()`, `0`, \
`false`). Use struct-literal syntax for safety, or call \
`Default::default()` only when you know every required field will \
be overridden downstream. A strict variant is planned for the \
operation rework.",
create_ident, create_ident
);
let struct_tokens = quote! {
#[doc = #create_ident_doc]
#[derive(Debug, Clone, Default, ::serde::Serialize, ::serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct #create_ident {
#(#field_decls,)*
}
};
let lowerings: Vec<TokenStream> = fields
.iter()
.filter_map(|f| {
if matches!(f.category, FilterCategory::Enum) && f.enum_ident.is_none() {
return None;
}
let n = &f.name;
let col = &f.column;
let is_optional = f.nullable || f.has_default;
let push_stmt = if is_optional {
quote! {
if let ::core::option::Option::Some(__v) = self.#n {
__out.push((
::std::string::String::from(#col),
::core::convert::Into::<::prax_query::filter::FilterValue>::into(__v),
));
}
}
} else {
quote! {
__out.push((
::std::string::String::from(#col),
::core::convert::Into::<::prax_query::filter::FilterValue>::into(self.#n),
));
}
};
Some(push_stmt)
})
.collect();
let impl_tokens = quote! {
impl ::prax_query::inputs::CreateInput for #module_name::#create_ident {
type Model = #model_ident;
type Data = ::prax_query::inputs::CreatePayload;
fn into_ir(self) -> Self::Data {
let mut __out: ::prax_query::inputs::CreatePayload =
::std::vec::Vec::new();
#(#lowerings)*
__out
}
}
};
CreateInputTokens {
struct_tokens,
impl_tokens,
}
}