use crate::schema::{Relationship, Table, Through};
use super::filter::{ERRORS_DOC, push_column_conditions};
use super::{FilterRelationship, QUERY_PARAM, filter_relationships, push_call, push_call_ending};
use crate::generator::push_doc;
use crate::generator::sql::{ident, live_condition, str_literal, table_ref};
pub(super) fn push_conditions_fn(
output: &mut String,
table: &Table,
tables: &[Table],
relationships: &[FilterRelationship<'_>],
) {
let mut doc = vec![
"Appends ` AND <condition>` for each condition, without the `WHERE` clause or",
"the soft-delete condition, so another resource's filter can nest this one.",
];
if relationships.is_empty() {
push_doc(output, 4, &doc);
output.push_str(&format!(
" pub(crate) fn push_conditions(self, {QUERY_PARAM}) {{\n"
));
push_column_conditions(output, table);
output.push_str(" }\n");
return;
}
doc.extend([
"`outer` names the row being filtered (its table, or a relationship alias)",
"and `depth` counts the relationship filters this one is nested in.",
]);
doc.extend(ERRORS_DOC);
push_doc(output, 4, &doc);
output.push_str(&format!(
" pub(crate) fn push_conditions(\n self,\n outer: &str,\n depth: usize,\n {QUERY_PARAM},\n ) -> Result<(), sqlx::Error> {{\n"
));
push_column_conditions(output, table);
let quoted = |name: &str| str_literal(&ident(name));
for FilterRelationship {
relationship,
target,
join,
} in relationships
{
output.push_str(&format!(
" if let Some(filter) = self.{} {{\n",
relationship.name
));
push_call_ending(
output,
12,
"let alias = open_exists",
&[
str_literal(&table_ref(target)),
"depth".to_owned(),
"query".to_owned(),
],
"?;",
);
let mut pairs = Vec::new();
if join.is_none() {
pairs.push((&relationship.target_column, &relationship.column));
}
pairs.extend(
target
.tenant()
.zip(table.tenant())
.map(|(theirs, ours)| (&theirs.sql_name, &ours.sql_name)),
);
for (theirs, ours) in pairs {
push_call(
output,
12,
"correlate",
&[
"&alias".to_owned(),
quoted(theirs),
"outer".to_owned(),
quoted(ours),
"query".to_owned(),
],
);
}
if let Some(live) = live_condition(target, None) {
output.push_str(&format!(
" query.push({});\n",
str_literal(&format!(" AND {live}"))
));
}
if let (Some(join), Some(through)) = (join, &relationship.through) {
push_join_exists(output, table, join, relationship, through);
}
if filter_relationships(target, tables).is_empty() {
output.push_str(" filter.push_conditions(query);\n");
} else {
output.push_str(" filter.push_conditions(&alias, depth + 1, query)?;\n");
}
output.push_str(" query.push(\")\");\n }\n");
}
output.push_str(" Ok(())\n }\n");
}
fn push_join_exists(
output: &mut String,
table: &Table,
join: &Table,
relationship: &Relationship,
through: &Through,
) {
let quoted = |name: &str| str_literal(&ident(name));
output.push_str(" let through = format!(\"{alias}_j\");\n");
output.push_str(&format!(
" query.push({});\n",
str_literal(&format!(
" AND EXISTS (SELECT 1 FROM {} AS ",
table_ref(join)
))
));
output.push_str(" query.push(&through);\n");
output.push_str(" query.push(\" WHERE TRUE\");\n");
let mut pairs = vec![
(
&through.target_column,
"&alias",
&relationship.target_column,
),
(&through.column, "outer", &relationship.column),
];
pairs.extend(
join.tenant()
.zip(table.tenant())
.map(|(theirs, ours)| (&theirs.sql_name, "outer", &ours.sql_name)),
);
for (theirs, outer, ours) in pairs {
push_call(
output,
12,
"correlate",
&[
"&through".to_owned(),
quoted(theirs),
outer.to_owned(),
quoted(ours),
"query".to_owned(),
],
);
}
let close = match live_condition(join, None) {
Some(live) => format!(" AND {live})"),
None => ")".to_owned(),
};
output.push_str(&format!(
" query.push({});\n",
str_literal(&close)
));
}