use sqlx::{MySql, QueryBuilder};
use crate::params::{FilterExpr, Op, ExistsValue};
use crate::whitelist::FieldWhitelist;
pub fn build_filter<'a>(
qb: &mut QueryBuilder<'a, MySql>,
expr: &'a FilterExpr,
whitelist: &FieldWhitelist,
) -> Result<(), String> {
match expr {
FilterExpr::And { and } => {
qb.push("(");
for (i, e) in and.iter().enumerate() {
if i > 0 { qb.push(" AND "); }
build_filter(qb, e, whitelist)?;
}
qb.push(")");
}
FilterExpr::Or { or } => {
qb.push("(");
for (i, e) in or.iter().enumerate() {
if i > 0 { qb.push(" OR "); }
build_filter(qb, e, whitelist)?;
}
qb.push(")");
}
FilterExpr::Cond(c) => match c.op {
Op::IsNull => {
let f = c.field.as_ref().unwrap();
whitelist.check(f)?;
qb.push(f).push(" IS NULL");
}
Op::IsNotNull => {
let f = c.field.as_ref().unwrap();
whitelist.check(f)?;
qb.push(f).push(" IS NOT NULL");
}
Op::Exists | Op::NotExists => {
let v: ExistsValue =
serde_json::from_value(c.value.clone().unwrap()).unwrap();
qb.push(if matches!(c.op, Op::Exists) { "EXISTS (" } else { "NOT EXISTS (" });
qb.push(&v.sql);
if let Some(bind) = v.bind {
for b in bind {
qb.push_bind(b);
}
}
qb.push(")");
}
_ => {
let f = c.field.as_ref().unwrap();
whitelist.check(f)?;
qb.push(f);
match c.op {
Op::Eq => { qb.push(" = "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::Ne => { qb.push(" != "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::Like => {
qb.push(" LIKE ");
qb.push_bind(format!("%{}%", c.value.as_ref().unwrap().as_str().unwrap()));
}
Op::Gt => { qb.push(" > "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::Lt => { qb.push(" < "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::Gte => { qb.push(" >= "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::Lte => { qb.push(" <= "); qb.push_bind(c.value.as_ref().unwrap()); }
Op::In | Op::NotIn => {
let arr = c.value.as_ref().unwrap().as_array().unwrap();
qb.push(if matches!(c.op, Op::In) { " IN (" } else { " NOT IN (" });
for (i, v) in arr.iter().enumerate() {
if i > 0 { qb.push(", "); }
qb.push_bind(v);
}
qb.push(")");
}
Op::Between => {
let arr = c.value.as_ref().unwrap().as_array().unwrap();
qb.push(" BETWEEN ");
qb.push_bind(&arr[0]);
qb.push(" AND ");
qb.push_bind(&arr[1]);
}
Op::FindInSet => {
qb.push(" FIND_IN_SET(");
qb.push_bind(c.value.as_ref().unwrap());
qb.push(", ").push(f).push(")");
}
Op::JsonContains => {
qb.push(" JSON_CONTAINS(");
qb.push(f).push(", ");
qb.push_bind(c.value.as_ref().unwrap().to_string());
qb.push(")");
}
_ => {}
}
}
},
}
Ok(())
}