Skip to main content

cratestack_sql/filter/
expr.rs

1pub use cratestack_policy::RelationQuantifier;
2
3use super::coalesce::CoalesceFilter;
4use super::filter::Filter;
5use super::json::JsonFilter;
6use super::spatial::SpatialFilter;
7
8#[derive(Debug, Clone, PartialEq)]
9pub struct RelationFilter {
10    pub quantifier: RelationQuantifier,
11    pub parent_table: &'static str,
12    pub parent_column: &'static str,
13    pub related_table: &'static str,
14    pub related_column: &'static str,
15    pub filter: Box<FilterExpr>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum FilterExpr {
20    Filter(Filter),
21    All(Vec<FilterExpr>),
22    Any(Vec<FilterExpr>),
23    Not(Box<FilterExpr>),
24    Relation(RelationFilter),
25    /// `COALESCE(col_a, col_b, ...) op value` — built via
26    /// [`super::coalesce::coalesce`].
27    Coalesce(CoalesceFilter),
28    /// JSON / JSONB column predicates — see [`JsonFilter`]. Built via
29    /// `FieldRef::json_has_key(...)` and
30    /// `FieldRef::json_get_text(...).<cmp>(...)`.
31    Json(JsonFilter),
32    /// PostGIS spatial predicates — see [`SpatialFilter`]. Built via
33    /// `FieldRef::covers_geography(...)` /
34    /// `FieldRef::dwithin_geography(...)`. PG-only; the embedded
35    /// rusqlite backend doesn't ship SpatiaLite by default, so its
36    /// renderer fails loud at codegen time.
37    Spatial(SpatialFilter),
38}
39
40impl From<Filter> for FilterExpr {
41    fn from(value: Filter) -> Self {
42        Self::Filter(value)
43    }
44}
45
46impl RelationFilter {
47    pub fn new(
48        quantifier: RelationQuantifier,
49        parent_table: &'static str,
50        parent_column: &'static str,
51        related_table: &'static str,
52        related_column: &'static str,
53        filter: FilterExpr,
54    ) -> Self {
55        Self {
56            quantifier,
57            parent_table,
58            parent_column,
59            related_table,
60            related_column,
61            filter: Box::new(filter),
62        }
63    }
64}
65
66impl FilterExpr {
67    pub fn all(filters: impl IntoIterator<Item = FilterExpr>) -> Self {
68        Self::All(filters.into_iter().collect())
69    }
70
71    pub fn any(filters: impl IntoIterator<Item = FilterExpr>) -> Self {
72        Self::Any(filters.into_iter().collect())
73    }
74
75    pub fn not(self) -> Self {
76        match self {
77            Self::Not(inner) => *inner,
78            inner => Self::Not(Box::new(inner)),
79        }
80    }
81
82    pub fn and(self, other: impl Into<FilterExpr>) -> Self {
83        match (self, other.into()) {
84            (Self::All(mut left), Self::All(right)) => {
85                left.extend(right);
86                Self::All(left)
87            }
88            (Self::All(mut left), right) => {
89                left.push(right);
90                Self::All(left)
91            }
92            (left, Self::All(mut right)) => {
93                let mut filters = vec![left];
94                filters.append(&mut right);
95                Self::All(filters)
96            }
97            (left, right) => Self::All(vec![left, right]),
98        }
99    }
100
101    pub fn or(self, other: impl Into<FilterExpr>) -> Self {
102        match (self, other.into()) {
103            (Self::Any(mut left), Self::Any(right)) => {
104                left.extend(right);
105                Self::Any(left)
106            }
107            (Self::Any(mut left), right) => {
108                left.push(right);
109                Self::Any(left)
110            }
111            (left, Self::Any(mut right)) => {
112                let mut filters = vec![left];
113                filters.append(&mut right);
114                Self::Any(filters)
115            }
116            (left, right) => Self::Any(vec![left, right]),
117        }
118    }
119
120    pub fn relation(
121        parent_table: &'static str,
122        parent_column: &'static str,
123        related_table: &'static str,
124        related_column: &'static str,
125        filter: FilterExpr,
126    ) -> Self {
127        Self::Relation(RelationFilter::new(
128            RelationQuantifier::ToOne,
129            parent_table,
130            parent_column,
131            related_table,
132            related_column,
133            filter,
134        ))
135    }
136
137    pub fn relation_some(
138        parent_table: &'static str,
139        parent_column: &'static str,
140        related_table: &'static str,
141        related_column: &'static str,
142        filter: FilterExpr,
143    ) -> Self {
144        Self::Relation(RelationFilter::new(
145            RelationQuantifier::Some,
146            parent_table,
147            parent_column,
148            related_table,
149            related_column,
150            filter,
151        ))
152    }
153
154    pub fn relation_every(
155        parent_table: &'static str,
156        parent_column: &'static str,
157        related_table: &'static str,
158        related_column: &'static str,
159        filter: FilterExpr,
160    ) -> Self {
161        Self::Relation(RelationFilter::new(
162            RelationQuantifier::Every,
163            parent_table,
164            parent_column,
165            related_table,
166            related_column,
167            filter,
168        ))
169    }
170
171    pub fn relation_none(
172        parent_table: &'static str,
173        parent_column: &'static str,
174        related_table: &'static str,
175        related_column: &'static str,
176        filter: FilterExpr,
177    ) -> Self {
178        Self::Relation(RelationFilter::new(
179            RelationQuantifier::None,
180            parent_table,
181            parent_column,
182            related_table,
183            related_column,
184            filter,
185        ))
186    }
187}