use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::LitStr;
use crate::codegen::schema::SchemaContext;
pub(super) fn collect_field_query_methods(
schema: &SchemaContext,
) -> (Vec<TokenStream>, Vec<TokenStream>, Vec<TokenStream>) {
let mut where_methods = Vec::new();
let mut order_by_methods = Vec::new();
let mut distinct_methods = Vec::new();
for field in &schema.fields {
let field_name_str = field.name.as_str();
let where_method_name = format_ident!("where_{}", field_name_str);
let order_by_method_name = format_ident!("order_by_{}", field_name_str);
let field_type_str = field.field_type.as_str();
let field_name_lit = LitStr::new(field_name_str, proc_macro2::Span::call_site());
let where_method = if field_type_str.starts_with("record<") && field_type_str.ends_with('>')
{
quote! {
pub fn #where_method_name(mut self, predicate: valence::RecordPredicate) -> Self {
self.inner = self.inner.where_record(#field_name_lit.to_string(), predicate);
self
}
}
} else if field_type_str == "currency" {
let where_code = format_ident!("where_{}_code", field_name_str);
let where_minor = format_ident!("where_{}_minor", field_name_str);
let code_path = format!("{field_name_str}.code");
let minor_path = format!("{field_name_str}.amount_minor");
let code_path_lit = LitStr::new(&code_path, proc_macro2::Span::call_site());
let minor_path_lit = LitStr::new(&minor_path, proc_macro2::Span::call_site());
quote! {
pub fn #where_code(mut self, code: valence::CurrencyCode) -> Self {
self.inner = self.inner.where_string(
#code_path_lit.to_string(),
valence::StringPredicate::Equals(code.as_str().to_string()),
);
self
}
pub fn #where_minor(mut self, predicate: valence::IntPredicate) -> Self {
self.inner = self.inner.where_int(#minor_path_lit.to_string(), predicate);
self
}
}
} else if field_type_str.starts_with("json_as:") {
quote! {}
} else {
match field_type_str {
"integer" => quote! {
pub fn #where_method_name(mut self, predicate: valence::IntPredicate) -> Self {
self.inner = self.inner.where_int(#field_name_lit.to_string(), predicate);
self
}
},
"string" | "text" => quote! {
pub fn #where_method_name(mut self, predicate: valence::StringPredicate) -> Self {
self.inner = self.inner.where_string(#field_name_lit.to_string(), predicate);
self
}
},
"datetime" => quote! {
pub fn #where_method_name(mut self, predicate: valence::DateTimePredicate) -> Self {
self.inner = self.inner.where_datetime(#field_name_lit.to_string(), predicate);
self
}
},
"json" => quote! {},
_ => quote! {
pub fn #where_method_name(mut self, predicate: valence::StringPredicate) -> Self {
self.inner = self.inner.where_string(#field_name_lit.to_string(), predicate);
self
}
},
}
};
if !where_method.is_empty() {
where_methods.push(where_method);
}
if field.nullable {
let where_is_none_method = format_ident!("where_{}_is_none", field_name_str);
let where_is_some_method = format_ident!("where_{}_is_some", field_name_str);
let field_name_lit = LitStr::new(field_name_str, proc_macro2::Span::call_site());
where_methods.push(quote! {
pub fn #where_is_none_method(mut self) -> Self {
self.inner = self.inner.where_null(#field_name_lit.to_string(), valence::NullPredicate::IsNone);
self
}
pub fn #where_is_some_method(mut self) -> Self {
self.inner = self.inner.where_null(#field_name_lit.to_string(), valence::NullPredicate::IsSome);
self
}
});
}
let field_name_lit = LitStr::new(field_name_str, proc_macro2::Span::call_site());
order_by_methods.push(quote! {
pub fn #order_by_method_name(mut self, direction: valence::SortDirection) -> Self {
self.inner = self.inner.order_by(#field_name_lit.to_string(), direction);
self
}
});
let is_record = field_type_str.starts_with("record<") && field_type_str.ends_with('>');
let is_string_like = matches!(field_type_str, "string" | "text")
|| (!is_record
&& !field_type_str.starts_with("json_as:")
&& !matches!(
field_type_str,
"integer" | "float" | "decimal" | "boolean" | "datetime" | "json" | "currency"
));
if is_string_like {
let distinct_method_name = format_ident!("distinct_{}", field_name_str);
let field_name_lit = LitStr::new(field_name_str, proc_macro2::Span::call_site());
distinct_methods.push(quote! {
pub async fn #distinct_method_name(self) -> valence::Result<Vec<String>> {
self.inner.distinct_values(#field_name_lit, self.valence).await
}
});
}
}
(where_methods, order_by_methods, distinct_methods)
}