use std::marker::PhantomData;
use std::mem::ManuallyDrop;
#[cfg(feature = "postgres-only")]
use ipnetwork::IpNetwork;
use rorm_db::sql::aggregation::SelectAggregator;
#[cfg(feature = "postgres-only")]
use crate::conditions::{Binary, BinaryOperator, Value};
use crate::conditions::{Column, Unary, UnaryOperator};
use crate::crud::selector::{AggregatedColumn, PathedSelector, Selector};
#[cfg(feature = "postgres-only")]
use crate::fields::traits::FieldILike;
use crate::fields::traits::{
FieldAvg, FieldColumns, FieldCount, FieldEq, FieldIn, FieldLike, FieldMax, FieldMin, FieldOrd,
FieldRegexp, FieldSum,
};
use crate::fields::utils::column_name::ColumnName;
use crate::internal::field::{Field, SingleColumnField};
use crate::internal::relation_path::{Path, PathField};
use crate::sealed;
pub struct FieldProxy<T>(PhantomData<ManuallyDrop<T>>);
macro_rules! FieldType {
($I:ident) => {
<<$I as FieldProxyImpl>::Field as Field>::Type
};
}
impl<F, P, I> FieldProxy<I>
where
F: Field + PathField<<F as Field>::Type>,
P: Path<Current = <F::ParentField as Field>::Model>,
I: FieldProxyImpl<Field = F, Path = P>,
{
pub fn query_as<S>(self, selector: S) -> PathedSelector<S, <I::Path as Path>::Step<I::Field>>
where
S: Selector<Model = <F::ChildField as Field>::Model>,
{
PathedSelector {
selector,
path: Default::default(),
}
}
}
impl<I: FieldProxyImpl> FieldProxy<I> {
pub fn is_none<T>(self) -> Unary<Column<I>>
where
I::Field: SingleColumnField<Type = Option<T>>,
{
Unary {
operator: UnaryOperator::IsNull,
fst_arg: Column(self),
}
}
pub fn is_some<T>(self) -> Unary<Column<I>>
where
I::Field: SingleColumnField<Type = Option<T>>,
{
Unary {
operator: UnaryOperator::IsNotNull,
fst_arg: Column(self),
}
}
pub fn equals<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldEq<'rhs, Rhs, Any>>::EqCond<I>
where
FieldType!(I): FieldEq<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_equals(self, rhs)
}
pub fn not_equals<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldEq<'rhs, Rhs, Any>>::NeCond<I>
where
FieldType!(I): FieldEq<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_not_equals(self, rhs)
}
pub fn r#in<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldIn<'rhs, Rhs, Any>>::InCond<I>
where
FieldType!(I): FieldIn<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_in(self, rhs)
}
pub fn not_in<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldIn<'rhs, Rhs, Any>>::NiCond<I>
where
FieldType!(I): FieldIn<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_not_in(self, rhs)
}
pub fn less_than<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldOrd<'rhs, Rhs, Any>>::LtCond<I>
where
FieldType!(I): FieldOrd<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_less_than(self, rhs)
}
pub fn less_equals<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldOrd<'rhs, Rhs, Any>>::LeCond<I>
where
FieldType!(I): FieldOrd<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_less_equals(self, rhs)
}
pub fn greater_than<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldOrd<'rhs, Rhs, Any>>::GtCond<I>
where
FieldType!(I): FieldOrd<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_greater_than(self, rhs)
}
pub fn greater_equals<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldOrd<'rhs, Rhs, Any>>::GeCond<I>
where
FieldType!(I): FieldOrd<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_greater_equals(self, rhs)
}
pub fn like<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldLike<'rhs, Rhs, Any>>::LiCond<I>
where
FieldType!(I): FieldLike<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_like(self, rhs)
}
pub fn not_like<'rhs, Rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldLike<'rhs, Rhs, Any>>::NlCond<I>
where
FieldType!(I): FieldLike<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_not_like(self, rhs)
}
pub fn contains<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldLike<'rhs, String, Any>>::LiCond<I>
where
FieldType!(I): FieldLike<'rhs, String, Any>,
{
self.like(format!("%{}%", escape_like(rhs)))
}
pub fn starts_with<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldLike<'rhs, String, Any>>::LiCond<I>
where
FieldType!(I): FieldLike<'rhs, String, Any>,
{
self.like(format!("{}%", escape_like(rhs)))
}
pub fn ends_with<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldLike<'rhs, String, Any>>::LiCond<I>
where
FieldType!(I): FieldLike<'rhs, String, Any>,
{
self.like(format!("%{}", escape_like(rhs)))
}
pub fn regexp<'rhs, Rhs: 'rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldRegexp<'rhs, Rhs, Any>>::ReCond<I>
where
FieldType!(I): FieldRegexp<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_regexp(self, rhs)
}
pub fn not_regexp<'rhs, Rhs: 'rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldRegexp<'rhs, Rhs, Any>>::NrCond<I>
where
FieldType!(I): FieldRegexp<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_not_regexp(self, rhs)
}
pub fn count(self) -> AggregatedColumn<I, i64>
where
FieldType!(I): FieldCount,
{
AggregatedColumn {
sql: SelectAggregator::Count,
alias: "count",
field: self,
result: PhantomData,
}
}
pub fn sum(self) -> AggregatedColumn<I, <FieldType!(I) as FieldSum>::Result>
where
FieldType!(I): FieldSum,
{
AggregatedColumn {
sql: SelectAggregator::Sum,
alias: "sum",
field: self,
result: PhantomData,
}
}
pub fn avg(self) -> AggregatedColumn<I, Option<f64>>
where
FieldType!(I): FieldAvg,
{
AggregatedColumn {
sql: SelectAggregator::Avg,
alias: "avg",
field: self,
result: PhantomData,
}
}
pub fn max(self) -> AggregatedColumn<I, <FieldType!(I) as FieldMax>::Result>
where
FieldType!(I): FieldMax,
{
AggregatedColumn {
sql: SelectAggregator::Max,
alias: "max",
field: self,
result: PhantomData,
}
}
pub fn min(self) -> AggregatedColumn<I, <FieldType!(I) as FieldMin>::Result>
where
FieldType!(I): FieldMin,
{
AggregatedColumn {
sql: SelectAggregator::Min,
alias: "min",
field: self,
result: PhantomData,
}
}
}
#[cfg(feature = "postgres-only")]
impl<I: FieldProxyImpl> FieldProxy<I> {
pub fn like_ignore_case<'rhs, Rhs: 'rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldILike<'rhs, Rhs, Any>>::IliCond<I>
where
FieldType!(I): FieldILike<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_ilike(self, rhs)
}
pub fn not_like_ignore_case<'rhs, Rhs: 'rhs, Any>(
self,
rhs: Rhs,
) -> <FieldType!(I) as FieldILike<'rhs, Rhs, Any>>::NilCond<I>
where
FieldType!(I): FieldILike<'rhs, Rhs, Any>,
{
<FieldType!(I)>::field_not_ilike(self, rhs)
}
pub fn contains_ignore_case<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldILike<'rhs, String, Any>>::IliCond<I>
where
FieldType!(I): FieldILike<'rhs, String, Any>,
{
self.like_ignore_case(format!("%{}%", escape_like(rhs)))
}
pub fn starts_with_ignore_case<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldILike<'rhs, String, Any>>::IliCond<I>
where
FieldType!(I): FieldILike<'rhs, String, Any>,
{
self.like_ignore_case(format!("{}%", escape_like(rhs)))
}
pub fn ends_with_ignore_case<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldILike<'rhs, String, Any>>::IliCond<I>
where
FieldType!(I): FieldILike<'rhs, String, Any>,
{
self.like_ignore_case(format!("%{}", escape_like(rhs)))
}
pub fn equals_ignore_case<'rhs, Any>(
self,
rhs: &str,
) -> <FieldType!(I) as FieldILike<'rhs, String, Any>>::IliCond<I>
where
FieldType!(I): FieldILike<'rhs, String, Any>,
{
self.like_ignore_case(escape_like(rhs))
}
}
#[cfg(feature = "postgres-only")]
impl<'a, I> FieldProxy<I>
where
I: FieldProxyImpl,
I::Field: Field<Type = IpNetwork>,
{
pub fn net_contained_in(self, rhs: IpNetwork) -> Binary<Column<I>, Value<'a>> {
Binary {
operator: BinaryOperator::Contained,
fst_arg: Column(self),
snd_arg: Value::IpNetwork(rhs),
}
}
pub fn net_contained_in_or_equals(self, rhs: IpNetwork) -> Binary<Column<I>, Value<'a>> {
Binary {
operator: BinaryOperator::ContainedOrEquals,
fst_arg: Column(self),
snd_arg: Value::IpNetwork(rhs),
}
}
pub fn net_contains(self, rhs: IpNetwork) -> Binary<Column<I>, Value<'a>> {
Binary {
operator: BinaryOperator::Contains,
fst_arg: Column(self),
snd_arg: Value::IpNetwork(rhs),
}
}
pub fn net_contains_or_equals(self, rhs: IpNetwork) -> Binary<Column<I>, Value<'a>> {
Binary {
operator: BinaryOperator::ContainsOrEquals,
fst_arg: Column(self),
snd_arg: Value::IpNetwork(rhs),
}
}
}
unsafe impl<T> Send for FieldProxy<T> {}
unsafe impl<T> Sync for FieldProxy<T> {}
impl<T> Clone for FieldProxy<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for FieldProxy<T> {}
pub trait FieldProxyImpl: 'static {
sealed!(trait);
type Field: Field;
type Path: Path;
}
impl<F, P> FieldProxyImpl for (F, P)
where
F: Field,
P: Path,
{
sealed!(impl);
type Field = F;
type Path = P;
}
pub const fn new<I: FieldProxyImpl>() -> FieldProxy<I> {
FieldProxy(PhantomData)
}
pub const fn index<I: FieldProxyImpl>(_: fn() -> FieldProxy<I>) -> usize {
<I::Field as Field>::INDEX
}
pub const fn columns<T: FieldProxyImpl>(
_: fn() -> FieldProxy<T>,
) -> FieldColumns<<T::Field as Field>::Type, ColumnName> {
<T::Field as Field>::EFFECTIVE_NAMES
}
fn escape_like(string: &str) -> String {
string
.replace('\\', r"\\")
.replace('%', r"\%")
.replace('_', r"\_")
}