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 regex(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Regex, SqlValue::String(pattern.into()))
}
fn not_regex(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::NotRegex, SqlValue::String(pattern.into()))
}
fn iregex(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::IRegex, SqlValue::String(pattern.into()))
}
fn not_iregex(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::NotIRegex,
SqlValue::String(pattern.into()),
)
}
fn trigram_similar(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::TrigramSimilar,
SqlValue::String(pattern.into()),
)
}
fn trigram_word_similar(self, pattern: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::TrigramWordSimilar,
SqlValue::String(pattern.into()),
)
}
fn search(self, query: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(Self::COLUMN, Op::Search, SqlValue::String(query.into()))
}
fn array_contains<V>(self, values: V) -> TypedFilter<Self::Model>
where
V: IntoIterator,
V::Item: Into<SqlValue>,
{
TypedFilter::scalar(
Self::COLUMN,
Op::ArrayContains,
SqlValue::Array(values.into_iter().map(Into::into).collect()),
)
}
fn array_contained_by<V>(self, values: V) -> TypedFilter<Self::Model>
where
V: IntoIterator,
V::Item: Into<SqlValue>,
{
TypedFilter::scalar(
Self::COLUMN,
Op::ArrayContainedBy,
SqlValue::Array(values.into_iter().map(Into::into).collect()),
)
}
fn array_overlap<V>(self, values: V) -> TypedFilter<Self::Model>
where
V: IntoIterator,
V::Item: Into<SqlValue>,
{
TypedFilter::scalar(
Self::COLUMN,
Op::ArrayOverlap,
SqlValue::Array(values.into_iter().map(Into::into).collect()),
)
}
fn range_contains(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeContains,
SqlValue::RangeLiteral(literal.into()),
)
}
fn range_contained_by(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeContainedBy,
SqlValue::RangeLiteral(literal.into()),
)
}
fn range_overlap(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeOverlap,
SqlValue::RangeLiteral(literal.into()),
)
}
fn range_strictly_left(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeStrictlyLeft,
SqlValue::RangeLiteral(literal.into()),
)
}
fn range_strictly_right(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeStrictlyRight,
SqlValue::RangeLiteral(literal.into()),
)
}
fn range_adjacent(self, literal: impl Into<String>) -> TypedFilter<Self::Model> {
TypedFilter::scalar(
Self::COLUMN,
Op::RangeAdjacent,
SqlValue::RangeLiteral(literal.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> {
let sql: SqlValue = value.into().into();
TypedAssignment {
inner: Assignment {
column: Self::COLUMN,
value: sql.into(),
},
_model: PhantomData,
}
}
fn set_expr(self, expr: impl Into<crate::core::Expr>) -> TypedAssignment<Self::Model> {
TypedAssignment {
inner: Assignment {
column: Self::COLUMN,
value: expr.into(),
},
_model: PhantomData,
}
}
fn eq_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Eq, rhs.into())
}
fn ne_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Ne, rhs.into())
}
fn lt_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Lt, rhs.into())
}
fn lte_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Lte, rhs.into())
}
fn gt_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Gt, rhs.into())
}
fn gte_expr(self, rhs: impl Into<crate::core::Expr>) -> TypedExpr<Self::Model> {
TypedExpr::column_cmp(Self::COLUMN, Op::Gte, rhs.into())
}
}
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 xor<E: Into<TypedExpr<M>>>(self, rhs: E) -> TypedExpr<M> {
TypedExpr::from(self).xor(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,
}
}
}
impl<M: Model> From<TypedFilter<M>> for WhereExpr {
fn from(tf: TypedFilter<M>) -> Self {
Self::Predicate(tf.inner)
}
}
impl<M: Model> From<TypedExpr<M>> for WhereExpr {
fn from(te: TypedExpr<M>) -> Self {
te.inner
}
}
pub struct TypedExpr<M: Model> {
pub(crate) inner: WhereExpr,
_model: PhantomData<fn() -> M>,
}
impl<M: Model> TypedExpr<M> {
#[must_use]
pub(crate) fn column_cmp(column: &'static str, op: Op, rhs: crate::core::Expr) -> Self {
Self {
inner: WhereExpr::ColumnCompare(super::query::ColumnFilter { column, op, rhs }),
_model: PhantomData,
}
}
#[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 xor<E: Into<Self>>(self, rhs: E) -> Self {
let rhs = rhs.into();
let inner = match (self.inner, rhs.inner) {
(WhereExpr::Xor(mut a), WhereExpr::Xor(b)) => {
a.extend(b);
WhereExpr::Xor(a)
}
(WhereExpr::Xor(mut a), b) => {
a.push(b);
WhereExpr::Xor(a)
}
(a, WhereExpr::Xor(mut b)) => {
b.insert(0, a);
WhereExpr::Xor(b)
}
(a, b) => WhereExpr::Xor(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
}
}
pub trait TypedFieldList<M: Model>: sealed::Sealed {
fn rust_field_names(&self) -> Vec<&'static str>;
}
mod sealed {
pub trait Sealed {}
}
impl<M: Model, A: Column<Model = M>> TypedFieldList<M> for (A,) {
fn rust_field_names(&self) -> Vec<&'static str> {
vec![A::NAME]
}
}
impl<A> sealed::Sealed for (A,) {}
macro_rules! impl_typed_field_list_tuple {
( $( ( $($T:ident),+ ) ),+ $(,)? ) => {
$(
impl<M: Model, $($T: Column<Model = M>),+> TypedFieldList<M> for ($($T,)+) {
fn rust_field_names(&self) -> Vec<&'static str> {
vec![$($T::NAME),+]
}
}
impl<$($T),+> sealed::Sealed for ($($T,)+) {}
)+
};
}
impl_typed_field_list_tuple!(
(A, B),
(A, B, C),
(A, B, C, D),
(A, B, C, D, E),
(A, B, C, D, E, F),
(A, B, C, D, E, F, G),
(A, B, C, D, E, F, G, H),
(A, B, C, D, E, F, G, H, I),
(A, B, C, D, E, F, G, H, I, J),
(A, B, C, D, E, F, G, H, I, J, K),
(A, B, C, D, E, F, G, H, I, J, K, L),
);