1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Not expected to be used directly.

use crate::fkey::ForeignKey;
use crate::query::{BoolExpr, Column, Expr, Join};
use crate::sqlval::{FieldType, IntoSql, SqlVal, ToSql};
use crate::DataObject;
use std::borrow::Borrow;
use std::cmp::{PartialEq, PartialOrd};
use std::marker::PhantomData;

macro_rules! binary_op {
    ($func_name:ident, $bound:path, $cond:ident) => {
        pub fn $func_name<U>(&self, val: &U) -> BoolExpr
        where
            T: $bound,
            U: ToSql,
        {
            BoolExpr::$cond(self.name, Expr::Val(val.to_sql()))
        }
    };
}

/// Marker trait to determine whether values can be compared.
///
/// Unlike `PartialEq`, handles `Option`, which we need for nullable
/// types. We would like to automatically implement it if PartialEq
/// is implemented, but we can't do that without specialization or
/// negative trait bounds.
pub trait DataEq<Rhs> {}
impl<T> DataEq<T> for Option<T> where T: PartialEq<T> + FieldType {}
impl<T> DataEq<T> for T where T: PartialEq<T> + FieldType {}

/// Marker trait to determine whether values can be compared.
/// Unlike `PartialOrd`, handles `Option`, which we need for nullable types.
pub trait DataOrd<Rhs> {}
impl<T> DataOrd<T> for Option<T> where T: PartialOrd<T> + FieldType {}
impl<T> DataOrd<T> for T where T: PartialOrd<T> + FieldType {}

/// Used to implement the `query!` and `filter!` macros.
pub struct FieldExpr<T>
where
    T: Into<SqlVal>,
{
    name: &'static str,
    phantom: PhantomData<T>,
}

impl<T> FieldExpr<T>
where
    T: Into<SqlVal>,
{
    pub fn new(name: &'static str) -> Self {
        FieldExpr {
            name,
            phantom: PhantomData,
        }
    }

    pub fn name(&self) -> &'static str {
        self.name
    }

    binary_op!(eq, std::cmp::PartialEq<U>, Eq);
    binary_op!(ne, std::cmp::PartialEq<U>, Ne);
    binary_op!(lt, DataOrd<U>, Lt);
    binary_op!(gt, DataOrd<U>, Gt);
    binary_op!(le, DataOrd<U>, Le);
    binary_op!(ge, DataOrd<U>, Ge);

    pub fn like<U>(&self, val: U) -> BoolExpr
    where
        U: ToSql,
    {
        BoolExpr::Like(self.name, Expr::Val(val.to_sql()))
    }
}
impl<F: DataObject> FieldExpr<ForeignKey<F>> {
    pub fn subfilter(&self, q: BoolExpr) -> BoolExpr {
        BoolExpr::Subquery {
            col: self.name,
            tbl2: F::TABLE,
            tbl2_col: F::PKCOL,
            expr: Box::new(q),
        }
    }
    pub fn subfilterpk(&self, pk: F::PKType) -> BoolExpr {
        self.subfilter(BoolExpr::Eq(
            F::PKCOL,
            crate::query::Expr::Val(pk.into_sql()),
        ))
    }
    pub fn fields(&self) -> F::Fields {
        F::Fields::default()
    }
}

pub struct ManyFieldExpr<O, T>
where
    O: DataObject, // owner
    T: DataObject, // owned
{
    many_table: &'static str,
    phantomo: PhantomData<O>,
    phantomt: PhantomData<T>,
}
impl<O, T> ManyFieldExpr<O, T>
where
    O: DataObject,
    T: DataObject,
{
    pub fn new(many_table: &'static str) -> Self {
        ManyFieldExpr {
            many_table,
            phantomo: PhantomData,
            phantomt: PhantomData,
        }
    }
    pub fn contains(&self, q: BoolExpr) -> BoolExpr {
        //let many_tbl = format!("{}_{}_Many", O::TABLE, self.name);
        BoolExpr::SubqueryJoin {
            col: O::PKCOL,
            tbl2: T::TABLE,
            col2: Column::new(self.many_table, "owner"),
            joins: vec![Join::Inner {
                join_table: self.many_table,
                col1: Column::new(self.many_table, "has"),
                col2: Column::new(T::TABLE, T::PKCOL),
            }],
            expr: Box::new(q),
        }
    }
    pub fn containspk(&self, pk: impl Borrow<<T::PKType as FieldType>::RefType>) -> BoolExpr {
        self.contains(BoolExpr::Eq(
            T::PKCOL,
            crate::query::Expr::Val(pk.borrow().to_sql()),
        ))
    }
    pub fn fields(&self) -> T::Fields {
        T::Fields::default()
    }
}