use core::marker::PhantomData;
use crate::builder::QueryBuilder;
use crate::dialect::Dialect;
use crate::value::{IntoBind, Value};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Conj {
And,
Or,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Predicate<D: Dialect> {
Binary {
col: String,
op: &'static str,
val: Value,
},
In {
col: String,
neg: bool,
vals: Vec<Value>,
},
Null {
col: String,
neg: bool,
},
Between {
col: String,
lo: Value,
hi: Value,
},
ILike {
col: String,
val: Value,
},
JsonContains {
col: String,
val: Value,
},
Raw {
sql: String,
binds: Vec<Value>,
},
Group {
outer_conj: Conj,
preds: Vec<Predicate<D>>,
},
Column {
lhs: String,
op: &'static str,
rhs: String,
},
Exists {
neg: bool,
sub: Box<QueryBuilder<D>>,
},
InSubquery {
col: String,
neg: bool,
sub: Box<QueryBuilder<D>>,
},
}
pub struct WhereBuilder<D: Dialect> {
preds: Vec<Predicate<D>>,
_marker: PhantomData<D>,
}
impl<D: Dialect> Default for WhereBuilder<D> {
fn default() -> Self {
Self::new()
}
}
impl<D: Dialect> WhereBuilder<D> {
pub fn new() -> Self {
Self {
preds: Vec::new(),
_marker: PhantomData,
}
}
pub(crate) fn into_preds(self) -> Vec<Predicate<D>> {
self.preds
}
fn group(
mut self,
outer_conj: Conj,
f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>,
) -> Self {
let preds = f(WhereBuilder::new()).into_preds();
self.preds.push(Predicate::Group { outer_conj, preds });
self
}
pub fn and_where(self, f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>) -> Self {
self.group(Conj::And, f)
}
pub fn or_where(self, f: impl FnOnce(WhereBuilder<D>) -> WhereBuilder<D>) -> Self {
self.group(Conj::Or, f)
}
fn binary(mut self, col: &str, op: &'static str, val: impl IntoBind) -> Self {
self.preds.push(Predicate::Binary {
col: col.to_owned(),
op,
val: val.into_bind(),
});
self
}
pub fn where_eq(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "=", val)
}
pub fn where_ne(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "!=", val)
}
pub fn where_gt(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, ">", val)
}
pub fn where_gte(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, ">=", val)
}
pub fn where_lt(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "<", val)
}
pub fn where_lte(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "<=", val)
}
pub fn where_like(self, col: &str, val: impl IntoBind) -> Self {
self.binary(col, "LIKE", val)
}
pub fn where_ilike(mut self, col: &str, val: impl IntoBind) -> Self {
self.preds.push(Predicate::ILike {
col: col.to_owned(),
val: val.into_bind(),
});
self
}
pub fn where_jsonb_contains(mut self, col: &str, val: impl IntoBind) -> Self {
self.preds.push(Predicate::JsonContains {
col: col.to_owned(),
val: val.into_bind(),
});
self
}
fn in_(mut self, col: &str, neg: bool, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.preds.push(Predicate::In {
col: col.to_owned(),
neg,
vals: vals.into_iter().map(IntoBind::into_bind).collect(),
});
self
}
pub fn where_in(self, col: &str, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.in_(col, false, vals)
}
pub fn where_not_in(self, col: &str, vals: impl IntoIterator<Item = impl IntoBind>) -> Self {
self.in_(col, true, vals)
}
fn null(mut self, col: &str, neg: bool) -> Self {
self.preds.push(Predicate::Null {
col: col.to_owned(),
neg,
});
self
}
pub fn where_null(self, col: &str) -> Self {
self.null(col, false)
}
pub fn where_not_null(self, col: &str) -> Self {
self.null(col, true)
}
pub fn where_between(mut self, col: &str, lo: impl IntoBind, hi: impl IntoBind) -> Self {
self.preds.push(Predicate::Between {
col: col.to_owned(),
lo: lo.into_bind(),
hi: hi.into_bind(),
});
self
}
pub fn where_raw(mut self, sql: &str, binds: Vec<Value>) -> Self {
self.preds.push(Predicate::Raw {
sql: sql.to_owned(),
binds,
});
self
}
pub fn where_column(mut self, lhs: &str, op: &'static str, rhs: &str) -> Self {
self.preds.push(Predicate::Column {
lhs: lhs.to_owned(),
op,
rhs: rhs.to_owned(),
});
self
}
pub fn where_exists(mut self, sub: QueryBuilder<D>) -> Self {
self.preds.push(Predicate::Exists {
neg: false,
sub: Box::new(sub),
});
self
}
pub fn where_not_exists(mut self, sub: QueryBuilder<D>) -> Self {
self.preds.push(Predicate::Exists {
neg: true,
sub: Box::new(sub),
});
self
}
pub fn where_in_subquery(mut self, col: &str, sub: QueryBuilder<D>) -> Self {
self.preds.push(Predicate::InSubquery {
col: col.to_owned(),
neg: false,
sub: Box::new(sub),
});
self
}
pub fn where_not_in_subquery(mut self, col: &str, sub: QueryBuilder<D>) -> Self {
self.preds.push(Predicate::InSubquery {
col: col.to_owned(),
neg: true,
sub: Box::new(sub),
});
self
}
}