real_time_sqlx/
queries.rs

1//! Query system for real-time SQLX
2
3use serialize::{Condition, Constraint, ConstraintValue, FinalType, Operator, QueryTree};
4
5use crate::{
6    operations::serialize::JsonObject,
7    utils::{sql_ilike, sql_like},
8};
9
10pub mod display;
11pub mod serialize;
12
13// ************************************************************************* //
14//                        QUERY SYSTEM IMPLEMENTATION                        //
15// ************************************************************************* //
16
17/// Comparing 2 final types
18impl FinalType {
19    /// Compare self (left side) with another final type (right side) using an operator
20    pub fn compare(&self, other: &FinalType, operator: &Operator) -> bool {
21        match operator {
22            Operator::Equal => self.equals(other),
23            Operator::LessThan => self.less_than(other),
24            Operator::GreaterThan => self.greater_than(other),
25            Operator::LessThanOrEqual => self.less_than_or_equal(other),
26            Operator::GreaterThanOrEqual => self.greater_than_or_equal(other),
27            Operator::NotEqual => !self.equals(other),
28            Operator::Like => match (self, other) {
29                (FinalType::String(s), FinalType::String(t)) => sql_like(t, s),
30                _ => false,
31            },
32            Operator::ILike => match (self, other) {
33                (FinalType::String(s), FinalType::String(t)) => sql_ilike(t, s),
34                _ => false,
35            },
36            _ => panic!("Invalid operator {} for comparison", operator),
37        }
38    }
39
40    // Particularized implementations
41
42    /// &self == other
43    pub fn equals(&self, other: &FinalType) -> bool {
44        match (self, other) {
45            (FinalType::Number(n), FinalType::Number(m)) => {
46                if n.is_f64() && m.is_f64() {
47                    n.as_f64().unwrap() == m.as_f64().unwrap()
48                } else if n.is_i64() && m.is_i64() {
49                    n.as_i64().unwrap() == m.as_i64().unwrap()
50                } else {
51                    false
52                }
53            }
54            (FinalType::String(s), FinalType::String(t)) => s == t,
55            (FinalType::Bool(b), FinalType::Bool(c)) => b == c,
56            (FinalType::Null, FinalType::Null) => true,
57            _ => false,
58        }
59    }
60
61    /// &self < other
62    pub fn less_than(&self, other: &FinalType) -> bool {
63        match (self, other) {
64            (FinalType::Number(n), FinalType::Number(m)) => {
65                if n.is_f64() && m.is_f64() {
66                    n.as_f64().unwrap() < m.as_f64().unwrap()
67                } else if n.is_i64() && m.is_i64() {
68                    n.as_i64().unwrap() < m.as_i64().unwrap()
69                } else {
70                    false
71                }
72            }
73            (FinalType::String(s), FinalType::String(t)) => s < t,
74            (FinalType::Bool(b), FinalType::Bool(c)) => b < c,
75            _ => false,
76        }
77    }
78
79    /// &self > other
80    pub fn greater_than(&self, other: &FinalType) -> bool {
81        match (self, other) {
82            (FinalType::Number(n), FinalType::Number(m)) => {
83                if n.is_f64() && m.is_f64() {
84                    n.as_f64().unwrap() > m.as_f64().unwrap()
85                } else if n.is_i64() && m.is_i64() {
86                    n.as_i64().unwrap() > m.as_i64().unwrap()
87                } else {
88                    false
89                }
90            }
91            (FinalType::String(s), FinalType::String(t)) => s > t,
92            (FinalType::Bool(b), FinalType::Bool(c)) => b > c,
93            _ => false,
94        }
95    }
96
97    /// &self <= other
98    pub fn less_than_or_equal(&self, other: &FinalType) -> bool {
99        self.less_than(other) || self.equals(other)
100    }
101
102    /// &self >= other
103    pub fn greater_than_or_equal(&self, other: &FinalType) -> bool {
104        self.greater_than(other) || self.equals(other)
105    }
106}
107
108impl ConstraintValue {
109    /// Compare a constraint value with a final type (a constraint value can be a list of final types)
110    /// NOTE : assume that the ConstraintValue is always on the right side of the comparison
111    /// (for instance with the operator IN)
112    pub fn compare(&self, other: &FinalType, operator: &Operator) -> bool {
113        match self {
114            ConstraintValue::Final(final_type) => final_type.compare(other, operator),
115            ConstraintValue::List(list) => match operator {
116                Operator::In => {
117                    for value in list {
118                        if value.compare(other, &Operator::Equal) {
119                            return true;
120                        }
121                    }
122                    false
123                }
124                _ => panic!("Invalid operator {} for list comparison", operator),
125            },
126        }
127    }
128}
129
130// ************************************************************************* //
131//                       CHECKS AGAINST JSON OBJECT                          //
132// ************************************************************************* //
133
134pub trait Checkable {
135    fn check(&self, object: &JsonObject) -> bool;
136}
137
138impl Checkable for Constraint {
139    /// Check if a constraint is satisfied by a JSON object
140    fn check(&self, object: &JsonObject) -> bool {
141        let value = object
142            .get(&self.column)
143            .expect("Column not found in JSON object");
144
145        let final_type = FinalType::try_from(value.clone())
146            .expect(format!("Incompatible value for column: {value}").as_str());
147
148        self.value.compare(&final_type, &self.operator)
149    }
150}
151
152impl Checkable for Condition {
153    /// Check if a condition is satisfied by a JSON object
154    fn check(&self, object: &JsonObject) -> bool {
155        match self {
156            Condition::Single { constraint } => constraint.check(object),
157            Condition::And { conditions } => {
158                for condition in conditions {
159                    if !condition.check(object) {
160                        return false;
161                    }
162                }
163                true
164            }
165            Condition::Or { conditions } => {
166                for condition in conditions {
167                    if condition.check(object) {
168                        return true;
169                    }
170                }
171                false
172            }
173        }
174    }
175}
176
177impl Checkable for QueryTree {
178    /// Check if a query is satisfied by a JSON object
179    fn check(&self, object: &JsonObject) -> bool {
180        if let Some(condition) = &self.condition {
181            condition.check(object)
182        } else {
183            true
184        }
185    }
186}