use std::marker::PhantomData;
use crate::query::ast::OrderTerm;
use crate::query::expr::{AggFunc, BinaryOp, Expr};
use crate::query::queryset::QuerySet;
use crate::query::write::Assignment;
use crate::value::{BindValue, Value};
pub trait IntoAssignExpr<T> {
fn into_assign_expr(self) -> Expr;
}
macro_rules! impl_assign_literal {
($t:ty) => {
impl IntoAssignExpr<$t> for $t {
fn into_assign_expr(self) -> Expr {
Expr::value(self.to_value())
}
}
impl IntoAssignExpr<Option<$t>> for $t {
fn into_assign_expr(self) -> Expr {
Expr::value(self.to_value())
}
}
};
}
impl_assign_literal!(i64);
impl_assign_literal!(i32);
impl_assign_literal!(f64);
impl_assign_literal!(bool);
impl_assign_literal!(String);
impl IntoAssignExpr<String> for &str {
fn into_assign_expr(self) -> Expr {
Expr::value(Value::Text(self.to_string()))
}
}
impl<T> IntoAssignExpr<T> for Value {
fn into_assign_expr(self) -> Expr {
Expr::value(self)
}
}
impl<T> IntoAssignExpr<T> for Expr {
fn into_assign_expr(self) -> Expr {
self
}
}
pub trait Numeric {}
impl Numeric for i64 {}
impl Numeric for i32 {}
impl Numeric for f64 {}
pub trait IntoSqlValue<T> {
fn into_sql_value(self) -> Value;
}
impl<T: BindValue> IntoSqlValue<T> for T {
fn into_sql_value(self) -> Value {
self.to_value()
}
}
impl IntoSqlValue<String> for &str {
fn into_sql_value(self) -> Value {
Value::Text(self.to_string())
}
}
pub struct Column<M, T> {
table: &'static str,
name: &'static str,
_marker: PhantomData<fn() -> (M, T)>,
}
impl<M, T> Column<M, T> {
pub const fn new(table: &'static str, name: &'static str) -> Self {
Self {
table,
name,
_marker: PhantomData,
}
}
pub fn table(&self) -> &'static str {
self.table
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn expr(&self) -> Expr {
Expr::column(self.table, self.name)
}
pub fn eq<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Eq, value)
}
pub fn ne<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Ne, value)
}
pub fn gt<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Gt, value)
}
pub fn ge<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Ge, value)
}
pub fn lt<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Lt, value)
}
pub fn le<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.compare(BinaryOp::Le, value)
}
pub fn in_list<V, I>(self, values: I) -> Expr
where
V: IntoSqlValue<T>,
I: IntoIterator<Item = V>,
{
let values = values.into_iter().map(IntoSqlValue::into_sql_value).collect();
Expr::in_list(self.expr(), values)
}
pub fn not_in<V, I>(self, values: I) -> Expr
where
V: IntoSqlValue<T>,
I: IntoIterator<Item = V>,
{
Expr::not(self.in_list(values))
}
pub fn in_subquery<X: crate::model::Model>(self, qs: QuerySet<X>) -> Expr {
Expr::in_subquery(self.expr(), qs.into_statement(), false)
}
pub fn not_in_subquery<X: crate::model::Model>(self, qs: QuerySet<X>) -> Expr {
Expr::in_subquery(self.expr(), qs.into_statement(), true)
}
pub fn between<V: IntoSqlValue<T>>(self, low: V, high: V) -> Expr {
Expr::between(
self.expr(),
Expr::value(low.into_sql_value()),
Expr::value(high.into_sql_value()),
)
}
pub fn is_null(self) -> Expr {
Expr::is_null(self.expr(), false)
}
pub fn is_not_null(self) -> Expr {
Expr::is_null(self.expr(), true)
}
pub fn asc(self) -> OrderTerm {
OrderTerm::new(self.expr(), false)
}
pub fn desc(self) -> OrderTerm {
OrderTerm::new(self.expr(), true)
}
pub fn set<A: IntoAssignExpr<T>>(self, value: A) -> Assignment {
Assignment::new(self.name, value.into_assign_expr())
}
pub fn as_(self, alias: &'static str) -> Expr {
self.expr().as_(alias)
}
pub fn count(self) -> Expr {
Expr::aggregate(AggFunc::Count, [self.expr()])
}
pub fn lower(self) -> Expr {
Expr::func("lower", [self.expr()])
}
pub fn upper(self) -> Expr {
Expr::func("upper", [self.expr()])
}
pub fn length(self) -> Expr {
Expr::func("length", [self.expr()])
}
pub fn trim(self) -> Expr {
Expr::func("trim", [self.expr()])
}
pub fn abs(self) -> Expr {
Expr::func("abs", [self.expr()])
}
pub fn is_distinct_from<V: IntoSqlValue<T>>(self, value: V) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::IsDistinctFrom,
Expr::value(value.into_sql_value()),
)
}
pub fn is_not_distinct_from<V: IntoSqlValue<T>>(self, value: V) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::IsNotDistinctFrom,
Expr::value(value.into_sql_value()),
)
}
fn compare<V: IntoSqlValue<T>>(self, op: BinaryOp, value: V) -> Expr {
Expr::binary(self.expr(), op, Expr::value(value.into_sql_value()))
}
}
#[cfg(feature = "postgres")]
impl<M, T: crate::value::BindValue> Column<M, T> {
pub fn string_aggregation(self, delimiter: &str) -> Expr {
Expr::aggregate(AggFunc::StringAggregation, [
self.expr(),
Expr::value(Value::Text(delimiter.to_string())),
])
}
pub fn array_aggregation(self) -> Expr {
Expr::aggregate(AggFunc::ArrayAggregation, [self.expr()])
}
pub fn json_aggregation(self) -> Expr {
Expr::aggregate(AggFunc::JsonAggregation, [self.expr()])
}
pub fn jsonb_aggregation(self) -> Expr {
Expr::aggregate(AggFunc::JsonbAggregation, [self.expr()])
}
}
impl<M, T> From<Column<M, T>> for Expr {
fn from(column: Column<M, T>) -> Self {
column.expr()
}
}
impl<M, T: Numeric> Column<M, T> {
pub fn sum(self) -> Expr {
Expr::aggregate(AggFunc::Sum, [self.expr()])
}
pub fn avg(self) -> Expr {
Expr::aggregate(AggFunc::Avg, [self.expr()])
}
pub fn min(self) -> Expr {
Expr::aggregate(AggFunc::Min, [self.expr()])
}
pub fn max(self) -> Expr {
Expr::aggregate(AggFunc::Max, [self.expr()])
}
pub fn add<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.expr().add(Expr::value(value.into_sql_value()))
}
pub fn sub<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.expr().sub(Expr::value(value.into_sql_value()))
}
pub fn mul<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.expr().mul(Expr::value(value.into_sql_value()))
}
pub fn div<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.expr().div(Expr::value(value.into_sql_value()))
}
pub fn rem<V: IntoSqlValue<T>>(self, value: V) -> Expr {
self.expr().rem(Expr::value(value.into_sql_value()))
}
pub fn round(self) -> Expr {
Expr::func("round", [self.expr()])
}
pub fn ceil(self) -> Expr {
Expr::func("ceil", [self.expr()])
}
pub fn floor(self) -> Expr {
Expr::func("floor", [self.expr()])
}
}
impl<M> Column<M, String> {
pub fn like(self, pattern: &str) -> Expr {
Expr::binary(
self.expr(),
crate::query::expr::BinaryOp::Like,
Expr::value(Value::Text(pattern.to_string())),
)
}
pub fn ilike(self, pattern: &str) -> Expr {
Expr::binary(
self.expr(),
crate::query::expr::BinaryOp::ILike,
Expr::value(Value::Text(pattern.to_string())),
)
}
pub fn starts_with(self, prefix: &str) -> Expr {
self.like(&format!("{prefix}%"))
}
pub fn ends_with(self, suffix: &str) -> Expr {
self.like(&format!("%{suffix}"))
}
pub fn contains(self, needle: &str) -> Expr {
self.like(&format!("%{needle}%"))
}
pub fn istarts_with(self, prefix: &str) -> Expr {
self.ilike(&format!("{prefix}%"))
}
pub fn iends_with(self, suffix: &str) -> Expr {
self.ilike(&format!("%{suffix}"))
}
pub fn icontains(self, needle: &str) -> Expr {
self.ilike(&format!("%{needle}%"))
}
pub fn substr(self, start: i64) -> Expr {
Expr::func("substr", [self.expr(), Expr::value(Value::Int(start))])
}
pub fn substr_len(self, start: i64, len: i64) -> Expr {
Expr::func(
"substr",
[
self.expr(),
Expr::value(Value::Int(start)),
Expr::value(Value::Int(len)),
],
)
}
pub fn position(self, substring: &str) -> Expr {
Expr::func("position", [
Expr::value(Value::Text(substring.to_string())),
self.expr(),
])
}
pub fn replace(self, from: &str, to: &str) -> Expr {
Expr::func("replace", [
self.expr(),
Expr::value(Value::Text(from.to_string())),
Expr::value(Value::Text(to.to_string())),
])
}
}
#[cfg(feature = "postgres")]
impl<M> Column<M, String> {
pub fn regex_match(self, pattern: &str) -> Expr {
Expr::func("regexp_like", [self.expr(), Expr::value(Value::Text(pattern.to_string()))])
}
pub fn regex_replace(self, pattern: &str, replacement: &str) -> Expr {
Expr::func("regexp_replace", [
self.expr(),
Expr::value(Value::Text(pattern.to_string())),
Expr::value(Value::Text(replacement.to_string())),
])
}
pub fn split_part(self, delimiter: &str, field: i64) -> Expr {
Expr::func("split_part", [
self.expr(),
Expr::value(Value::Text(delimiter.to_string())),
Expr::value(Value::Int(field)),
])
}
pub fn left(self, n: i64) -> Expr {
Expr::func("left", [self.expr(), Expr::value(Value::Int(n))])
}
pub fn right(self, n: i64) -> Expr {
Expr::func("right", [self.expr(), Expr::value(Value::Int(n))])
}
pub fn repeat(self, n: i64) -> Expr {
Expr::func("repeat", [self.expr(), Expr::value(Value::Int(n))])
}
pub fn reverse(self) -> Expr {
Expr::func("reverse", [self.expr()])
}
}
#[cfg(feature = "postgres")]
impl<M> Column<M, bool> {
pub fn bool_and(self) -> Expr {
Expr::aggregate(AggFunc::BoolAnd, [self.expr()])
}
pub fn bool_or(self) -> Expr {
Expr::aggregate(AggFunc::BoolOr, [self.expr()])
}
}
impl<M> Column<M, serde_json::Value> {
pub fn json_get(self, key: &str) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::JsonGet,
Expr::value(Value::Text(key.to_string())),
)
}
pub fn json_get_text(self, key: &str) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::JsonGetText,
Expr::value(Value::Text(key.to_string())),
)
}
pub fn json_contains(self, value: serde_json::Value) -> Expr {
Expr::binary(self.expr(), BinaryOp::Contains, Expr::value(Value::Json(value)))
}
pub fn json_has_key(self, key: &str) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::JsonKeyExists,
Expr::value(Value::Text(key.to_string())),
)
}
pub fn json_has_any(self, keys: &[&str]) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::JsonKeyExistsAny,
Expr::value(Value::Array(keys.iter().map(|k| Value::Text(k.to_string())).collect())),
)
}
pub fn json_has_all(self, keys: &[&str]) -> Expr {
Expr::binary(
self.expr(),
BinaryOp::JsonKeyExistsAll,
Expr::value(Value::Array(keys.iter().map(|k| Value::Text(k.to_string())).collect())),
)
}
pub fn json_path(self, path: &[&str]) -> Expr {
let path_array: Vec<Value> = path.iter().map(|k| Value::Text(k.to_string())).collect();
Expr::binary(
self.expr(),
BinaryOp::JsonPath,
Expr::value(Value::Array(path_array)),
)
}
pub fn json_path_text(self, path: &[&str]) -> Expr {
let path_array: Vec<Value> = path.iter().map(|k| Value::Text(k.to_string())).collect();
Expr::binary(
self.expr(),
BinaryOp::JsonPathText,
Expr::value(Value::Array(path_array)),
)
}
}
impl<M, T: BindValue> Column<M, Vec<T>> {
pub fn any(self, value: T) -> Expr {
Expr::binary(
Expr::value(value.to_value()),
BinaryOp::Eq,
Expr::func("ANY", [self.expr()]),
)
}
pub fn array_contains(self, items: impl IntoIterator<Item = T>) -> Expr {
let array = Value::Array(items.into_iter().map(|item| item.to_value()).collect());
Expr::binary(self.expr(), BinaryOp::Contains, Expr::value(array))
}
pub fn overlaps(self, items: impl IntoIterator<Item = T>) -> Expr {
let array = Value::Array(items.into_iter().map(|item| item.to_value()).collect());
Expr::binary(self.expr(), BinaryOp::Overlap, Expr::value(array))
}
pub fn contained_by(self, items: impl IntoIterator<Item = T>) -> Expr {
let array = Value::Array(items.into_iter().map(|item| item.to_value()).collect());
Expr::binary(self.expr(), BinaryOp::ArrayContainedBy, Expr::value(array))
}
pub fn array_append(self, value: T) -> Expr {
Expr::func("array_append", [self.expr(), Expr::value(value.to_value())])
}
pub fn array_remove(self, value: T) -> Expr {
Expr::func("array_remove", [self.expr(), Expr::value(value.to_value())])
}
pub fn array_position(self, value: T) -> Expr {
Expr::func("array_position", [self.expr(), Expr::value(value.to_value())])
}
pub fn array_length(self, dimension: i64) -> Expr {
Expr::func("array_length", [self.expr(), Expr::value(Value::Int(dimension))])
}
}
impl<M, T> Clone for Column<M, T> {
fn clone(&self) -> Self {
*self
}
}
impl<M, T> Copy for Column<M, T> {}