1use crate::sql::{
2 ArrayRangeOperator, FilterCondition, FilterOperator, IntoSqlParam, IsValue, ParamStore,
3 PatternOperator, TextSearchType, validate_column_name,
4};
5pub trait Filterable: Sized {
9 fn filters_mut(&mut self) -> &mut Vec<FilterCondition>;
11 fn params_mut(&mut self) -> &mut ParamStore;
13
14 fn eq(mut self, column: &str, value: impl IntoSqlParam) -> Self {
16 if let Err(e) = validate_column_name(column) {
17 tracing::error!("Invalid column name in eq filter: {e}");
18 return self;
19 }
20 let idx = self.params_mut().push_value(value);
21 self.filters_mut().push(FilterCondition::Comparison {
22 column: column.to_string(),
23 operator: FilterOperator::Eq,
24 param_index: idx,
25 });
26 self
27 }
28
29 fn neq(mut self, column: &str, value: impl IntoSqlParam) -> Self {
31 if let Err(e) = validate_column_name(column) {
32 tracing::error!("Invalid column name in neq filter: {e}");
33 return self;
34 }
35 let idx = self.params_mut().push_value(value);
36 self.filters_mut().push(FilterCondition::Comparison {
37 column: column.to_string(),
38 operator: FilterOperator::Neq,
39 param_index: idx,
40 });
41 self
42 }
43
44 fn gt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
46 if let Err(e) = validate_column_name(column) {
47 tracing::error!("Invalid column name in gt filter: {e}");
48 return self;
49 }
50 let idx = self.params_mut().push_value(value);
51 self.filters_mut().push(FilterCondition::Comparison {
52 column: column.to_string(),
53 operator: FilterOperator::Gt,
54 param_index: idx,
55 });
56 self
57 }
58
59 fn gte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
61 if let Err(e) = validate_column_name(column) {
62 tracing::error!("Invalid column name in gte filter: {e}");
63 return self;
64 }
65 let idx = self.params_mut().push_value(value);
66 self.filters_mut().push(FilterCondition::Comparison {
67 column: column.to_string(),
68 operator: FilterOperator::Gte,
69 param_index: idx,
70 });
71 self
72 }
73
74 fn lt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
76 if let Err(e) = validate_column_name(column) {
77 tracing::error!("Invalid column name in lt filter: {e}");
78 return self;
79 }
80 let idx = self.params_mut().push_value(value);
81 self.filters_mut().push(FilterCondition::Comparison {
82 column: column.to_string(),
83 operator: FilterOperator::Lt,
84 param_index: idx,
85 });
86 self
87 }
88
89 fn lte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
91 if let Err(e) = validate_column_name(column) {
92 tracing::error!("Invalid column name in lte filter: {e}");
93 return self;
94 }
95 let idx = self.params_mut().push_value(value);
96 self.filters_mut().push(FilterCondition::Comparison {
97 column: column.to_string(),
98 operator: FilterOperator::Lte,
99 param_index: idx,
100 });
101 self
102 }
103
104 fn like(mut self, column: &str, pattern: impl IntoSqlParam) -> Self {
106 if let Err(e) = validate_column_name(column) {
107 tracing::error!("Invalid column name in like filter: {e}");
108 return self;
109 }
110 let idx = self.params_mut().push_value(pattern);
111 self.filters_mut().push(FilterCondition::Pattern {
112 column: column.to_string(),
113 operator: PatternOperator::Like,
114 param_index: idx,
115 });
116 self
117 }
118
119 fn ilike(mut self, column: &str, pattern: impl IntoSqlParam) -> Self {
121 if let Err(e) = validate_column_name(column) {
122 tracing::error!("Invalid column name in ilike filter: {e}");
123 return self;
124 }
125 let idx = self.params_mut().push_value(pattern);
126 self.filters_mut().push(FilterCondition::Pattern {
127 column: column.to_string(),
128 operator: PatternOperator::ILike,
129 param_index: idx,
130 });
131 self
132 }
133
134 fn is(mut self, column: &str, value: IsValue) -> Self {
136 if let Err(e) = validate_column_name(column) {
137 tracing::error!("Invalid column name in is filter: {e}");
138 return self;
139 }
140 self.filters_mut().push(FilterCondition::Is {
141 column: column.to_string(),
142 value,
143 });
144 self
145 }
146
147 fn in_<V: IntoSqlParam>(mut self, column: &str, values: Vec<V>) -> Self {
149 if let Err(e) = validate_column_name(column) {
150 tracing::error!("Invalid column name in in_ filter: {e}");
151 return self;
152 }
153 let indices: Vec<usize> = values
154 .into_iter()
155 .map(|v| self.params_mut().push_value(v))
156 .collect();
157 self.filters_mut().push(FilterCondition::In {
158 column: column.to_string(),
159 param_indices: indices,
160 });
161 self
162 }
163
164 fn contains(mut self, column: &str, value: impl IntoSqlParam) -> Self {
166 if let Err(e) = validate_column_name(column) {
167 tracing::error!("Invalid column name in contains filter: {e}");
168 return self;
169 }
170 let idx = self.params_mut().push_value(value);
171 self.filters_mut().push(FilterCondition::ArrayRange {
172 column: column.to_string(),
173 operator: ArrayRangeOperator::Contains,
174 param_index: idx,
175 });
176 self
177 }
178
179 fn contained_by(mut self, column: &str, value: impl IntoSqlParam) -> Self {
181 if let Err(e) = validate_column_name(column) {
182 tracing::error!("Invalid column name in contained_by filter: {e}");
183 return self;
184 }
185 let idx = self.params_mut().push_value(value);
186 self.filters_mut().push(FilterCondition::ArrayRange {
187 column: column.to_string(),
188 operator: ArrayRangeOperator::ContainedBy,
189 param_index: idx,
190 });
191 self
192 }
193
194 fn overlaps(mut self, column: &str, value: impl IntoSqlParam) -> Self {
196 if let Err(e) = validate_column_name(column) {
197 tracing::error!("Invalid column name in overlaps filter: {e}");
198 return self;
199 }
200 let idx = self.params_mut().push_value(value);
201 self.filters_mut().push(FilterCondition::ArrayRange {
202 column: column.to_string(),
203 operator: ArrayRangeOperator::Overlaps,
204 param_index: idx,
205 });
206 self
207 }
208
209 fn range_gt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
211 if let Err(e) = validate_column_name(column) {
212 tracing::error!("Invalid column name in range_gt filter: {e}");
213 return self;
214 }
215 let idx = self.params_mut().push_value(value);
216 self.filters_mut().push(FilterCondition::ArrayRange {
217 column: column.to_string(),
218 operator: ArrayRangeOperator::RangeGt,
219 param_index: idx,
220 });
221 self
222 }
223
224 fn range_gte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
226 if let Err(e) = validate_column_name(column) {
227 tracing::error!("Invalid column name in range_gte filter: {e}");
228 return self;
229 }
230 let idx = self.params_mut().push_value(value);
231 self.filters_mut().push(FilterCondition::ArrayRange {
232 column: column.to_string(),
233 operator: ArrayRangeOperator::RangeGte,
234 param_index: idx,
235 });
236 self
237 }
238
239 fn range_lt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
241 if let Err(e) = validate_column_name(column) {
242 tracing::error!("Invalid column name in range_lt filter: {e}");
243 return self;
244 }
245 let idx = self.params_mut().push_value(value);
246 self.filters_mut().push(FilterCondition::ArrayRange {
247 column: column.to_string(),
248 operator: ArrayRangeOperator::RangeLt,
249 param_index: idx,
250 });
251 self
252 }
253
254 fn range_lte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
256 if let Err(e) = validate_column_name(column) {
257 tracing::error!("Invalid column name in range_lte filter: {e}");
258 return self;
259 }
260 let idx = self.params_mut().push_value(value);
261 self.filters_mut().push(FilterCondition::ArrayRange {
262 column: column.to_string(),
263 operator: ArrayRangeOperator::RangeLte,
264 param_index: idx,
265 });
266 self
267 }
268
269 fn range_adjacent(mut self, column: &str, value: impl IntoSqlParam) -> Self {
271 if let Err(e) = validate_column_name(column) {
272 tracing::error!("Invalid column name in range_adjacent filter: {e}");
273 return self;
274 }
275 let idx = self.params_mut().push_value(value);
276 self.filters_mut().push(FilterCondition::ArrayRange {
277 column: column.to_string(),
278 operator: ArrayRangeOperator::RangeAdjacent,
279 param_index: idx,
280 });
281 self
282 }
283
284 fn text_search(
286 mut self,
287 column: &str,
288 query: impl IntoSqlParam,
289 search_type: TextSearchType,
290 config: Option<&str>,
291 ) -> Self {
292 if let Err(e) = validate_column_name(column) {
293 tracing::error!("Invalid column name in text_search filter: {e}");
294 return self;
295 }
296 let idx = self.params_mut().push_value(query);
297 self.filters_mut().push(FilterCondition::TextSearch {
298 column: column.to_string(),
299 query_param_index: idx,
300 config: config.map(|s| s.to_string()),
301 search_type,
302 });
303 self
304 }
305
306 fn not(mut self, f: impl FnOnce(FilterCollector) -> FilterCollector) -> Self {
308 let collector = f(FilterCollector::new(self.params_mut()));
309 if let Some(condition) = collector.into_single_condition() {
310 self.filters_mut().push(FilterCondition::Not(Box::new(condition)));
311 }
312 self
313 }
314
315 fn or_filter(mut self, f: impl FnOnce(FilterCollector) -> FilterCollector) -> Self {
317 let collector = f(FilterCollector::new(self.params_mut()));
318 let conditions = collector.into_conditions();
319 if !conditions.is_empty() {
320 self.filters_mut().push(FilterCondition::Or(conditions));
321 }
322 self
323 }
324
325 fn match_filter(mut self, pairs: Vec<(&str, impl IntoSqlParam + Clone)>) -> Self {
327 let conditions: Vec<(String, usize)> = pairs
328 .into_iter()
329 .filter_map(|(col, val)| {
330 if let Err(e) = validate_column_name(col) {
331 tracing::error!("Invalid column name in match_filter: {e}");
332 return None;
333 }
334 let idx = self.params_mut().push_value(val);
335 Some((col.to_string(), idx))
336 })
337 .collect();
338 if !conditions.is_empty() {
339 self.filters_mut().push(FilterCondition::Match { conditions });
340 }
341 self
342 }
343
344 fn filter(mut self, raw_sql: &str) -> Self {
346 self.filters_mut()
347 .push(FilterCondition::Raw(raw_sql.to_string()));
348 self
349 }
350}
351
352pub struct FilterCollector<'a> {
354 filters: Vec<FilterCondition>,
355 params: &'a mut ParamStore,
356}
357
358impl<'a> FilterCollector<'a> {
359 pub fn new(params: &'a mut ParamStore) -> Self {
360 Self {
361 filters: Vec::new(),
362 params,
363 }
364 }
365
366 pub fn eq(mut self, column: &str, value: impl IntoSqlParam) -> Self {
367 if validate_column_name(column).is_ok() {
368 let idx = self.params.push_value(value);
369 self.filters.push(FilterCondition::Comparison {
370 column: column.to_string(),
371 operator: FilterOperator::Eq,
372 param_index: idx,
373 });
374 }
375 self
376 }
377
378 pub fn neq(mut self, column: &str, value: impl IntoSqlParam) -> Self {
379 if validate_column_name(column).is_ok() {
380 let idx = self.params.push_value(value);
381 self.filters.push(FilterCondition::Comparison {
382 column: column.to_string(),
383 operator: FilterOperator::Neq,
384 param_index: idx,
385 });
386 }
387 self
388 }
389
390 pub fn gt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
391 if validate_column_name(column).is_ok() {
392 let idx = self.params.push_value(value);
393 self.filters.push(FilterCondition::Comparison {
394 column: column.to_string(),
395 operator: FilterOperator::Gt,
396 param_index: idx,
397 });
398 }
399 self
400 }
401
402 pub fn gte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
403 if validate_column_name(column).is_ok() {
404 let idx = self.params.push_value(value);
405 self.filters.push(FilterCondition::Comparison {
406 column: column.to_string(),
407 operator: FilterOperator::Gte,
408 param_index: idx,
409 });
410 }
411 self
412 }
413
414 pub fn lt(mut self, column: &str, value: impl IntoSqlParam) -> Self {
415 if validate_column_name(column).is_ok() {
416 let idx = self.params.push_value(value);
417 self.filters.push(FilterCondition::Comparison {
418 column: column.to_string(),
419 operator: FilterOperator::Lt,
420 param_index: idx,
421 });
422 }
423 self
424 }
425
426 pub fn lte(mut self, column: &str, value: impl IntoSqlParam) -> Self {
427 if validate_column_name(column).is_ok() {
428 let idx = self.params.push_value(value);
429 self.filters.push(FilterCondition::Comparison {
430 column: column.to_string(),
431 operator: FilterOperator::Lte,
432 param_index: idx,
433 });
434 }
435 self
436 }
437
438 pub fn like(mut self, column: &str, pattern: impl IntoSqlParam) -> Self {
439 if validate_column_name(column).is_ok() {
440 let idx = self.params.push_value(pattern);
441 self.filters.push(FilterCondition::Pattern {
442 column: column.to_string(),
443 operator: PatternOperator::Like,
444 param_index: idx,
445 });
446 }
447 self
448 }
449
450 pub fn ilike(mut self, column: &str, pattern: impl IntoSqlParam) -> Self {
451 if validate_column_name(column).is_ok() {
452 let idx = self.params.push_value(pattern);
453 self.filters.push(FilterCondition::Pattern {
454 column: column.to_string(),
455 operator: PatternOperator::ILike,
456 param_index: idx,
457 });
458 }
459 self
460 }
461
462 pub fn is(mut self, column: &str, value: IsValue) -> Self {
463 if validate_column_name(column).is_ok() {
464 self.filters.push(FilterCondition::Is {
465 column: column.to_string(),
466 value,
467 });
468 }
469 self
470 }
471
472 pub fn into_conditions(self) -> Vec<FilterCondition> {
473 self.filters
474 }
475
476 pub fn into_single_condition(self) -> Option<FilterCondition> {
477 let mut filters = self.filters;
478 if filters.len() == 1 {
479 Some(filters.remove(0))
480 } else if filters.is_empty() {
481 None
482 } else {
483 Some(FilterCondition::And(filters))
484 }
485 }
486}