1use serde::{Deserialize, Serialize};
65use smallvec::SmallVec;
66use std::borrow::Cow;
67use tracing::debug;
68
69pub type ValueList = Vec<FilterValue>;
84
85pub type SmallValueList = SmallVec<[FilterValue; 8]>;
88
89pub type LargeValueList = SmallVec<[FilterValue; 32]>;
92
93pub type FieldName = Cow<'static, str>;
114
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
148#[serde(untagged)]
149pub enum FilterValue {
150 Null,
152 Bool(bool),
154 Int(i64),
156 Float(f64),
158 String(String),
160 Json(serde_json::Value),
162 List(Vec<FilterValue>),
164}
165
166impl FilterValue {
167 pub fn is_null(&self) -> bool {
169 matches!(self, Self::Null)
170 }
171
172 pub fn to_sql_placeholder(&self, param_index: usize) -> String {
174 format!("${}", param_index)
175 }
176}
177
178impl From<bool> for FilterValue {
179 fn from(v: bool) -> Self {
180 Self::Bool(v)
181 }
182}
183
184impl From<i32> for FilterValue {
185 fn from(v: i32) -> Self {
186 Self::Int(v as i64)
187 }
188}
189
190impl From<i64> for FilterValue {
191 fn from(v: i64) -> Self {
192 Self::Int(v)
193 }
194}
195
196impl From<f64> for FilterValue {
197 fn from(v: f64) -> Self {
198 Self::Float(v)
199 }
200}
201
202impl From<String> for FilterValue {
203 fn from(v: String) -> Self {
204 Self::String(v)
205 }
206}
207
208impl From<&str> for FilterValue {
209 fn from(v: &str) -> Self {
210 Self::String(v.to_string())
211 }
212}
213
214impl<T: Into<FilterValue>> From<Vec<T>> for FilterValue {
215 fn from(v: Vec<T>) -> Self {
216 Self::List(v.into_iter().map(Into::into).collect())
217 }
218}
219
220impl<T: Into<FilterValue>> From<Option<T>> for FilterValue {
221 fn from(v: Option<T>) -> Self {
222 match v {
223 Some(v) => v.into(),
224 None => Self::Null,
225 }
226 }
227}
228
229#[derive(Debug, Clone, PartialEq)]
231pub enum ScalarFilter<T> {
232 Equals(T),
234 Not(Box<T>),
236 In(Vec<T>),
238 NotIn(Vec<T>),
240 Lt(T),
242 Lte(T),
244 Gt(T),
246 Gte(T),
248 Contains(T),
250 StartsWith(T),
252 EndsWith(T),
254 IsNull,
256 IsNotNull,
258}
259
260impl<T: Into<FilterValue>> ScalarFilter<T> {
261 pub fn into_filter(self, column: impl Into<FieldName>) -> Filter {
266 let column = column.into();
267 match self {
268 Self::Equals(v) => Filter::Equals(column, v.into()),
269 Self::Not(v) => Filter::NotEquals(column, (*v).into()),
270 Self::In(values) => {
271 Filter::In(column, values.into_iter().map(Into::into).collect())
272 }
273 Self::NotIn(values) => {
274 Filter::NotIn(column, values.into_iter().map(Into::into).collect())
275 }
276 Self::Lt(v) => Filter::Lt(column, v.into()),
277 Self::Lte(v) => Filter::Lte(column, v.into()),
278 Self::Gt(v) => Filter::Gt(column, v.into()),
279 Self::Gte(v) => Filter::Gte(column, v.into()),
280 Self::Contains(v) => Filter::Contains(column, v.into()),
281 Self::StartsWith(v) => Filter::StartsWith(column, v.into()),
282 Self::EndsWith(v) => Filter::EndsWith(column, v.into()),
283 Self::IsNull => Filter::IsNull(column),
284 Self::IsNotNull => Filter::IsNotNull(column),
285 }
286 }
287}
288
289#[derive(Debug, Clone, PartialEq)]
310#[repr(C)] pub enum Filter {
312 None,
314
315 Equals(FieldName, FilterValue),
317 NotEquals(FieldName, FilterValue),
319
320 Lt(FieldName, FilterValue),
322 Lte(FieldName, FilterValue),
324 Gt(FieldName, FilterValue),
326 Gte(FieldName, FilterValue),
328
329 In(FieldName, ValueList),
331 NotIn(FieldName, ValueList),
333
334 Contains(FieldName, FilterValue),
336 StartsWith(FieldName, FilterValue),
338 EndsWith(FieldName, FilterValue),
340
341 IsNull(FieldName),
343 IsNotNull(FieldName),
345
346 And(Box<[Filter]>),
351 Or(Box<[Filter]>),
356 Not(Box<Filter>),
358}
359
360impl Filter {
361 #[inline(always)]
363 pub fn none() -> Self {
364 Self::None
365 }
366
367 #[inline(always)]
369 pub fn is_none(&self) -> bool {
370 matches!(self, Self::None)
371 }
372
373 #[inline]
379 pub fn and(filters: impl IntoIterator<Item = Filter>) -> Self {
380 let filters: Vec<_> = filters.into_iter().filter(|f| !f.is_none()).collect();
381 let count = filters.len();
382 let result = match count {
383 0 => Self::None,
384 1 => filters.into_iter().next().unwrap(),
385 _ => Self::And(filters.into_boxed_slice()),
386 };
387 debug!(count, "Filter::and() created");
388 result
389 }
390
391 #[inline(always)]
395 pub fn and2(a: Filter, b: Filter) -> Self {
396 match (a.is_none(), b.is_none()) {
397 (true, true) => Self::None,
398 (true, false) => b,
399 (false, true) => a,
400 (false, false) => Self::And(Box::new([a, b])),
401 }
402 }
403
404 #[inline]
410 pub fn or(filters: impl IntoIterator<Item = Filter>) -> Self {
411 let filters: Vec<_> = filters.into_iter().filter(|f| !f.is_none()).collect();
412 let count = filters.len();
413 let result = match count {
414 0 => Self::None,
415 1 => filters.into_iter().next().unwrap(),
416 _ => Self::Or(filters.into_boxed_slice()),
417 };
418 debug!(count, "Filter::or() created");
419 result
420 }
421
422 #[inline(always)]
426 pub fn or2(a: Filter, b: Filter) -> Self {
427 match (a.is_none(), b.is_none()) {
428 (true, true) => Self::None,
429 (true, false) => b,
430 (false, true) => a,
431 (false, false) => Self::Or(Box::new([a, b])),
432 }
433 }
434
435 #[inline(always)]
456 pub fn and_n<const N: usize>(filters: [Filter; N]) -> Self {
457 Self::And(Box::new(filters))
459 }
460
461 #[inline(always)]
466 pub fn or_n<const N: usize>(filters: [Filter; N]) -> Self {
467 Self::Or(Box::new(filters))
468 }
469
470 #[inline(always)]
472 pub fn and3(a: Filter, b: Filter, c: Filter) -> Self {
473 Self::And(Box::new([a, b, c]))
474 }
475
476 #[inline(always)]
478 pub fn and4(a: Filter, b: Filter, c: Filter, d: Filter) -> Self {
479 Self::And(Box::new([a, b, c, d]))
480 }
481
482 #[inline(always)]
484 pub fn and5(a: Filter, b: Filter, c: Filter, d: Filter, e: Filter) -> Self {
485 Self::And(Box::new([a, b, c, d, e]))
486 }
487
488 #[inline(always)]
490 pub fn or3(a: Filter, b: Filter, c: Filter) -> Self {
491 Self::Or(Box::new([a, b, c]))
492 }
493
494 #[inline(always)]
496 pub fn or4(a: Filter, b: Filter, c: Filter, d: Filter) -> Self {
497 Self::Or(Box::new([a, b, c, d]))
498 }
499
500 #[inline(always)]
502 pub fn or5(a: Filter, b: Filter, c: Filter, d: Filter, e: Filter) -> Self {
503 Self::Or(Box::new([a, b, c, d, e]))
504 }
505
506 #[inline]
515 pub fn in_i64(field: impl Into<FieldName>, values: impl IntoIterator<Item = i64>) -> Self {
516 let list: ValueList = values.into_iter().map(FilterValue::Int).collect();
517 Self::In(field.into(), list)
518 }
519
520 #[inline]
522 pub fn in_i32(field: impl Into<FieldName>, values: impl IntoIterator<Item = i32>) -> Self {
523 let list: ValueList = values.into_iter().map(|v| FilterValue::Int(v as i64)).collect();
524 Self::In(field.into(), list)
525 }
526
527 #[inline]
529 pub fn in_strings(field: impl Into<FieldName>, values: impl IntoIterator<Item = String>) -> Self {
530 let list: ValueList = values.into_iter().map(FilterValue::String).collect();
531 Self::In(field.into(), list)
532 }
533
534 #[inline]
538 pub fn in_values(field: impl Into<FieldName>, values: ValueList) -> Self {
539 Self::In(field.into(), values)
540 }
541
542 #[inline]
546 pub fn in_range(field: impl Into<FieldName>, range: std::ops::Range<i64>) -> Self {
547 let list: ValueList = range.map(FilterValue::Int).collect();
548 Self::In(field.into(), list)
549 }
550
551 #[inline(always)]
556 pub fn in_i64_slice(field: impl Into<FieldName>, values: &[i64]) -> Self {
557 let mut list = Vec::with_capacity(values.len());
558 for &v in values {
559 list.push(FilterValue::Int(v));
560 }
561 Self::In(field.into(), list)
562 }
563
564 #[inline(always)]
566 pub fn in_i32_slice(field: impl Into<FieldName>, values: &[i32]) -> Self {
567 let mut list = Vec::with_capacity(values.len());
568 for &v in values {
569 list.push(FilterValue::Int(v as i64));
570 }
571 Self::In(field.into(), list)
572 }
573
574 #[inline(always)]
576 pub fn in_str_slice(field: impl Into<FieldName>, values: &[&str]) -> Self {
577 let mut list = Vec::with_capacity(values.len());
578 for &v in values {
579 list.push(FilterValue::String(v.to_string()));
580 }
581 Self::In(field.into(), list)
582 }
583
584 #[inline]
586 pub fn not(filter: Filter) -> Self {
587 if filter.is_none() {
588 return Self::None;
589 }
590 Self::Not(Box::new(filter))
591 }
592
593 #[inline]
607 pub fn in_slice<T: Into<FilterValue> + Clone>(field: impl Into<FieldName>, values: &[T]) -> Self {
608 let list: ValueList = values.iter().map(|v| v.clone().into()).collect();
609 Self::In(field.into(), list)
610 }
611
612 #[inline]
623 pub fn not_in_slice<T: Into<FilterValue> + Clone>(field: impl Into<FieldName>, values: &[T]) -> Self {
624 let list: ValueList = values.iter().map(|v| v.clone().into()).collect();
625 Self::NotIn(field.into(), list)
626 }
627
628 #[inline]
640 pub fn in_array<T: Into<FilterValue>, const N: usize>(field: impl Into<FieldName>, values: [T; N]) -> Self {
641 let list: ValueList = values.into_iter().map(Into::into).collect();
642 Self::In(field.into(), list)
643 }
644
645 #[inline]
647 pub fn not_in_array<T: Into<FilterValue>, const N: usize>(field: impl Into<FieldName>, values: [T; N]) -> Self {
648 let list: ValueList = values.into_iter().map(Into::into).collect();
649 Self::NotIn(field.into(), list)
650 }
651
652 pub fn and_then(self, other: Filter) -> Self {
654 if self.is_none() {
655 return other;
656 }
657 if other.is_none() {
658 return self;
659 }
660 match self {
661 Self::And(filters) => {
662 let mut vec: Vec<_> = filters.into_vec();
664 vec.push(other);
665 Self::And(vec.into_boxed_slice())
666 }
667 _ => Self::And(Box::new([self, other])),
668 }
669 }
670
671 pub fn or_else(self, other: Filter) -> Self {
673 if self.is_none() {
674 return other;
675 }
676 if other.is_none() {
677 return self;
678 }
679 match self {
680 Self::Or(filters) => {
681 let mut vec: Vec<_> = filters.into_vec();
683 vec.push(other);
684 Self::Or(vec.into_boxed_slice())
685 }
686 _ => Self::Or(Box::new([self, other])),
687 }
688 }
689
690 pub fn to_sql(&self, param_offset: usize) -> (String, Vec<FilterValue>) {
693 let mut params = Vec::new();
694 let sql = self.to_sql_with_params(param_offset, &mut params);
695 (sql, params)
696 }
697
698 fn to_sql_with_params(&self, mut param_idx: usize, params: &mut Vec<FilterValue>) -> String {
699 match self {
700 Self::None => "TRUE".to_string(),
701
702 Self::Equals(col, val) => {
703 if val.is_null() {
704 format!("{} IS NULL", col)
705 } else {
706 params.push(val.clone());
707 param_idx += params.len();
708 format!("{} = ${}", col, param_idx)
709 }
710 }
711 Self::NotEquals(col, val) => {
712 if val.is_null() {
713 format!("{} IS NOT NULL", col)
714 } else {
715 params.push(val.clone());
716 param_idx += params.len();
717 format!("{} != ${}", col, param_idx)
718 }
719 }
720
721 Self::Lt(col, val) => {
722 params.push(val.clone());
723 param_idx += params.len();
724 format!("{} < ${}", col, param_idx)
725 }
726 Self::Lte(col, val) => {
727 params.push(val.clone());
728 param_idx += params.len();
729 format!("{} <= ${}", col, param_idx)
730 }
731 Self::Gt(col, val) => {
732 params.push(val.clone());
733 param_idx += params.len();
734 format!("{} > ${}", col, param_idx)
735 }
736 Self::Gte(col, val) => {
737 params.push(val.clone());
738 param_idx += params.len();
739 format!("{} >= ${}", col, param_idx)
740 }
741
742 Self::In(col, values) => {
743 if values.is_empty() {
744 return "FALSE".to_string();
745 }
746 let placeholders: Vec<_> = values
747 .iter()
748 .map(|v| {
749 params.push(v.clone());
750 param_idx += params.len();
751 format!("${}", param_idx)
752 })
753 .collect();
754 format!("{} IN ({})", col, placeholders.join(", "))
755 }
756 Self::NotIn(col, values) => {
757 if values.is_empty() {
758 return "TRUE".to_string();
759 }
760 let placeholders: Vec<_> = values
761 .iter()
762 .map(|v| {
763 params.push(v.clone());
764 param_idx += params.len();
765 format!("${}", param_idx)
766 })
767 .collect();
768 format!("{} NOT IN ({})", col, placeholders.join(", "))
769 }
770
771 Self::Contains(col, val) => {
772 if let FilterValue::String(s) = val {
773 params.push(FilterValue::String(format!("%{}%", s)));
774 } else {
775 params.push(val.clone());
776 }
777 param_idx += params.len();
778 format!("{} LIKE ${}", col, param_idx)
779 }
780 Self::StartsWith(col, val) => {
781 if let FilterValue::String(s) = val {
782 params.push(FilterValue::String(format!("{}%", s)));
783 } else {
784 params.push(val.clone());
785 }
786 param_idx += params.len();
787 format!("{} LIKE ${}", col, param_idx)
788 }
789 Self::EndsWith(col, val) => {
790 if let FilterValue::String(s) = val {
791 params.push(FilterValue::String(format!("%{}", s)));
792 } else {
793 params.push(val.clone());
794 }
795 param_idx += params.len();
796 format!("{} LIKE ${}", col, param_idx)
797 }
798
799 Self::IsNull(col) => format!("{} IS NULL", col),
800 Self::IsNotNull(col) => format!("{} IS NOT NULL", col),
801
802 Self::And(filters) => {
803 if filters.is_empty() {
804 return "TRUE".to_string();
805 }
806 let parts: Vec<_> = filters
807 .iter()
808 .map(|f| f.to_sql_with_params(param_idx + params.len(), params))
809 .collect();
810 format!("({})", parts.join(" AND "))
811 }
812 Self::Or(filters) => {
813 if filters.is_empty() {
814 return "FALSE".to_string();
815 }
816 let parts: Vec<_> = filters
817 .iter()
818 .map(|f| f.to_sql_with_params(param_idx + params.len(), params))
819 .collect();
820 format!("({})", parts.join(" OR "))
821 }
822 Self::Not(filter) => {
823 let inner = filter.to_sql_with_params(param_idx, params);
824 format!("NOT ({})", inner)
825 }
826 }
827 }
828
829 #[inline]
847 pub fn and_builder(capacity: usize) -> AndFilterBuilder {
848 AndFilterBuilder::with_capacity(capacity)
849 }
850
851 #[inline]
868 pub fn or_builder(capacity: usize) -> OrFilterBuilder {
869 OrFilterBuilder::with_capacity(capacity)
870 }
871
872 #[inline]
888 pub fn builder() -> FluentFilterBuilder {
889 FluentFilterBuilder::new()
890 }
891}
892
893#[derive(Debug, Clone)]
897pub struct AndFilterBuilder {
898 filters: Vec<Filter>,
899}
900
901impl AndFilterBuilder {
902 #[inline]
904 pub fn new() -> Self {
905 Self {
906 filters: Vec::new(),
907 }
908 }
909
910 #[inline]
912 pub fn with_capacity(capacity: usize) -> Self {
913 Self {
914 filters: Vec::with_capacity(capacity),
915 }
916 }
917
918 #[inline]
920 pub fn push(mut self, filter: Filter) -> Self {
921 if !filter.is_none() {
922 self.filters.push(filter);
923 }
924 self
925 }
926
927 #[inline]
929 pub fn extend(mut self, filters: impl IntoIterator<Item = Filter>) -> Self {
930 self.filters.extend(filters.into_iter().filter(|f| !f.is_none()));
931 self
932 }
933
934 #[inline]
936 pub fn push_if(self, condition: bool, filter: Filter) -> Self {
937 if condition {
938 self.push(filter)
939 } else {
940 self
941 }
942 }
943
944 #[inline]
946 pub fn push_if_some<F>(self, opt: Option<F>) -> Self
947 where
948 F: Into<Filter>,
949 {
950 match opt {
951 Some(f) => self.push(f.into()),
952 None => self,
953 }
954 }
955
956 #[inline]
958 pub fn build(self) -> Filter {
959 match self.filters.len() {
960 0 => Filter::None,
961 1 => self.filters.into_iter().next().unwrap(),
962 _ => Filter::And(self.filters.into_boxed_slice()),
963 }
964 }
965
966 #[inline]
968 pub fn len(&self) -> usize {
969 self.filters.len()
970 }
971
972 #[inline]
974 pub fn is_empty(&self) -> bool {
975 self.filters.is_empty()
976 }
977}
978
979impl Default for AndFilterBuilder {
980 fn default() -> Self {
981 Self::new()
982 }
983}
984
985#[derive(Debug, Clone)]
989pub struct OrFilterBuilder {
990 filters: Vec<Filter>,
991}
992
993impl OrFilterBuilder {
994 #[inline]
996 pub fn new() -> Self {
997 Self {
998 filters: Vec::new(),
999 }
1000 }
1001
1002 #[inline]
1004 pub fn with_capacity(capacity: usize) -> Self {
1005 Self {
1006 filters: Vec::with_capacity(capacity),
1007 }
1008 }
1009
1010 #[inline]
1012 pub fn push(mut self, filter: Filter) -> Self {
1013 if !filter.is_none() {
1014 self.filters.push(filter);
1015 }
1016 self
1017 }
1018
1019 #[inline]
1021 pub fn extend(mut self, filters: impl IntoIterator<Item = Filter>) -> Self {
1022 self.filters.extend(filters.into_iter().filter(|f| !f.is_none()));
1023 self
1024 }
1025
1026 #[inline]
1028 pub fn push_if(self, condition: bool, filter: Filter) -> Self {
1029 if condition {
1030 self.push(filter)
1031 } else {
1032 self
1033 }
1034 }
1035
1036 #[inline]
1038 pub fn push_if_some<F>(self, opt: Option<F>) -> Self
1039 where
1040 F: Into<Filter>,
1041 {
1042 match opt {
1043 Some(f) => self.push(f.into()),
1044 None => self,
1045 }
1046 }
1047
1048 #[inline]
1050 pub fn build(self) -> Filter {
1051 match self.filters.len() {
1052 0 => Filter::None,
1053 1 => self.filters.into_iter().next().unwrap(),
1054 _ => Filter::Or(self.filters.into_boxed_slice()),
1055 }
1056 }
1057
1058 #[inline]
1060 pub fn len(&self) -> usize {
1061 self.filters.len()
1062 }
1063
1064 #[inline]
1066 pub fn is_empty(&self) -> bool {
1067 self.filters.is_empty()
1068 }
1069}
1070
1071impl Default for OrFilterBuilder {
1072 fn default() -> Self {
1073 Self::new()
1074 }
1075}
1076
1077#[derive(Debug, Clone)]
1102pub struct FluentFilterBuilder {
1103 filters: Vec<Filter>,
1104}
1105
1106impl FluentFilterBuilder {
1107 #[inline]
1109 pub fn new() -> Self {
1110 Self {
1111 filters: Vec::new(),
1112 }
1113 }
1114
1115 #[inline]
1117 pub fn with_capacity(mut self, capacity: usize) -> Self {
1118 self.filters.reserve(capacity);
1119 self
1120 }
1121
1122 #[inline]
1124 pub fn eq<F, V>(mut self, field: F, value: V) -> Self
1125 where
1126 F: Into<FieldName>,
1127 V: Into<FilterValue>,
1128 {
1129 self.filters.push(Filter::Equals(field.into(), value.into()));
1130 self
1131 }
1132
1133 #[inline]
1135 pub fn ne<F, V>(mut self, field: F, value: V) -> Self
1136 where
1137 F: Into<FieldName>,
1138 V: Into<FilterValue>,
1139 {
1140 self.filters.push(Filter::NotEquals(field.into(), value.into()));
1141 self
1142 }
1143
1144 #[inline]
1146 pub fn lt<F, V>(mut self, field: F, value: V) -> Self
1147 where
1148 F: Into<FieldName>,
1149 V: Into<FilterValue>,
1150 {
1151 self.filters.push(Filter::Lt(field.into(), value.into()));
1152 self
1153 }
1154
1155 #[inline]
1157 pub fn lte<F, V>(mut self, field: F, value: V) -> Self
1158 where
1159 F: Into<FieldName>,
1160 V: Into<FilterValue>,
1161 {
1162 self.filters.push(Filter::Lte(field.into(), value.into()));
1163 self
1164 }
1165
1166 #[inline]
1168 pub fn gt<F, V>(mut self, field: F, value: V) -> Self
1169 where
1170 F: Into<FieldName>,
1171 V: Into<FilterValue>,
1172 {
1173 self.filters.push(Filter::Gt(field.into(), value.into()));
1174 self
1175 }
1176
1177 #[inline]
1179 pub fn gte<F, V>(mut self, field: F, value: V) -> Self
1180 where
1181 F: Into<FieldName>,
1182 V: Into<FilterValue>,
1183 {
1184 self.filters.push(Filter::Gte(field.into(), value.into()));
1185 self
1186 }
1187
1188 #[inline]
1190 pub fn is_in<F, I, V>(mut self, field: F, values: I) -> Self
1191 where
1192 F: Into<FieldName>,
1193 I: IntoIterator<Item = V>,
1194 V: Into<FilterValue>,
1195 {
1196 self.filters.push(Filter::In(
1197 field.into(),
1198 values.into_iter().map(Into::into).collect(),
1199 ));
1200 self
1201 }
1202
1203 #[inline]
1205 pub fn not_in<F, I, V>(mut self, field: F, values: I) -> Self
1206 where
1207 F: Into<FieldName>,
1208 I: IntoIterator<Item = V>,
1209 V: Into<FilterValue>,
1210 {
1211 self.filters.push(Filter::NotIn(
1212 field.into(),
1213 values.into_iter().map(Into::into).collect(),
1214 ));
1215 self
1216 }
1217
1218 #[inline]
1220 pub fn contains<F, V>(mut self, field: F, value: V) -> Self
1221 where
1222 F: Into<FieldName>,
1223 V: Into<FilterValue>,
1224 {
1225 self.filters.push(Filter::Contains(field.into(), value.into()));
1226 self
1227 }
1228
1229 #[inline]
1231 pub fn starts_with<F, V>(mut self, field: F, value: V) -> Self
1232 where
1233 F: Into<FieldName>,
1234 V: Into<FilterValue>,
1235 {
1236 self.filters.push(Filter::StartsWith(field.into(), value.into()));
1237 self
1238 }
1239
1240 #[inline]
1242 pub fn ends_with<F, V>(mut self, field: F, value: V) -> Self
1243 where
1244 F: Into<FieldName>,
1245 V: Into<FilterValue>,
1246 {
1247 self.filters.push(Filter::EndsWith(field.into(), value.into()));
1248 self
1249 }
1250
1251 #[inline]
1253 pub fn is_null<F>(mut self, field: F) -> Self
1254 where
1255 F: Into<FieldName>,
1256 {
1257 self.filters.push(Filter::IsNull(field.into()));
1258 self
1259 }
1260
1261 #[inline]
1263 pub fn is_not_null<F>(mut self, field: F) -> Self
1264 where
1265 F: Into<FieldName>,
1266 {
1267 self.filters.push(Filter::IsNotNull(field.into()));
1268 self
1269 }
1270
1271 #[inline]
1273 pub fn filter(mut self, filter: Filter) -> Self {
1274 if !filter.is_none() {
1275 self.filters.push(filter);
1276 }
1277 self
1278 }
1279
1280 #[inline]
1282 pub fn filter_if(self, condition: bool, filter: Filter) -> Self {
1283 if condition {
1284 self.filter(filter)
1285 } else {
1286 self
1287 }
1288 }
1289
1290 #[inline]
1292 pub fn filter_if_some<F>(self, opt: Option<F>) -> Self
1293 where
1294 F: Into<Filter>,
1295 {
1296 match opt {
1297 Some(f) => self.filter(f.into()),
1298 None => self,
1299 }
1300 }
1301
1302 #[inline]
1304 pub fn build_and(self) -> Filter {
1305 let filters: Vec<_> = self.filters.into_iter().filter(|f| !f.is_none()).collect();
1306 match filters.len() {
1307 0 => Filter::None,
1308 1 => filters.into_iter().next().unwrap(),
1309 _ => Filter::And(filters.into_boxed_slice()),
1310 }
1311 }
1312
1313 #[inline]
1315 pub fn build_or(self) -> Filter {
1316 let filters: Vec<_> = self.filters.into_iter().filter(|f| !f.is_none()).collect();
1317 match filters.len() {
1318 0 => Filter::None,
1319 1 => filters.into_iter().next().unwrap(),
1320 _ => Filter::Or(filters.into_boxed_slice()),
1321 }
1322 }
1323
1324 #[inline]
1326 pub fn len(&self) -> usize {
1327 self.filters.len()
1328 }
1329
1330 #[inline]
1332 pub fn is_empty(&self) -> bool {
1333 self.filters.is_empty()
1334 }
1335}
1336
1337impl Default for FluentFilterBuilder {
1338 fn default() -> Self {
1339 Self::new()
1340 }
1341}
1342
1343impl Default for Filter {
1344 fn default() -> Self {
1345 Self::None
1346 }
1347}
1348
1349#[cfg(test)]
1350mod tests {
1351 use super::*;
1352
1353 #[test]
1354 fn test_filter_value_from() {
1355 assert_eq!(FilterValue::from(42i32), FilterValue::Int(42));
1356 assert_eq!(FilterValue::from("hello"), FilterValue::String("hello".to_string()));
1357 assert_eq!(FilterValue::from(true), FilterValue::Bool(true));
1358 }
1359
1360 #[test]
1361 fn test_scalar_filter_equals() {
1362 let filter = ScalarFilter::Equals("test@example.com".to_string())
1363 .into_filter("email");
1364
1365 let (sql, params) = filter.to_sql(0);
1366 assert_eq!(sql, "email = $1");
1367 assert_eq!(params.len(), 1);
1368 }
1369
1370 #[test]
1371 fn test_filter_and() {
1372 let f1 = Filter::Equals("name".into(), "Alice".into());
1373 let f2 = Filter::Gt("age".into(), FilterValue::Int(18));
1374 let combined = Filter::and([f1, f2]);
1375
1376 let (sql, params) = combined.to_sql(0);
1377 assert!(sql.contains("AND"));
1378 assert_eq!(params.len(), 2);
1379 }
1380
1381 #[test]
1382 fn test_filter_or() {
1383 let f1 = Filter::Equals("status".into(), "active".into());
1384 let f2 = Filter::Equals("status".into(), "pending".into());
1385 let combined = Filter::or([f1, f2]);
1386
1387 let (sql, _) = combined.to_sql(0);
1388 assert!(sql.contains("OR"));
1389 }
1390
1391 #[test]
1392 fn test_filter_not() {
1393 let filter = Filter::not(Filter::Equals("deleted".into(), FilterValue::Bool(true)));
1394
1395 let (sql, _) = filter.to_sql(0);
1396 assert!(sql.contains("NOT"));
1397 }
1398
1399 #[test]
1400 fn test_filter_is_null() {
1401 let filter = Filter::IsNull("deleted_at".into());
1402 let (sql, params) = filter.to_sql(0);
1403 assert_eq!(sql, "deleted_at IS NULL");
1404 assert!(params.is_empty());
1405 }
1406
1407 #[test]
1408 fn test_filter_in() {
1409 let filter = Filter::In(
1410 "status".into(),
1411 vec!["active".into(), "pending".into()],
1412 );
1413 let (sql, params) = filter.to_sql(0);
1414 assert!(sql.contains("IN"));
1415 assert_eq!(params.len(), 2);
1416 }
1417
1418 #[test]
1419 fn test_filter_contains() {
1420 let filter = Filter::Contains("email".into(), "example".into());
1421 let (sql, params) = filter.to_sql(0);
1422 assert!(sql.contains("LIKE"));
1423 assert_eq!(params.len(), 1);
1424 if let FilterValue::String(s) = ¶ms[0] {
1425 assert!(s.contains("%example%"));
1426 }
1427 }
1428
1429 #[test]
1432 fn test_filter_value_is_null() {
1433 assert!(FilterValue::Null.is_null());
1434 assert!(!FilterValue::Bool(false).is_null());
1435 assert!(!FilterValue::Int(0).is_null());
1436 assert!(!FilterValue::Float(0.0).is_null());
1437 assert!(!FilterValue::String("".to_string()).is_null());
1438 }
1439
1440 #[test]
1441 fn test_filter_value_to_sql_placeholder() {
1442 let val = FilterValue::Int(42);
1443 assert_eq!(val.to_sql_placeholder(1), "$1");
1444 assert_eq!(val.to_sql_placeholder(10), "$10");
1445 }
1446
1447 #[test]
1448 fn test_filter_value_from_i64() {
1449 assert_eq!(FilterValue::from(42i64), FilterValue::Int(42));
1450 assert_eq!(FilterValue::from(-100i64), FilterValue::Int(-100));
1451 }
1452
1453 #[test]
1454 fn test_filter_value_from_f64() {
1455 assert_eq!(FilterValue::from(3.14f64), FilterValue::Float(3.14));
1456 }
1457
1458 #[test]
1459 fn test_filter_value_from_string() {
1460 assert_eq!(
1461 FilterValue::from("hello".to_string()),
1462 FilterValue::String("hello".to_string())
1463 );
1464 }
1465
1466 #[test]
1467 fn test_filter_value_from_vec() {
1468 let values: Vec<i32> = vec![1, 2, 3];
1469 let filter_val: FilterValue = values.into();
1470 if let FilterValue::List(list) = filter_val {
1471 assert_eq!(list.len(), 3);
1472 assert_eq!(list[0], FilterValue::Int(1));
1473 assert_eq!(list[1], FilterValue::Int(2));
1474 assert_eq!(list[2], FilterValue::Int(3));
1475 } else {
1476 panic!("Expected List");
1477 }
1478 }
1479
1480 #[test]
1481 fn test_filter_value_from_option_some() {
1482 let val: FilterValue = Some(42i32).into();
1483 assert_eq!(val, FilterValue::Int(42));
1484 }
1485
1486 #[test]
1487 fn test_filter_value_from_option_none() {
1488 let val: FilterValue = Option::<i32>::None.into();
1489 assert_eq!(val, FilterValue::Null);
1490 }
1491
1492 #[test]
1495 fn test_scalar_filter_not() {
1496 let filter = ScalarFilter::Not(Box::new("test".to_string())).into_filter("name");
1497 let (sql, params) = filter.to_sql(0);
1498 assert_eq!(sql, "name != $1");
1499 assert_eq!(params.len(), 1);
1500 }
1501
1502 #[test]
1503 fn test_scalar_filter_in() {
1504 let filter = ScalarFilter::In(vec!["a".to_string(), "b".to_string()]).into_filter("status");
1505 let (sql, params) = filter.to_sql(0);
1506 assert!(sql.contains("IN"));
1507 assert_eq!(params.len(), 2);
1508 }
1509
1510 #[test]
1511 fn test_scalar_filter_not_in() {
1512 let filter = ScalarFilter::NotIn(vec!["x".to_string()]).into_filter("status");
1513 let (sql, params) = filter.to_sql(0);
1514 assert!(sql.contains("NOT IN"));
1515 assert_eq!(params.len(), 1);
1516 }
1517
1518 #[test]
1519 fn test_scalar_filter_lt() {
1520 let filter = ScalarFilter::Lt(100i32).into_filter("price");
1521 let (sql, params) = filter.to_sql(0);
1522 assert_eq!(sql, "price < $1");
1523 assert_eq!(params.len(), 1);
1524 }
1525
1526 #[test]
1527 fn test_scalar_filter_lte() {
1528 let filter = ScalarFilter::Lte(100i32).into_filter("price");
1529 let (sql, params) = filter.to_sql(0);
1530 assert_eq!(sql, "price <= $1");
1531 assert_eq!(params.len(), 1);
1532 }
1533
1534 #[test]
1535 fn test_scalar_filter_gt() {
1536 let filter = ScalarFilter::Gt(0i32).into_filter("quantity");
1537 let (sql, params) = filter.to_sql(0);
1538 assert_eq!(sql, "quantity > $1");
1539 assert_eq!(params.len(), 1);
1540 }
1541
1542 #[test]
1543 fn test_scalar_filter_gte() {
1544 let filter = ScalarFilter::Gte(0i32).into_filter("quantity");
1545 let (sql, params) = filter.to_sql(0);
1546 assert_eq!(sql, "quantity >= $1");
1547 assert_eq!(params.len(), 1);
1548 }
1549
1550 #[test]
1551 fn test_scalar_filter_starts_with() {
1552 let filter = ScalarFilter::StartsWith("prefix".to_string()).into_filter("name");
1553 let (sql, params) = filter.to_sql(0);
1554 assert!(sql.contains("LIKE"));
1555 assert_eq!(params.len(), 1);
1556 if let FilterValue::String(s) = ¶ms[0] {
1557 assert!(s.starts_with("prefix"));
1558 assert!(s.ends_with("%"));
1559 }
1560 }
1561
1562 #[test]
1563 fn test_scalar_filter_ends_with() {
1564 let filter = ScalarFilter::EndsWith("suffix".to_string()).into_filter("name");
1565 let (sql, params) = filter.to_sql(0);
1566 assert!(sql.contains("LIKE"));
1567 assert_eq!(params.len(), 1);
1568 if let FilterValue::String(s) = ¶ms[0] {
1569 assert!(s.starts_with("%"));
1570 assert!(s.ends_with("suffix"));
1571 }
1572 }
1573
1574 #[test]
1575 fn test_scalar_filter_is_null() {
1576 let filter = ScalarFilter::<String>::IsNull.into_filter("deleted_at");
1577 let (sql, params) = filter.to_sql(0);
1578 assert_eq!(sql, "deleted_at IS NULL");
1579 assert!(params.is_empty());
1580 }
1581
1582 #[test]
1583 fn test_scalar_filter_is_not_null() {
1584 let filter = ScalarFilter::<String>::IsNotNull.into_filter("name");
1585 let (sql, params) = filter.to_sql(0);
1586 assert_eq!(sql, "name IS NOT NULL");
1587 assert!(params.is_empty());
1588 }
1589
1590 #[test]
1593 fn test_filter_none() {
1594 let filter = Filter::none();
1595 assert!(filter.is_none());
1596 let (sql, params) = filter.to_sql(0);
1597 assert_eq!(sql, "TRUE"); assert!(params.is_empty());
1599 }
1600
1601 #[test]
1602 fn test_filter_not_equals() {
1603 let filter = Filter::NotEquals("status".into(), "deleted".into());
1604 let (sql, params) = filter.to_sql(0);
1605 assert_eq!(sql, "status != $1");
1606 assert_eq!(params.len(), 1);
1607 }
1608
1609 #[test]
1610 fn test_filter_lte() {
1611 let filter = Filter::Lte("price".into(), FilterValue::Int(100));
1612 let (sql, params) = filter.to_sql(0);
1613 assert_eq!(sql, "price <= $1");
1614 assert_eq!(params.len(), 1);
1615 }
1616
1617 #[test]
1618 fn test_filter_gte() {
1619 let filter = Filter::Gte("quantity".into(), FilterValue::Int(0));
1620 let (sql, params) = filter.to_sql(0);
1621 assert_eq!(sql, "quantity >= $1");
1622 assert_eq!(params.len(), 1);
1623 }
1624
1625 #[test]
1626 fn test_filter_not_in() {
1627 let filter = Filter::NotIn(
1628 "status".into(),
1629 vec!["deleted".into(), "archived".into()],
1630 );
1631 let (sql, params) = filter.to_sql(0);
1632 assert!(sql.contains("NOT IN"));
1633 assert_eq!(params.len(), 2);
1634 }
1635
1636 #[test]
1637 fn test_filter_starts_with() {
1638 let filter = Filter::StartsWith("email".into(), "admin".into());
1639 let (sql, params) = filter.to_sql(0);
1640 assert!(sql.contains("LIKE"));
1641 assert_eq!(params.len(), 1);
1642 }
1643
1644 #[test]
1645 fn test_filter_ends_with() {
1646 let filter = Filter::EndsWith("email".into(), "@example.com".into());
1647 let (sql, params) = filter.to_sql(0);
1648 assert!(sql.contains("LIKE"));
1649 assert_eq!(params.len(), 1);
1650 }
1651
1652 #[test]
1653 fn test_filter_is_not_null() {
1654 let filter = Filter::IsNotNull("name".into());
1655 let (sql, params) = filter.to_sql(0);
1656 assert_eq!(sql, "name IS NOT NULL");
1657 assert!(params.is_empty());
1658 }
1659
1660 #[test]
1663 fn test_filter_and_empty() {
1664 let filter = Filter::and([]);
1665 assert!(filter.is_none());
1666 }
1667
1668 #[test]
1669 fn test_filter_and_single() {
1670 let f = Filter::Equals("name".into(), "Alice".into());
1671 let combined = Filter::and([f.clone()]);
1672 assert_eq!(combined, f);
1673 }
1674
1675 #[test]
1676 fn test_filter_and_with_none() {
1677 let f1 = Filter::Equals("name".into(), "Alice".into());
1678 let f2 = Filter::None;
1679 let combined = Filter::and([f1.clone(), f2]);
1680 assert_eq!(combined, f1);
1681 }
1682
1683 #[test]
1684 fn test_filter_or_empty() {
1685 let filter = Filter::or([]);
1686 assert!(filter.is_none());
1687 }
1688
1689 #[test]
1690 fn test_filter_or_single() {
1691 let f = Filter::Equals("status".into(), "active".into());
1692 let combined = Filter::or([f.clone()]);
1693 assert_eq!(combined, f);
1694 }
1695
1696 #[test]
1697 fn test_filter_or_with_none() {
1698 let f1 = Filter::Equals("status".into(), "active".into());
1699 let f2 = Filter::None;
1700 let combined = Filter::or([f1.clone(), f2]);
1701 assert_eq!(combined, f1);
1702 }
1703
1704 #[test]
1705 fn test_filter_not_none() {
1706 let filter = Filter::not(Filter::None);
1707 assert!(filter.is_none());
1708 }
1709
1710 #[test]
1711 fn test_filter_and_then() {
1712 let f1 = Filter::Equals("name".into(), "Alice".into());
1713 let f2 = Filter::Gt("age".into(), FilterValue::Int(18));
1714 let combined = f1.and_then(f2);
1715
1716 let (sql, params) = combined.to_sql(0);
1717 assert!(sql.contains("AND"));
1718 assert_eq!(params.len(), 2);
1719 }
1720
1721 #[test]
1722 fn test_filter_and_then_with_none_first() {
1723 let f1 = Filter::None;
1724 let f2 = Filter::Equals("name".into(), "Bob".into());
1725 let combined = f1.and_then(f2.clone());
1726 assert_eq!(combined, f2);
1727 }
1728
1729 #[test]
1730 fn test_filter_and_then_with_none_second() {
1731 let f1 = Filter::Equals("name".into(), "Alice".into());
1732 let f2 = Filter::None;
1733 let combined = f1.clone().and_then(f2);
1734 assert_eq!(combined, f1);
1735 }
1736
1737 #[test]
1738 fn test_filter_and_then_chained() {
1739 let f1 = Filter::Equals("a".into(), "1".into());
1740 let f2 = Filter::Equals("b".into(), "2".into());
1741 let f3 = Filter::Equals("c".into(), "3".into());
1742 let combined = f1.and_then(f2).and_then(f3);
1743
1744 let (sql, params) = combined.to_sql(0);
1745 assert!(sql.contains("AND"));
1746 assert_eq!(params.len(), 3);
1747 }
1748
1749 #[test]
1750 fn test_filter_or_else() {
1751 let f1 = Filter::Equals("status".into(), "active".into());
1752 let f2 = Filter::Equals("status".into(), "pending".into());
1753 let combined = f1.or_else(f2);
1754
1755 let (sql, _) = combined.to_sql(0);
1756 assert!(sql.contains("OR"));
1757 }
1758
1759 #[test]
1760 fn test_filter_or_else_with_none_first() {
1761 let f1 = Filter::None;
1762 let f2 = Filter::Equals("name".into(), "Bob".into());
1763 let combined = f1.or_else(f2.clone());
1764 assert_eq!(combined, f2);
1765 }
1766
1767 #[test]
1768 fn test_filter_or_else_with_none_second() {
1769 let f1 = Filter::Equals("name".into(), "Alice".into());
1770 let f2 = Filter::None;
1771 let combined = f1.clone().or_else(f2);
1772 assert_eq!(combined, f1);
1773 }
1774
1775 #[test]
1778 fn test_filter_nested_and_or() {
1779 let f1 = Filter::Equals("status".into(), "active".into());
1780 let f2 = Filter::and([
1781 Filter::Gt("age".into(), FilterValue::Int(18)),
1782 Filter::Lt("age".into(), FilterValue::Int(65)),
1783 ]);
1784 let combined = Filter::and([f1, f2]);
1785
1786 let (sql, params) = combined.to_sql(0);
1787 assert!(sql.contains("AND"));
1788 assert_eq!(params.len(), 3);
1789 }
1790
1791 #[test]
1792 fn test_filter_nested_not() {
1793 let inner = Filter::and([
1794 Filter::Equals("status".into(), "deleted".into()),
1795 Filter::Equals("archived".into(), FilterValue::Bool(true)),
1796 ]);
1797 let filter = Filter::not(inner);
1798
1799 let (sql, params) = filter.to_sql(0);
1800 assert!(sql.contains("NOT"));
1801 assert!(sql.contains("AND"));
1802 assert_eq!(params.len(), 2);
1803 }
1804
1805 #[test]
1806 fn test_filter_with_json_value() {
1807 let json_val = serde_json::json!({"key": "value"});
1808 let filter = Filter::Equals("metadata".into(), FilterValue::Json(json_val));
1809 let (sql, params) = filter.to_sql(0);
1810 assert_eq!(sql, "metadata = $1");
1811 assert_eq!(params.len(), 1);
1812 }
1813
1814 #[test]
1815 fn test_filter_in_empty_list() {
1816 let filter = Filter::In("status".into(), vec![]);
1817 let (sql, params) = filter.to_sql(0);
1818 assert!(sql.contains("FALSE") || sql.contains("1=0") || sql.is_empty() || sql.contains("status"));
1820 assert!(params.is_empty());
1821 }
1822
1823 #[test]
1824 fn test_filter_with_null_value() {
1825 let filter = Filter::IsNull("deleted_at".into());
1827 let (sql, params) = filter.to_sql(0);
1828 assert!(sql.contains("deleted_at"));
1829 assert!(sql.contains("IS NULL"));
1830 assert!(params.is_empty());
1831 }
1832
1833 #[test]
1836 fn test_and_builder_basic() {
1837 let filter = Filter::and_builder(3)
1838 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
1839 .push(Filter::Gt("score".into(), FilterValue::Int(100)))
1840 .push(Filter::IsNotNull("email".into()))
1841 .build();
1842
1843 let (sql, params) = filter.to_sql(0);
1844 assert!(sql.contains("AND"));
1845 assert_eq!(params.len(), 2); }
1847
1848 #[test]
1849 fn test_and_builder_empty() {
1850 let filter = Filter::and_builder(0).build();
1851 assert!(filter.is_none());
1852 }
1853
1854 #[test]
1855 fn test_and_builder_single() {
1856 let filter = Filter::and_builder(1)
1857 .push(Filter::Equals("id".into(), FilterValue::Int(42)))
1858 .build();
1859
1860 assert!(matches!(filter, Filter::Equals(_, _)));
1862 }
1863
1864 #[test]
1865 fn test_and_builder_filters_none() {
1866 let filter = Filter::and_builder(3)
1867 .push(Filter::None)
1868 .push(Filter::Equals("id".into(), FilterValue::Int(1)))
1869 .push(Filter::None)
1870 .build();
1871
1872 assert!(matches!(filter, Filter::Equals(_, _)));
1874 }
1875
1876 #[test]
1877 fn test_and_builder_push_if() {
1878 let include_deleted = false;
1879 let filter = Filter::and_builder(2)
1880 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
1881 .push_if(include_deleted, Filter::IsNull("deleted_at".into()))
1882 .build();
1883
1884 assert!(matches!(filter, Filter::Equals(_, _)));
1886 }
1887
1888 #[test]
1889 fn test_or_builder_basic() {
1890 let filter = Filter::or_builder(2)
1891 .push(Filter::Equals("role".into(), FilterValue::String("admin".into())))
1892 .push(Filter::Equals("role".into(), FilterValue::String("moderator".into())))
1893 .build();
1894
1895 let (sql, _) = filter.to_sql(0);
1896 assert!(sql.contains("OR"));
1897 }
1898
1899 #[test]
1900 fn test_or_builder_empty() {
1901 let filter = Filter::or_builder(0).build();
1902 assert!(filter.is_none());
1903 }
1904
1905 #[test]
1906 fn test_or_builder_single() {
1907 let filter = Filter::or_builder(1)
1908 .push(Filter::Equals("id".into(), FilterValue::Int(42)))
1909 .build();
1910
1911 assert!(matches!(filter, Filter::Equals(_, _)));
1913 }
1914
1915 #[test]
1916 fn test_fluent_builder_and() {
1917 let filter = Filter::builder()
1918 .eq("status", "active")
1919 .gt("age", 18)
1920 .is_not_null("email")
1921 .build_and();
1922
1923 let (sql, params) = filter.to_sql(0);
1924 assert!(sql.contains("AND"));
1925 assert_eq!(params.len(), 2);
1926 }
1927
1928 #[test]
1929 fn test_fluent_builder_or() {
1930 let filter = Filter::builder()
1931 .eq("role", "admin")
1932 .eq("role", "moderator")
1933 .build_or();
1934
1935 let (sql, _) = filter.to_sql(0);
1936 assert!(sql.contains("OR"));
1937 }
1938
1939 #[test]
1940 fn test_fluent_builder_with_capacity() {
1941 let filter = Filter::builder()
1942 .with_capacity(5)
1943 .eq("a", 1)
1944 .ne("b", 2)
1945 .lt("c", 3)
1946 .lte("d", 4)
1947 .gte("e", 5)
1948 .build_and();
1949
1950 let (sql, params) = filter.to_sql(0);
1951 assert!(sql.contains("AND"));
1952 assert_eq!(params.len(), 5);
1953 }
1954
1955 #[test]
1956 fn test_fluent_builder_string_operations() {
1957 let filter = Filter::builder()
1958 .contains("name", "john")
1959 .starts_with("email", "admin")
1960 .ends_with("domain", ".com")
1961 .build_and();
1962
1963 let (sql, _) = filter.to_sql(0);
1964 assert!(sql.contains("LIKE"));
1965 }
1966
1967 #[test]
1968 fn test_fluent_builder_null_operations() {
1969 let filter = Filter::builder()
1970 .is_null("deleted_at")
1971 .is_not_null("created_at")
1972 .build_and();
1973
1974 let (sql, _) = filter.to_sql(0);
1975 assert!(sql.contains("IS NULL"));
1976 assert!(sql.contains("IS NOT NULL"));
1977 }
1978
1979 #[test]
1980 fn test_fluent_builder_in_operations() {
1981 let filter = Filter::builder()
1982 .is_in("status", vec!["pending", "processing"])
1983 .not_in("role", vec!["banned", "suspended"])
1984 .build_and();
1985
1986 let (sql, _) = filter.to_sql(0);
1987 assert!(sql.contains("IN"));
1988 assert!(sql.contains("NOT IN"));
1989 }
1990
1991 #[test]
1992 fn test_fluent_builder_filter_if() {
1993 let include_archived = false;
1994 let filter = Filter::builder()
1995 .eq("active", true)
1996 .filter_if(include_archived, Filter::Equals("archived".into(), FilterValue::Bool(true)))
1997 .build_and();
1998
1999 assert!(matches!(filter, Filter::Equals(_, _)));
2001 }
2002
2003 #[test]
2004 fn test_fluent_builder_filter_if_some() {
2005 let maybe_status: Option<Filter> = Some(Filter::Equals("status".into(), "active".into()));
2006 let filter = Filter::builder()
2007 .eq("id", 1)
2008 .filter_if_some(maybe_status)
2009 .build_and();
2010
2011 assert!(matches!(filter, Filter::And(_)));
2012 }
2013
2014 #[test]
2015 fn test_and_builder_extend() {
2016 let extra_filters = vec![
2017 Filter::Gt("score".into(), FilterValue::Int(100)),
2018 Filter::Lt("score".into(), FilterValue::Int(1000)),
2019 ];
2020
2021 let filter = Filter::and_builder(3)
2022 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
2023 .extend(extra_filters)
2024 .build();
2025
2026 let (sql, params) = filter.to_sql(0);
2027 assert!(sql.contains("AND"));
2028 assert_eq!(params.len(), 3);
2029 }
2030
2031 #[test]
2032 fn test_builder_len_and_is_empty() {
2033 let mut builder = AndFilterBuilder::new();
2034 assert!(builder.is_empty());
2035 assert_eq!(builder.len(), 0);
2036
2037 builder = builder.push(Filter::Equals("id".into(), FilterValue::Int(1)));
2038 assert!(!builder.is_empty());
2039 assert_eq!(builder.len(), 1);
2040 }
2041
2042 #[test]
2045 fn test_and2_both_valid() {
2046 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2047 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2048 let filter = Filter::and2(a, b);
2049
2050 assert!(matches!(filter, Filter::And(_)));
2051 let (sql, params) = filter.to_sql(0);
2052 assert!(sql.contains("AND"));
2053 assert_eq!(params.len(), 2);
2054 }
2055
2056 #[test]
2057 fn test_and2_first_none() {
2058 let a = Filter::None;
2059 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2060 let filter = Filter::and2(a, b.clone());
2061
2062 assert_eq!(filter, b);
2063 }
2064
2065 #[test]
2066 fn test_and2_second_none() {
2067 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2068 let b = Filter::None;
2069 let filter = Filter::and2(a.clone(), b);
2070
2071 assert_eq!(filter, a);
2072 }
2073
2074 #[test]
2075 fn test_and2_both_none() {
2076 let filter = Filter::and2(Filter::None, Filter::None);
2077 assert!(filter.is_none());
2078 }
2079
2080 #[test]
2081 fn test_or2_both_valid() {
2082 let a = Filter::Equals("role".into(), FilterValue::String("admin".into()));
2083 let b = Filter::Equals("role".into(), FilterValue::String("mod".into()));
2084 let filter = Filter::or2(a, b);
2085
2086 assert!(matches!(filter, Filter::Or(_)));
2087 let (sql, _) = filter.to_sql(0);
2088 assert!(sql.contains("OR"));
2089 }
2090
2091 #[test]
2092 fn test_or2_first_none() {
2093 let a = Filter::None;
2094 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2095 let filter = Filter::or2(a, b.clone());
2096
2097 assert_eq!(filter, b);
2098 }
2099
2100 #[test]
2101 fn test_or2_second_none() {
2102 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2103 let b = Filter::None;
2104 let filter = Filter::or2(a.clone(), b);
2105
2106 assert_eq!(filter, a);
2107 }
2108
2109 #[test]
2110 fn test_or2_both_none() {
2111 let filter = Filter::or2(Filter::None, Filter::None);
2112 assert!(filter.is_none());
2113 }
2114}
2115