1use std::marker::PhantomData;
2
3pub use cratestack_policy::RelationQuantifier;
4
5use crate::{IntoSqlValue, SqlValue, order::OrderTarget, values::FilterValue};
6
7#[derive(Debug, Clone, Copy)]
8pub struct FieldRef<M, T> {
9 column: &'static str,
10 _marker: PhantomData<fn() -> (M, T)>,
11}
12
13impl<M, T> FieldRef<M, T> {
14 pub const fn new(column: &'static str) -> Self {
15 Self {
16 column,
17 _marker: PhantomData,
18 }
19 }
20
21 pub fn asc(self) -> crate::OrderClause {
22 crate::OrderClause {
23 target: OrderTarget::Column(self.column),
24 direction: crate::SortDirection::Asc,
25 }
26 }
27
28 pub fn desc(self) -> crate::OrderClause {
29 crate::OrderClause {
30 target: OrderTarget::Column(self.column),
31 direction: crate::SortDirection::Desc,
32 }
33 }
34}
35
36impl<M, T> FieldRef<M, T> {
37 pub fn eq<V>(self, value: V) -> Filter
38 where
39 V: IntoSqlValue,
40 {
41 Filter::single(self.column, FilterOp::Eq, value)
42 }
43
44 pub fn ne<V>(self, value: V) -> Filter
45 where
46 V: IntoSqlValue,
47 {
48 Filter::single(self.column, FilterOp::Ne, value)
49 }
50
51 pub fn in_<I, V>(self, values: I) -> Filter
52 where
53 I: IntoIterator<Item = V>,
54 V: IntoSqlValue,
55 {
56 Filter {
57 column: self.column,
58 op: FilterOp::In,
59 value: FilterValue::Many(
60 values
61 .into_iter()
62 .map(IntoSqlValue::into_sql_value)
63 .collect(),
64 ),
65 }
66 }
67
68 pub fn lt<V>(self, value: V) -> Filter
69 where
70 V: IntoSqlValue,
71 {
72 Filter::single(self.column, FilterOp::Lt, value)
73 }
74
75 pub fn lte<V>(self, value: V) -> Filter
76 where
77 V: IntoSqlValue,
78 {
79 Filter::single(self.column, FilterOp::Lte, value)
80 }
81
82 pub fn gt<V>(self, value: V) -> Filter
83 where
84 V: IntoSqlValue,
85 {
86 Filter::single(self.column, FilterOp::Gt, value)
87 }
88
89 pub fn gte<V>(self, value: V) -> Filter
90 where
91 V: IntoSqlValue,
92 {
93 Filter::single(self.column, FilterOp::Gte, value)
94 }
95}
96
97impl<M> FieldRef<M, bool> {
98 pub fn is_true(self) -> Filter {
99 self.eq(true)
100 }
101
102 pub fn is_false(self) -> Filter {
103 self.eq(false)
104 }
105}
106
107impl<M> FieldRef<M, String> {
108 pub fn contains(self, value: impl Into<String>) -> Filter {
109 Filter::string_pattern(self.column, FilterOp::Contains, "%{}%", value)
110 }
111
112 pub fn starts_with(self, value: impl Into<String>) -> Filter {
113 Filter::string_pattern(self.column, FilterOp::StartsWith, "{}%", value)
114 }
115}
116
117impl<M, T> FieldRef<M, Option<T>> {
118 pub fn is_null(self) -> Filter {
119 Filter {
120 column: self.column,
121 op: FilterOp::IsNull,
122 value: FilterValue::None,
123 }
124 }
125
126 pub fn is_not_null(self) -> Filter {
127 Filter {
128 column: self.column,
129 op: FilterOp::IsNotNull,
130 value: FilterValue::None,
131 }
132 }
133}
134
135impl<M> FieldRef<M, Option<String>> {
136 pub fn contains(self, value: impl Into<String>) -> Filter {
137 Filter::string_pattern(self.column, FilterOp::Contains, "%{}%", value)
138 }
139
140 pub fn starts_with(self, value: impl Into<String>) -> Filter {
141 Filter::string_pattern(self.column, FilterOp::StartsWith, "{}%", value)
142 }
143}
144
145#[derive(Debug, Clone, PartialEq)]
146pub struct Filter {
147 pub(crate) column: &'static str,
148 pub(crate) op: FilterOp,
149 pub(crate) value: FilterValue,
150}
151
152impl Filter {
153 fn single<V>(column: &'static str, op: FilterOp, value: V) -> Self
154 where
155 V: IntoSqlValue,
156 {
157 Self {
158 column,
159 op,
160 value: FilterValue::Single(value.into_sql_value()),
161 }
162 }
163
164 fn string_pattern(
165 column: &'static str,
166 op: FilterOp,
167 pattern: &str,
168 value: impl Into<String>,
169 ) -> Self {
170 Self {
171 column,
172 op,
173 value: FilterValue::Single(SqlValue::String(pattern.replacen("{}", &value.into(), 1))),
174 }
175 }
176}
177
178#[derive(Debug, Clone, PartialEq)]
179pub struct RelationFilter {
180 pub(crate) quantifier: RelationQuantifier,
181 pub(crate) parent_table: &'static str,
182 pub(crate) parent_column: &'static str,
183 pub(crate) related_table: &'static str,
184 pub(crate) related_column: &'static str,
185 pub(crate) filter: Box<FilterExpr>,
186}
187
188#[derive(Debug, Clone, PartialEq)]
189pub enum FilterExpr {
190 Filter(Filter),
191 All(Vec<FilterExpr>),
192 Any(Vec<FilterExpr>),
193 Not(Box<FilterExpr>),
194 Relation(RelationFilter),
195}
196
197impl From<Filter> for FilterExpr {
198 fn from(value: Filter) -> Self {
199 Self::Filter(value)
200 }
201}
202
203impl RelationFilter {
204 pub fn new(
205 quantifier: RelationQuantifier,
206 parent_table: &'static str,
207 parent_column: &'static str,
208 related_table: &'static str,
209 related_column: &'static str,
210 filter: FilterExpr,
211 ) -> Self {
212 Self {
213 quantifier,
214 parent_table,
215 parent_column,
216 related_table,
217 related_column,
218 filter: Box::new(filter),
219 }
220 }
221}
222
223impl FilterExpr {
224 pub fn all(filters: impl IntoIterator<Item = FilterExpr>) -> Self {
225 Self::All(filters.into_iter().collect())
226 }
227
228 pub fn any(filters: impl IntoIterator<Item = FilterExpr>) -> Self {
229 Self::Any(filters.into_iter().collect())
230 }
231
232 pub fn not(self) -> Self {
233 match self {
234 Self::Not(inner) => *inner,
235 inner => Self::Not(Box::new(inner)),
236 }
237 }
238
239 pub fn and(self, other: impl Into<FilterExpr>) -> Self {
240 match (self, other.into()) {
241 (Self::All(mut left), Self::All(right)) => {
242 left.extend(right);
243 Self::All(left)
244 }
245 (Self::All(mut left), right) => {
246 left.push(right);
247 Self::All(left)
248 }
249 (left, Self::All(mut right)) => {
250 let mut filters = vec![left];
251 filters.append(&mut right);
252 Self::All(filters)
253 }
254 (left, right) => Self::All(vec![left, right]),
255 }
256 }
257
258 pub fn or(self, other: impl Into<FilterExpr>) -> Self {
259 match (self, other.into()) {
260 (Self::Any(mut left), Self::Any(right)) => {
261 left.extend(right);
262 Self::Any(left)
263 }
264 (Self::Any(mut left), right) => {
265 left.push(right);
266 Self::Any(left)
267 }
268 (left, Self::Any(mut right)) => {
269 let mut filters = vec![left];
270 filters.append(&mut right);
271 Self::Any(filters)
272 }
273 (left, right) => Self::Any(vec![left, right]),
274 }
275 }
276
277 pub fn relation(
278 parent_table: &'static str,
279 parent_column: &'static str,
280 related_table: &'static str,
281 related_column: &'static str,
282 filter: FilterExpr,
283 ) -> Self {
284 Self::Relation(RelationFilter::new(
285 RelationQuantifier::ToOne,
286 parent_table,
287 parent_column,
288 related_table,
289 related_column,
290 filter,
291 ))
292 }
293
294 pub fn relation_some(
295 parent_table: &'static str,
296 parent_column: &'static str,
297 related_table: &'static str,
298 related_column: &'static str,
299 filter: FilterExpr,
300 ) -> Self {
301 Self::Relation(RelationFilter::new(
302 RelationQuantifier::Some,
303 parent_table,
304 parent_column,
305 related_table,
306 related_column,
307 filter,
308 ))
309 }
310
311 pub fn relation_every(
312 parent_table: &'static str,
313 parent_column: &'static str,
314 related_table: &'static str,
315 related_column: &'static str,
316 filter: FilterExpr,
317 ) -> Self {
318 Self::Relation(RelationFilter::new(
319 RelationQuantifier::Every,
320 parent_table,
321 parent_column,
322 related_table,
323 related_column,
324 filter,
325 ))
326 }
327
328 pub fn relation_none(
329 parent_table: &'static str,
330 parent_column: &'static str,
331 related_table: &'static str,
332 related_column: &'static str,
333 filter: FilterExpr,
334 ) -> Self {
335 Self::Relation(RelationFilter::new(
336 RelationQuantifier::None,
337 parent_table,
338 parent_column,
339 related_table,
340 related_column,
341 filter,
342 ))
343 }
344}
345
346#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub(crate) enum FilterOp {
348 Eq,
349 Ne,
350 Lt,
351 Lte,
352 Gt,
353 Gte,
354 In,
355 Contains,
356 StartsWith,
357 IsNull,
358 IsNotNull,
359}