use std::marker::PhantomData;
use super::{Assignment, Filter, Model, Op, SqlValue, WhereExpr};
pub trait Column: Copy + 'static {
type Model: Model;
type Value: Into<SqlValue>;
const NAME: &'static str;
const COLUMN: &'static str;
const FIELD_TYPE: super::FieldType;
fn eq<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Eq, value.into().into())
}
fn ne<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Ne, value.into().into())
}
fn lt<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Lt, value.into().into())
}
fn lte<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Lte, value.into().into())
}
fn gt<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Gt, value.into().into())
}
fn gte<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Gte, value.into().into())
}
fn like<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Like, value.into().into())
}
fn not_like<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::NotLike, value.into().into())
}
fn ilike<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::ILike, value.into().into())
}
fn not_ilike<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::NotILike, value.into().into())
}
fn is_null(self) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::IsNull, SqlValue::Bool(true))
}
fn is_not_null(self) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::IsNull, SqlValue::Bool(false))
}
fn is_in<V, I>(self, values: I) -> TypedFilter<Self::Model>
where
V: Into<Self::Value>,
I: IntoIterator<Item = V>,
{
let list: Vec<SqlValue> = values.into_iter().map(|v| v.into().into()).collect();
TypedFilter::scalar(Self::COLUMN, Op::In, SqlValue::List(list))
}
fn not_in<V, I>(self, values: I) -> TypedFilter<Self::Model>
where
V: Into<Self::Value>,
I: IntoIterator<Item = V>,
{
let list: Vec<SqlValue> = values.into_iter().map(|v| v.into().into()).collect();
TypedFilter::scalar(Self::COLUMN, Op::NotIn, SqlValue::List(list))
}
fn between<V: Into<Self::Value>>(self, lo: V, hi: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::Between,
SqlValue::List(vec![lo.into().into(), hi.into().into()]),
)
}
fn is_distinct_from<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::IsDistinctFrom, value.into().into())
}
fn is_not_distinct_from<V: Into<Self::Value>>(self, value: V) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::IsNotDistinctFrom, value.into().into())
}
fn json_contains(self, value: serde_json::Value) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::JsonContains, SqlValue::Json(value))
}
fn json_contained_by(self, value: serde_json::Value) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::JsonContainedBy, SqlValue::Json(value))
}
fn json_has_key(self, key: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::JsonHasKey, SqlValue::String(key.into()))
}
fn json_has_any_key<I: IntoIterator<Item = impl Into<String>>>(
self,
keys: I,
) -> TypedFilter<Self::Model> {
let list: Vec<SqlValue> = keys
.into_iter()
.map(|k| SqlValue::String(k.into()))
.collect();
TypedFilter::scalar(Self::COLUMN, Op::JsonHasAnyKey, SqlValue::List(list))
}
fn json_has_all_keys<I: IntoIterator<Item = impl Into<String>>>(
self,
keys: I,
) -> TypedFilter<Self::Model> {
let list: Vec<SqlValue> = keys
.into_iter()
.map(|k| SqlValue::String(k.into()))
.collect();
TypedFilter::scalar(Self::COLUMN, Op::JsonHasAllKeys, SqlValue::List(list))
}
fn set<V: Into<Self::Value>>(self, value: V) -> TypedAssignment<Self::Model> {
TypedAssignment {
inner: Assignment {
column: Self::COLUMN,
value: value.into().into(),
},
_model: PhantomData,
}
}
}
pub struct TypedFilter<M: Model> {
pub(crate) inner: Filter,
_model: PhantomData<fn() -> M>,
}
impl<M: Model> TypedFilter<M> {
fn scalar(column: &'static str, op: Op, value: SqlValue) -> Self {
Self {
inner: Filter { column, op, value },
_model: PhantomData,
}
}
#[must_use]
pub fn into_filter(self) -> Filter {
self.inner
}
#[must_use]
pub fn and<E: Into<TypedExpr<M>>>(self, rhs: E) -> TypedExpr<M> {
TypedExpr::from(self).and(rhs)
}
#[must_use]
pub fn or<E: Into<TypedExpr<M>>>(self, rhs: E) -> TypedExpr<M> {
TypedExpr::from(self).or(rhs)
}
#[must_use]
pub fn not(self) -> TypedExpr<M> {
TypedExpr::from(self).not()
}
}
impl<M: Model> From<TypedFilter<M>> for TypedExpr<M> {
fn from(tf: TypedFilter<M>) -> Self {
Self {
inner: WhereExpr::Predicate(tf.inner),
_model: PhantomData,
}
}
}
pub struct TypedExpr<M: Model> {
pub(crate) inner: WhereExpr,
_model: PhantomData<fn() -> M>,
}
impl<M: Model> TypedExpr<M> {
#[must_use]
pub fn into_expr(self) -> WhereExpr {
self.inner
}
#[must_use]
pub fn and<E: Into<Self>>(self, rhs: E) -> Self {
let rhs = rhs.into();
let inner = match (self.inner, rhs.inner) {
(WhereExpr::And(mut a), WhereExpr::And(b)) => {
a.extend(b);
WhereExpr::And(a)
}
(WhereExpr::And(mut a), b) => {
a.push(b);
WhereExpr::And(a)
}
(a, WhereExpr::And(mut b)) => {
b.insert(0, a);
WhereExpr::And(b)
}
(a, b) => WhereExpr::And(vec![a, b]),
};
Self {
inner,
_model: PhantomData,
}
}
#[must_use]
pub fn or<E: Into<Self>>(self, rhs: E) -> Self {
let rhs = rhs.into();
let inner = match (self.inner, rhs.inner) {
(WhereExpr::Or(mut a), WhereExpr::Or(b)) => {
a.extend(b);
WhereExpr::Or(a)
}
(WhereExpr::Or(mut a), b) => {
a.push(b);
WhereExpr::Or(a)
}
(a, WhereExpr::Or(mut b)) => {
b.insert(0, a);
WhereExpr::Or(b)
}
(a, b) => WhereExpr::Or(vec![a, b]),
};
Self {
inner,
_model: PhantomData,
}
}
#[must_use]
pub fn not(self) -> Self {
Self {
inner: WhereExpr::Not(Box::new(self.inner)),
_model: PhantomData,
}
}
}
pub struct TypedAssignment<M: Model> {
pub(crate) inner: Assignment,
_model: PhantomData<fn() -> M>,
}
impl<M: Model> TypedAssignment<M> {
#[must_use]
pub fn into_assignment(self) -> Assignment {
self.inner
}
}