1use std::borrow::Cow;
2use crate::{IntoParams, Params};
3
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum FilterOperator {
6 Eq,
7 Ne,
8 Gt,
9 Lt,
10 Gte,
11 Lte,
12 Like,
16 ILike,
19 UnsafeLike,
24 In,
25 NotIn,
26 IsNull,
27 IsNotNull,
28 Between,
29 Contains,
32}
33
34#[derive(Clone, Debug, PartialEq)]
35pub enum FilterValue {
36 String(Cow<'static, str>),
37 Int(i64),
38 UInt(u64),
39 Float(f64),
40 Bool(bool),
41 Array(Vec<FilterValue>),
42 #[cfg(feature = "json")]
47 Json(sqlx_data_integration::JsonValue),
48
49 #[cfg(feature = "uuid")]
51 Uuid(sqlx_data_integration::Uuid),
52
53 #[cfg(feature = "chrono")]
55 DateTimeChrono(sqlx_data_integration::DateTime),
56 #[cfg(feature = "chrono")]
57 NaiveDateTime(sqlx_data_integration::NaiveDateTime),
58 #[cfg(feature = "chrono")]
59 NaiveDate(sqlx_data_integration::NaiveDate),
60 #[cfg(feature = "chrono")]
61 NaiveTime(sqlx_data_integration::NaiveTime),
62
63 #[cfg(all(feature = "time", not(feature = "chrono")))]
64 OffsetDateTime(sqlx_data_integration::DateTime),
65 #[cfg(all(feature = "time", not(feature = "chrono")))]
66 PrimitiveDateTime(sqlx_data_integration::PrimitiveDateTime),
67 #[cfg(all(feature = "time", not(feature = "chrono")))]
68 Date(sqlx_data_integration::Date),
69 #[cfg(all(feature = "time", not(feature = "chrono")))]
70 Time(sqlx_data_integration::Time),
71
72 #[cfg(feature = "rust_decimal")]
74 Decimal(sqlx_data_integration::Decimal),
75 #[cfg(feature = "bigdecimal")]
76 Decimal(sqlx_data_integration::Decimal),
77
78 #[cfg(feature = "ipnet")]
80 IpNet(sqlx_data_integration::IpNet),
81 #[cfg(feature = "ipnet")]
82 Ipv4Net(sqlx_data_integration::Ipv4Net),
83 #[cfg(feature = "ipnet")]
84 Ipv6Net(sqlx_data_integration::Ipv6Net),
85
86 #[cfg(feature = "ipnetwork")]
87 IpNetwork(sqlx_data_integration::IpNetwork),
88
89 #[cfg(feature = "mac_address")]
91 MacAddress(sqlx_data_integration::MacAddress),
92
93 #[cfg(feature = "bit-vec")]
95 BitVec(sqlx_data_integration::BitVec),
96 #[cfg(feature = "bstr")]
97 BStr(sqlx_data_integration::BString),
98
99 #[cfg(feature = "regexp")]
101 Regex(String), Blob(Vec<u8>),
104 Null,
105}
106
107#[derive(Clone, Debug, PartialEq)]
108pub struct Filter {
109 pub field: String,
110 pub operator: FilterOperator,
111 pub value: FilterValue,
112 pub not: bool,
122}
123
124impl Filter {
125 pub fn new(
126 field: impl Into<String>,
127 operator: FilterOperator,
128 value: impl Into<FilterValue>,
129 ) -> Self {
130 Self {
131 field: field.into(),
132 operator,
133 value: value.into(),
134 not: false,
135 }
136 }
137}
138
139#[derive(Clone, Debug, PartialEq, Default)]
140pub struct FilterParams {
141 pub filters: Vec<Filter>,
142}
143
144impl IntoParams for FilterParams {
145 fn into_params(self) -> Params {
146 Params {
147 filters: Some(self),
148 search: None,
149 sort_by: None,
150 pagination: None,
151 limit: None,
152 offset: None,
153 }
154 }
155}
156
157impl<'a, T> From<&'a [T]> for FilterValue
160where
161 T: Into<FilterValue> + Copy,
162{
163 fn from(slice: &'a [T]) -> Self {
164 FilterValue::Array(slice.iter().copied().map(Into::into).collect())
165 }
166}
167
168impl From<String> for FilterValue {
169 fn from(value: String) -> Self {
170 FilterValue::String(Cow::Owned(value))
171 }
172}
173
174impl From<&String> for FilterValue {
175 fn from(value: &String) -> Self {
176 FilterValue::String(Cow::Owned(value.to_owned()))
177 }
178}
179
180impl From<&str> for FilterValue {
181 fn from(value: &str) -> Self {
182 FilterValue::String(Cow::Owned(value.to_owned()))
183 }
184}
185
186impl From<i8> for FilterValue {
187 fn from(value: i8) -> Self {
188 FilterValue::Int(value as i64)
189 }
190}
191
192impl From<i16> for FilterValue {
193 fn from(value: i16) -> Self {
194 FilterValue::Int(value as i64)
195 }
196}
197
198impl From<i32> for FilterValue {
199 fn from(value: i32) -> Self {
200 FilterValue::Int(value as i64)
201 }
202}
203
204impl From<i64> for FilterValue {
205 fn from(value: i64) -> Self {
206 FilterValue::Int(value)
207 }
208}
209
210impl From<u8> for FilterValue {
211 fn from(value: u8) -> Self {
212 FilterValue::UInt(value as u64)
213 }
214}
215
216impl From<u16> for FilterValue {
217 fn from(value: u16) -> Self {
218 FilterValue::UInt(value as u64)
219 }
220}
221
222impl From<u32> for FilterValue {
223 fn from(value: u32) -> Self {
224 FilterValue::UInt(value as u64)
225 }
226}
227
228impl From<u64> for FilterValue {
229 fn from(value: u64) -> Self {
230 FilterValue::UInt(value)
231 }
232}
233
234impl From<f32> for FilterValue {
235 fn from(value: f32) -> Self {
236 FilterValue::Float(value as f64)
237 }
238}
239
240impl From<f64> for FilterValue {
241 fn from(value: f64) -> Self {
242 FilterValue::Float(value)
243 }
244}
245
246impl From<isize> for FilterValue {
247 fn from(value: isize) -> Self {
248 FilterValue::Int(value as i64)
249 }
250}
251
252impl From<usize> for FilterValue {
253 fn from(value: usize) -> Self {
254 FilterValue::UInt(value as u64)
255 }
256}
257
258impl From<bool> for FilterValue {
259 fn from(value: bool) -> Self {
260 FilterValue::Bool(value)
261 }
262}
263
264impl<T> From<Option<T>> for FilterValue
265where
266 T: Into<FilterValue>,
267{
268 fn from(value: Option<T>) -> Self {
269 match value {
270 Some(v) => v.into(),
271 None => FilterValue::Null,
272 }
273 }
274}
275
276impl<T> From<Vec<T>> for FilterValue
277where
278 T: Into<FilterValue>,
279{
280 fn from(vec: Vec<T>) -> Self {
281 FilterValue::Array(vec.into_iter().map(Into::into).collect())
282 }
283}
284
285impl<T, const N: usize> From<[T; N]> for FilterValue
286where
287 T: Into<FilterValue> + Copy,
288{
289 fn from(array: [T; N]) -> Self {
290 FilterValue::Array(array.iter().copied().map(Into::into).collect())
291 }
292}
293
294#[cfg(feature = "uuid")]
298impl From<sqlx_data_integration::Uuid> for FilterValue {
299 fn from(value: sqlx_data_integration::Uuid) -> Self {
300 FilterValue::Uuid(value)
301 }
302}
303
304#[cfg(feature = "chrono")]
306impl From<sqlx_data_integration::DateTime> for FilterValue {
307 fn from(value: sqlx_data_integration::DateTime) -> Self {
308 FilterValue::DateTimeChrono(value)
309 }
310}
311
312#[cfg(feature = "chrono")]
313impl From<sqlx_data_integration::NaiveDateTime> for FilterValue {
314 fn from(value: sqlx_data_integration::NaiveDateTime) -> Self {
315 FilterValue::NaiveDateTime(value)
316 }
317}
318
319#[cfg(feature = "chrono")]
320impl From<sqlx_data_integration::NaiveDate> for FilterValue {
321 fn from(value: sqlx_data_integration::NaiveDate) -> Self {
322 FilterValue::NaiveDate(value)
323 }
324}
325
326#[cfg(feature = "chrono")]
327impl From<sqlx_data_integration::NaiveTime> for FilterValue {
328 fn from(value: sqlx_data_integration::NaiveTime) -> Self {
329 FilterValue::NaiveTime(value)
330 }
331}
332
333#[cfg(all(feature = "time", not(feature = "chrono")))]
335impl From<sqlx_data_integration::DateTime> for FilterValue {
336 fn from(value: sqlx_data_integration::DateTime) -> Self {
337 FilterValue::OffsetDateTime(value)
338 }
339}
340
341#[cfg(all(feature = "time", not(feature = "chrono")))]
342impl From<sqlx_data_integration::PrimitiveDateTime> for FilterValue {
343 fn from(value: sqlx_data_integration::PrimitiveDateTime) -> Self {
344 FilterValue::PrimitiveDateTime(value)
345 }
346}
347
348#[cfg(all(feature = "time", not(feature = "chrono")))]
349impl From<sqlx_data_integration::Date> for FilterValue {
350 fn from(value: sqlx_data_integration::Date) -> Self {
351 FilterValue::Date(value)
352 }
353}
354
355#[cfg(all(feature = "time", not(feature = "chrono")))]
356impl From<sqlx_data_integration::Time> for FilterValue {
357 fn from(value: sqlx_data_integration::Time) -> Self {
358 FilterValue::Time(value)
359 }
360}
361
362#[cfg(all(feature = "rust_decimal", not(feature = "sqlite")))]
364impl From<sqlx_data_integration::Decimal> for FilterValue {
365 fn from(value: sqlx_data_integration::Decimal) -> Self {
366 FilterValue::RustDecimal(value)
367 }
368}
369
370#[cfg(all(feature = "bigdecimal", not(feature = "sqlite")))]
371impl From<sqlx_data_integration::Decimal> for FilterValue {
372 fn from(value: sqlx_data_integration::Decimal) -> Self {
373 FilterValue::Decimal(value)
374 }
375}
376
377#[cfg(all(feature = "ipnet", not(feature = "sqlite")))]
379impl From<sqlx_data_integration::IpNet> for FilterValue {
380 fn from(value: sqlx_data_integration::IpNet) -> Self {
381 FilterValue::IpNet(value)
382 }
383}
384
385#[cfg(all(feature = "ipnet", not(feature = "sqlite")))]
386impl From<sqlx_data_integration::Ipv4Net> for FilterValue {
387 fn from(value: sqlx_data_integration::Ipv4Net) -> Self {
388 FilterValue::Ipv4Net(value)
389 }
390}
391
392#[cfg(all(feature = "ipnet", not(feature = "sqlite")))]
393impl From<sqlx_data_integration::Ipv6Net> for FilterValue {
394 fn from(value: sqlx_data_integration::Ipv6Net) -> Self {
395 FilterValue::Ipv6Net(value)
396 }
397}
398
399#[cfg(all(feature = "ipnetwork", not(feature = "sqlite")))]
400impl From<sqlx_data_integration::IpNetwork> for FilterValue {
401 fn from(value: sqlx_data_integration::IpNetwork) -> Self {
402 FilterValue::IpNetwork(value)
403 }
404}
405
406#[cfg(feature = "mac_address")]
408impl From<sqlx_data_integration::MacAddress> for FilterValue {
409 fn from(value: sqlx_data_integration::MacAddress) -> Self {
410 FilterValue::MacAddress(value)
411 }
412}
413
414#[cfg(all(feature = "bit-vec", not(feature = "sqlite")))]
416impl From<sqlx_data_integration::BitVec> for FilterValue {
417 fn from(value: sqlx_data_integration::BitVec) -> Self {
418 FilterValue::BitVec(value)
419 }
420}
421
422#[cfg(feature = "bstr")]
423impl From<sqlx_data_integration::BString> for FilterValue {
424 fn from(value: sqlx_data_integration::BString) -> Self {
425 FilterValue::BStr(value)
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432
433 #[test]
434 fn test_filter_value_from_primitives() {
435 assert_eq!(
436 FilterValue::from("hello"),
437 FilterValue::String(Cow::Owned("hello".to_string()))
438 );
439 assert_eq!(FilterValue::from("hello".to_string()), "hello".into());
440 assert_eq!(FilterValue::from(42i64), FilterValue::Int(42));
441 assert_eq!(FilterValue::from(42u32), FilterValue::UInt(42));
442 assert_eq!(FilterValue::from(1.5f64), FilterValue::Float(1.5f64));
443 assert_eq!(FilterValue::from(true), FilterValue::Bool(true));
444 }
445
446 #[test]
447 fn test_filter_value_from_option() {
448 assert_eq!(FilterValue::from(Some("value")), "value".into());
449 assert_eq!(FilterValue::from(None::<String>), FilterValue::Null);
450 assert_eq!(FilterValue::from(Some(100i32)), FilterValue::Int(100));
451 assert_eq!(FilterValue::from(None::<i32>), FilterValue::Null);
452 }
453
454 #[test]
455 fn test_filter_value_from_vec() {
456 let vec_str: Vec<String> = vec!["admin".to_string(), "user".to_string()];
457 let expected = FilterValue::Array(vec!["admin".into(), "user".into()]);
458 assert_eq!(FilterValue::from(vec_str), expected);
459
460 let vec_int: Vec<i32> = vec![1, 2, 3];
461 let expected_int = FilterValue::Array(vec![
462 FilterValue::Int(1),
463 FilterValue::Int(2),
464 FilterValue::Int(3),
465 ]);
466 assert_eq!(FilterValue::from(vec_int), expected_int);
467 }
468
469 #[test]
470 fn test_filter_value_from_slice() {
471 let value: FilterValue = ["admin", "moderator"].into();
472 let expected = FilterValue::Array(vec!["admin".into(), "moderator".into()]);
473 assert_eq!(value, expected);
474
475 let slice: &[i64] = &[10, 20, 30];
476 let value: FilterValue = slice.into();
477 let expected = FilterValue::Array(vec![
478 FilterValue::Int(10),
479 FilterValue::Int(20),
480 FilterValue::Int(30),
481 ]);
482 assert_eq!(value, expected);
483 }
484
485 #[test]
486 fn test_filter_new_with_vec_and_slice() {
487 let filter1 = Filter::new(
488 "role",
489 FilterOperator::In,
490 vec!["admin".to_string(), "user".to_string()],
491 );
492 assert_eq!(filter1.field, "role");
493 assert_eq!(filter1.operator, FilterOperator::In);
494 assert_eq!(
495 filter1.value,
496 FilterValue::Array(vec!["admin".into(), "user".into(),])
497 );
498 assert!(!filter1.not);
499
500 let filter2 = Filter::new("status", FilterOperator::In, ["active", "pending"]);
501 assert_eq!(filter2.field, "status");
502 assert_eq!(filter2.operator, FilterOperator::In);
503 assert_eq!(
504 filter2.value,
505 FilterValue::Array(vec!["active".into(), "pending".into(),])
506 );
507 }
508
509 #[test]
510 fn test_filter_with_mixed_option_in_array() {
511 let values: Vec<Option<&str>> = vec![Some("yes"), None, Some("maybe")];
512 let filter = Filter::new("answer", FilterOperator::In, values);
513
514 assert_eq!(
515 filter.value,
516 FilterValue::Array(vec!["yes".into(), FilterValue::Null, "maybe".into(),])
517 );
518 }
519
520}