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) => Filter::In(column, values.into_iter().map(Into::into).collect()),
271 Self::NotIn(values) => {
272 Filter::NotIn(column, values.into_iter().map(Into::into).collect())
273 }
274 Self::Lt(v) => Filter::Lt(column, v.into()),
275 Self::Lte(v) => Filter::Lte(column, v.into()),
276 Self::Gt(v) => Filter::Gt(column, v.into()),
277 Self::Gte(v) => Filter::Gte(column, v.into()),
278 Self::Contains(v) => Filter::Contains(column, v.into()),
279 Self::StartsWith(v) => Filter::StartsWith(column, v.into()),
280 Self::EndsWith(v) => Filter::EndsWith(column, v.into()),
281 Self::IsNull => Filter::IsNull(column),
282 Self::IsNotNull => Filter::IsNotNull(column),
283 }
284 }
285}
286
287#[derive(Debug, Clone, PartialEq)]
308#[repr(C)] #[derive(Default)]
310pub enum Filter {
311 #[default]
313 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
524 .into_iter()
525 .map(|v| FilterValue::Int(v as i64))
526 .collect();
527 Self::In(field.into(), list)
528 }
529
530 #[inline]
532 pub fn in_strings(
533 field: impl Into<FieldName>,
534 values: impl IntoIterator<Item = String>,
535 ) -> Self {
536 let list: ValueList = values.into_iter().map(FilterValue::String).collect();
537 Self::In(field.into(), list)
538 }
539
540 #[inline]
544 pub fn in_values(field: impl Into<FieldName>, values: ValueList) -> Self {
545 Self::In(field.into(), values)
546 }
547
548 #[inline]
552 pub fn in_range(field: impl Into<FieldName>, range: std::ops::Range<i64>) -> Self {
553 let list: ValueList = range.map(FilterValue::Int).collect();
554 Self::In(field.into(), list)
555 }
556
557 #[inline(always)]
562 pub fn in_i64_slice(field: impl Into<FieldName>, values: &[i64]) -> Self {
563 let mut list = Vec::with_capacity(values.len());
564 for &v in values {
565 list.push(FilterValue::Int(v));
566 }
567 Self::In(field.into(), list)
568 }
569
570 #[inline(always)]
572 pub fn in_i32_slice(field: impl Into<FieldName>, values: &[i32]) -> Self {
573 let mut list = Vec::with_capacity(values.len());
574 for &v in values {
575 list.push(FilterValue::Int(v as i64));
576 }
577 Self::In(field.into(), list)
578 }
579
580 #[inline(always)]
582 pub fn in_str_slice(field: impl Into<FieldName>, values: &[&str]) -> Self {
583 let mut list = Vec::with_capacity(values.len());
584 for &v in values {
585 list.push(FilterValue::String(v.to_string()));
586 }
587 Self::In(field.into(), list)
588 }
589
590 #[inline]
592 #[allow(clippy::should_implement_trait)]
593 pub fn not(filter: Filter) -> Self {
594 if filter.is_none() {
595 return Self::None;
596 }
597 Self::Not(Box::new(filter))
598 }
599
600 #[inline]
614 pub fn in_slice<T: Into<FilterValue> + Clone>(
615 field: impl Into<FieldName>,
616 values: &[T],
617 ) -> Self {
618 let list: ValueList = values.iter().map(|v| v.clone().into()).collect();
619 Self::In(field.into(), list)
620 }
621
622 #[inline]
633 pub fn not_in_slice<T: Into<FilterValue> + Clone>(
634 field: impl Into<FieldName>,
635 values: &[T],
636 ) -> Self {
637 let list: ValueList = values.iter().map(|v| v.clone().into()).collect();
638 Self::NotIn(field.into(), list)
639 }
640
641 #[inline]
653 pub fn in_array<T: Into<FilterValue>, const N: usize>(
654 field: impl Into<FieldName>,
655 values: [T; N],
656 ) -> Self {
657 let list: ValueList = values.into_iter().map(Into::into).collect();
658 Self::In(field.into(), list)
659 }
660
661 #[inline]
663 pub fn not_in_array<T: Into<FilterValue>, const N: usize>(
664 field: impl Into<FieldName>,
665 values: [T; N],
666 ) -> Self {
667 let list: ValueList = values.into_iter().map(Into::into).collect();
668 Self::NotIn(field.into(), list)
669 }
670
671 pub fn and_then(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::And(filters) => {
681 let mut vec: Vec<_> = filters.into_vec();
683 vec.push(other);
684 Self::And(vec.into_boxed_slice())
685 }
686 _ => Self::And(Box::new([self, other])),
687 }
688 }
689
690 pub fn or_else(self, other: Filter) -> Self {
692 if self.is_none() {
693 return other;
694 }
695 if other.is_none() {
696 return self;
697 }
698 match self {
699 Self::Or(filters) => {
700 let mut vec: Vec<_> = filters.into_vec();
702 vec.push(other);
703 Self::Or(vec.into_boxed_slice())
704 }
705 _ => Self::Or(Box::new([self, other])),
706 }
707 }
708
709 pub fn to_sql(&self, param_offset: usize) -> (String, Vec<FilterValue>) {
712 let mut params = Vec::new();
713 let sql = self.to_sql_with_params(param_offset, &mut params);
714 (sql, params)
715 }
716
717 fn to_sql_with_params(&self, mut param_idx: usize, params: &mut Vec<FilterValue>) -> String {
718 match self {
719 Self::None => "TRUE".to_string(),
720
721 Self::Equals(col, val) => {
722 if val.is_null() {
723 format!("{} IS NULL", col)
724 } else {
725 params.push(val.clone());
726 param_idx += params.len();
727 format!("{} = ${}", col, param_idx)
728 }
729 }
730 Self::NotEquals(col, val) => {
731 if val.is_null() {
732 format!("{} IS NOT NULL", col)
733 } else {
734 params.push(val.clone());
735 param_idx += params.len();
736 format!("{} != ${}", col, param_idx)
737 }
738 }
739
740 Self::Lt(col, val) => {
741 params.push(val.clone());
742 param_idx += params.len();
743 format!("{} < ${}", col, param_idx)
744 }
745 Self::Lte(col, val) => {
746 params.push(val.clone());
747 param_idx += params.len();
748 format!("{} <= ${}", col, param_idx)
749 }
750 Self::Gt(col, val) => {
751 params.push(val.clone());
752 param_idx += params.len();
753 format!("{} > ${}", col, param_idx)
754 }
755 Self::Gte(col, val) => {
756 params.push(val.clone());
757 param_idx += params.len();
758 format!("{} >= ${}", col, param_idx)
759 }
760
761 Self::In(col, values) => {
762 if values.is_empty() {
763 return "FALSE".to_string();
764 }
765 let placeholders: Vec<_> = values
766 .iter()
767 .map(|v| {
768 params.push(v.clone());
769 param_idx += params.len();
770 format!("${}", param_idx)
771 })
772 .collect();
773 format!("{} IN ({})", col, placeholders.join(", "))
774 }
775 Self::NotIn(col, values) => {
776 if values.is_empty() {
777 return "TRUE".to_string();
778 }
779 let placeholders: Vec<_> = values
780 .iter()
781 .map(|v| {
782 params.push(v.clone());
783 param_idx += params.len();
784 format!("${}", param_idx)
785 })
786 .collect();
787 format!("{} NOT IN ({})", col, placeholders.join(", "))
788 }
789
790 Self::Contains(col, val) => {
791 if let FilterValue::String(s) = val {
792 params.push(FilterValue::String(format!("%{}%", s)));
793 } else {
794 params.push(val.clone());
795 }
796 param_idx += params.len();
797 format!("{} LIKE ${}", col, param_idx)
798 }
799 Self::StartsWith(col, val) => {
800 if let FilterValue::String(s) = val {
801 params.push(FilterValue::String(format!("{}%", s)));
802 } else {
803 params.push(val.clone());
804 }
805 param_idx += params.len();
806 format!("{} LIKE ${}", col, param_idx)
807 }
808 Self::EndsWith(col, val) => {
809 if let FilterValue::String(s) = val {
810 params.push(FilterValue::String(format!("%{}", s)));
811 } else {
812 params.push(val.clone());
813 }
814 param_idx += params.len();
815 format!("{} LIKE ${}", col, param_idx)
816 }
817
818 Self::IsNull(col) => format!("{} IS NULL", col),
819 Self::IsNotNull(col) => format!("{} IS NOT NULL", col),
820
821 Self::And(filters) => {
822 if filters.is_empty() {
823 return "TRUE".to_string();
824 }
825 let parts: Vec<_> = filters
826 .iter()
827 .map(|f| f.to_sql_with_params(param_idx + params.len(), params))
828 .collect();
829 format!("({})", parts.join(" AND "))
830 }
831 Self::Or(filters) => {
832 if filters.is_empty() {
833 return "FALSE".to_string();
834 }
835 let parts: Vec<_> = filters
836 .iter()
837 .map(|f| f.to_sql_with_params(param_idx + params.len(), params))
838 .collect();
839 format!("({})", parts.join(" OR "))
840 }
841 Self::Not(filter) => {
842 let inner = filter.to_sql_with_params(param_idx, params);
843 format!("NOT ({})", inner)
844 }
845 }
846 }
847
848 #[inline]
866 pub fn and_builder(capacity: usize) -> AndFilterBuilder {
867 AndFilterBuilder::with_capacity(capacity)
868 }
869
870 #[inline]
887 pub fn or_builder(capacity: usize) -> OrFilterBuilder {
888 OrFilterBuilder::with_capacity(capacity)
889 }
890
891 #[inline]
907 pub fn builder() -> FluentFilterBuilder {
908 FluentFilterBuilder::new()
909 }
910}
911
912#[derive(Debug, Clone)]
916pub struct AndFilterBuilder {
917 filters: Vec<Filter>,
918}
919
920impl AndFilterBuilder {
921 #[inline]
923 pub fn new() -> Self {
924 Self {
925 filters: Vec::new(),
926 }
927 }
928
929 #[inline]
931 pub fn with_capacity(capacity: usize) -> Self {
932 Self {
933 filters: Vec::with_capacity(capacity),
934 }
935 }
936
937 #[inline]
939 pub fn push(mut self, filter: Filter) -> Self {
940 if !filter.is_none() {
941 self.filters.push(filter);
942 }
943 self
944 }
945
946 #[inline]
948 pub fn extend(mut self, filters: impl IntoIterator<Item = Filter>) -> Self {
949 self.filters
950 .extend(filters.into_iter().filter(|f| !f.is_none()));
951 self
952 }
953
954 #[inline]
956 pub fn push_if(self, condition: bool, filter: Filter) -> Self {
957 if condition { self.push(filter) } else { self }
958 }
959
960 #[inline]
962 pub fn push_if_some<F>(self, opt: Option<F>) -> Self
963 where
964 F: Into<Filter>,
965 {
966 match opt {
967 Some(f) => self.push(f.into()),
968 None => self,
969 }
970 }
971
972 #[inline]
974 pub fn build(self) -> Filter {
975 match self.filters.len() {
976 0 => Filter::None,
977 1 => self.filters.into_iter().next().unwrap(),
978 _ => Filter::And(self.filters.into_boxed_slice()),
979 }
980 }
981
982 #[inline]
984 pub fn len(&self) -> usize {
985 self.filters.len()
986 }
987
988 #[inline]
990 pub fn is_empty(&self) -> bool {
991 self.filters.is_empty()
992 }
993}
994
995impl Default for AndFilterBuilder {
996 fn default() -> Self {
997 Self::new()
998 }
999}
1000
1001#[derive(Debug, Clone)]
1005pub struct OrFilterBuilder {
1006 filters: Vec<Filter>,
1007}
1008
1009impl OrFilterBuilder {
1010 #[inline]
1012 pub fn new() -> Self {
1013 Self {
1014 filters: Vec::new(),
1015 }
1016 }
1017
1018 #[inline]
1020 pub fn with_capacity(capacity: usize) -> Self {
1021 Self {
1022 filters: Vec::with_capacity(capacity),
1023 }
1024 }
1025
1026 #[inline]
1028 pub fn push(mut self, filter: Filter) -> Self {
1029 if !filter.is_none() {
1030 self.filters.push(filter);
1031 }
1032 self
1033 }
1034
1035 #[inline]
1037 pub fn extend(mut self, filters: impl IntoIterator<Item = Filter>) -> Self {
1038 self.filters
1039 .extend(filters.into_iter().filter(|f| !f.is_none()));
1040 self
1041 }
1042
1043 #[inline]
1045 pub fn push_if(self, condition: bool, filter: Filter) -> Self {
1046 if condition { self.push(filter) } else { self }
1047 }
1048
1049 #[inline]
1051 pub fn push_if_some<F>(self, opt: Option<F>) -> Self
1052 where
1053 F: Into<Filter>,
1054 {
1055 match opt {
1056 Some(f) => self.push(f.into()),
1057 None => self,
1058 }
1059 }
1060
1061 #[inline]
1063 pub fn build(self) -> Filter {
1064 match self.filters.len() {
1065 0 => Filter::None,
1066 1 => self.filters.into_iter().next().unwrap(),
1067 _ => Filter::Or(self.filters.into_boxed_slice()),
1068 }
1069 }
1070
1071 #[inline]
1073 pub fn len(&self) -> usize {
1074 self.filters.len()
1075 }
1076
1077 #[inline]
1079 pub fn is_empty(&self) -> bool {
1080 self.filters.is_empty()
1081 }
1082}
1083
1084impl Default for OrFilterBuilder {
1085 fn default() -> Self {
1086 Self::new()
1087 }
1088}
1089
1090#[derive(Debug, Clone)]
1115pub struct FluentFilterBuilder {
1116 filters: Vec<Filter>,
1117}
1118
1119impl FluentFilterBuilder {
1120 #[inline]
1122 pub fn new() -> Self {
1123 Self {
1124 filters: Vec::new(),
1125 }
1126 }
1127
1128 #[inline]
1130 pub fn with_capacity(mut self, capacity: usize) -> Self {
1131 self.filters.reserve(capacity);
1132 self
1133 }
1134
1135 #[inline]
1137 pub fn eq<F, V>(mut self, field: F, value: V) -> Self
1138 where
1139 F: Into<FieldName>,
1140 V: Into<FilterValue>,
1141 {
1142 self.filters
1143 .push(Filter::Equals(field.into(), value.into()));
1144 self
1145 }
1146
1147 #[inline]
1149 pub fn ne<F, V>(mut self, field: F, value: V) -> Self
1150 where
1151 F: Into<FieldName>,
1152 V: Into<FilterValue>,
1153 {
1154 self.filters
1155 .push(Filter::NotEquals(field.into(), value.into()));
1156 self
1157 }
1158
1159 #[inline]
1161 pub fn lt<F, V>(mut self, field: F, value: V) -> Self
1162 where
1163 F: Into<FieldName>,
1164 V: Into<FilterValue>,
1165 {
1166 self.filters.push(Filter::Lt(field.into(), value.into()));
1167 self
1168 }
1169
1170 #[inline]
1172 pub fn lte<F, V>(mut self, field: F, value: V) -> Self
1173 where
1174 F: Into<FieldName>,
1175 V: Into<FilterValue>,
1176 {
1177 self.filters.push(Filter::Lte(field.into(), value.into()));
1178 self
1179 }
1180
1181 #[inline]
1183 pub fn gt<F, V>(mut self, field: F, value: V) -> Self
1184 where
1185 F: Into<FieldName>,
1186 V: Into<FilterValue>,
1187 {
1188 self.filters.push(Filter::Gt(field.into(), value.into()));
1189 self
1190 }
1191
1192 #[inline]
1194 pub fn gte<F, V>(mut self, field: F, value: V) -> Self
1195 where
1196 F: Into<FieldName>,
1197 V: Into<FilterValue>,
1198 {
1199 self.filters.push(Filter::Gte(field.into(), value.into()));
1200 self
1201 }
1202
1203 #[inline]
1205 pub fn is_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::In(
1212 field.into(),
1213 values.into_iter().map(Into::into).collect(),
1214 ));
1215 self
1216 }
1217
1218 #[inline]
1220 pub fn not_in<F, I, V>(mut self, field: F, values: I) -> Self
1221 where
1222 F: Into<FieldName>,
1223 I: IntoIterator<Item = V>,
1224 V: Into<FilterValue>,
1225 {
1226 self.filters.push(Filter::NotIn(
1227 field.into(),
1228 values.into_iter().map(Into::into).collect(),
1229 ));
1230 self
1231 }
1232
1233 #[inline]
1235 pub fn contains<F, V>(mut self, field: F, value: V) -> Self
1236 where
1237 F: Into<FieldName>,
1238 V: Into<FilterValue>,
1239 {
1240 self.filters
1241 .push(Filter::Contains(field.into(), value.into()));
1242 self
1243 }
1244
1245 #[inline]
1247 pub fn starts_with<F, V>(mut self, field: F, value: V) -> Self
1248 where
1249 F: Into<FieldName>,
1250 V: Into<FilterValue>,
1251 {
1252 self.filters
1253 .push(Filter::StartsWith(field.into(), value.into()));
1254 self
1255 }
1256
1257 #[inline]
1259 pub fn ends_with<F, V>(mut self, field: F, value: V) -> Self
1260 where
1261 F: Into<FieldName>,
1262 V: Into<FilterValue>,
1263 {
1264 self.filters
1265 .push(Filter::EndsWith(field.into(), value.into()));
1266 self
1267 }
1268
1269 #[inline]
1271 pub fn is_null<F>(mut self, field: F) -> Self
1272 where
1273 F: Into<FieldName>,
1274 {
1275 self.filters.push(Filter::IsNull(field.into()));
1276 self
1277 }
1278
1279 #[inline]
1281 pub fn is_not_null<F>(mut self, field: F) -> Self
1282 where
1283 F: Into<FieldName>,
1284 {
1285 self.filters.push(Filter::IsNotNull(field.into()));
1286 self
1287 }
1288
1289 #[inline]
1291 pub fn filter(mut self, filter: Filter) -> Self {
1292 if !filter.is_none() {
1293 self.filters.push(filter);
1294 }
1295 self
1296 }
1297
1298 #[inline]
1300 pub fn filter_if(self, condition: bool, filter: Filter) -> Self {
1301 if condition { self.filter(filter) } else { self }
1302 }
1303
1304 #[inline]
1306 pub fn filter_if_some<F>(self, opt: Option<F>) -> Self
1307 where
1308 F: Into<Filter>,
1309 {
1310 match opt {
1311 Some(f) => self.filter(f.into()),
1312 None => self,
1313 }
1314 }
1315
1316 #[inline]
1318 pub fn build_and(self) -> Filter {
1319 let filters: Vec<_> = self.filters.into_iter().filter(|f| !f.is_none()).collect();
1320 match filters.len() {
1321 0 => Filter::None,
1322 1 => filters.into_iter().next().unwrap(),
1323 _ => Filter::And(filters.into_boxed_slice()),
1324 }
1325 }
1326
1327 #[inline]
1329 pub fn build_or(self) -> Filter {
1330 let filters: Vec<_> = self.filters.into_iter().filter(|f| !f.is_none()).collect();
1331 match filters.len() {
1332 0 => Filter::None,
1333 1 => filters.into_iter().next().unwrap(),
1334 _ => Filter::Or(filters.into_boxed_slice()),
1335 }
1336 }
1337
1338 #[inline]
1340 pub fn len(&self) -> usize {
1341 self.filters.len()
1342 }
1343
1344 #[inline]
1346 pub fn is_empty(&self) -> bool {
1347 self.filters.is_empty()
1348 }
1349}
1350
1351impl Default for FluentFilterBuilder {
1352 fn default() -> Self {
1353 Self::new()
1354 }
1355}
1356
1357#[cfg(test)]
1358mod tests {
1359 use super::*;
1360
1361 #[test]
1362 fn test_filter_value_from() {
1363 assert_eq!(FilterValue::from(42i32), FilterValue::Int(42));
1364 assert_eq!(
1365 FilterValue::from("hello"),
1366 FilterValue::String("hello".to_string())
1367 );
1368 assert_eq!(FilterValue::from(true), FilterValue::Bool(true));
1369 }
1370
1371 #[test]
1372 fn test_scalar_filter_equals() {
1373 let filter = ScalarFilter::Equals("test@example.com".to_string()).into_filter("email");
1374
1375 let (sql, params) = filter.to_sql(0);
1376 assert_eq!(sql, "email = $1");
1377 assert_eq!(params.len(), 1);
1378 }
1379
1380 #[test]
1381 fn test_filter_and() {
1382 let f1 = Filter::Equals("name".into(), "Alice".into());
1383 let f2 = Filter::Gt("age".into(), FilterValue::Int(18));
1384 let combined = Filter::and([f1, f2]);
1385
1386 let (sql, params) = combined.to_sql(0);
1387 assert!(sql.contains("AND"));
1388 assert_eq!(params.len(), 2);
1389 }
1390
1391 #[test]
1392 fn test_filter_or() {
1393 let f1 = Filter::Equals("status".into(), "active".into());
1394 let f2 = Filter::Equals("status".into(), "pending".into());
1395 let combined = Filter::or([f1, f2]);
1396
1397 let (sql, _) = combined.to_sql(0);
1398 assert!(sql.contains("OR"));
1399 }
1400
1401 #[test]
1402 fn test_filter_not() {
1403 let filter = Filter::not(Filter::Equals("deleted".into(), FilterValue::Bool(true)));
1404
1405 let (sql, _) = filter.to_sql(0);
1406 assert!(sql.contains("NOT"));
1407 }
1408
1409 #[test]
1410 fn test_filter_is_null() {
1411 let filter = Filter::IsNull("deleted_at".into());
1412 let (sql, params) = filter.to_sql(0);
1413 assert_eq!(sql, "deleted_at IS NULL");
1414 assert!(params.is_empty());
1415 }
1416
1417 #[test]
1418 fn test_filter_in() {
1419 let filter = Filter::In("status".into(), vec!["active".into(), "pending".into()]);
1420 let (sql, params) = filter.to_sql(0);
1421 assert!(sql.contains("IN"));
1422 assert_eq!(params.len(), 2);
1423 }
1424
1425 #[test]
1426 fn test_filter_contains() {
1427 let filter = Filter::Contains("email".into(), "example".into());
1428 let (sql, params) = filter.to_sql(0);
1429 assert!(sql.contains("LIKE"));
1430 assert_eq!(params.len(), 1);
1431 if let FilterValue::String(s) = ¶ms[0] {
1432 assert!(s.contains("%example%"));
1433 }
1434 }
1435
1436 #[test]
1439 fn test_filter_value_is_null() {
1440 assert!(FilterValue::Null.is_null());
1441 assert!(!FilterValue::Bool(false).is_null());
1442 assert!(!FilterValue::Int(0).is_null());
1443 assert!(!FilterValue::Float(0.0).is_null());
1444 assert!(!FilterValue::String("".to_string()).is_null());
1445 }
1446
1447 #[test]
1448 fn test_filter_value_to_sql_placeholder() {
1449 let val = FilterValue::Int(42);
1450 assert_eq!(val.to_sql_placeholder(1), "$1");
1451 assert_eq!(val.to_sql_placeholder(10), "$10");
1452 }
1453
1454 #[test]
1455 fn test_filter_value_from_i64() {
1456 assert_eq!(FilterValue::from(42i64), FilterValue::Int(42));
1457 assert_eq!(FilterValue::from(-100i64), FilterValue::Int(-100));
1458 }
1459
1460 #[test]
1461 fn test_filter_value_from_f64() {
1462 assert_eq!(FilterValue::from(3.14f64), FilterValue::Float(3.14));
1463 }
1464
1465 #[test]
1466 fn test_filter_value_from_string() {
1467 assert_eq!(
1468 FilterValue::from("hello".to_string()),
1469 FilterValue::String("hello".to_string())
1470 );
1471 }
1472
1473 #[test]
1474 fn test_filter_value_from_vec() {
1475 let values: Vec<i32> = vec![1, 2, 3];
1476 let filter_val: FilterValue = values.into();
1477 if let FilterValue::List(list) = filter_val {
1478 assert_eq!(list.len(), 3);
1479 assert_eq!(list[0], FilterValue::Int(1));
1480 assert_eq!(list[1], FilterValue::Int(2));
1481 assert_eq!(list[2], FilterValue::Int(3));
1482 } else {
1483 panic!("Expected List");
1484 }
1485 }
1486
1487 #[test]
1488 fn test_filter_value_from_option_some() {
1489 let val: FilterValue = Some(42i32).into();
1490 assert_eq!(val, FilterValue::Int(42));
1491 }
1492
1493 #[test]
1494 fn test_filter_value_from_option_none() {
1495 let val: FilterValue = Option::<i32>::None.into();
1496 assert_eq!(val, FilterValue::Null);
1497 }
1498
1499 #[test]
1502 fn test_scalar_filter_not() {
1503 let filter = ScalarFilter::Not(Box::new("test".to_string())).into_filter("name");
1504 let (sql, params) = filter.to_sql(0);
1505 assert_eq!(sql, "name != $1");
1506 assert_eq!(params.len(), 1);
1507 }
1508
1509 #[test]
1510 fn test_scalar_filter_in() {
1511 let filter = ScalarFilter::In(vec!["a".to_string(), "b".to_string()]).into_filter("status");
1512 let (sql, params) = filter.to_sql(0);
1513 assert!(sql.contains("IN"));
1514 assert_eq!(params.len(), 2);
1515 }
1516
1517 #[test]
1518 fn test_scalar_filter_not_in() {
1519 let filter = ScalarFilter::NotIn(vec!["x".to_string()]).into_filter("status");
1520 let (sql, params) = filter.to_sql(0);
1521 assert!(sql.contains("NOT IN"));
1522 assert_eq!(params.len(), 1);
1523 }
1524
1525 #[test]
1526 fn test_scalar_filter_lt() {
1527 let filter = ScalarFilter::Lt(100i32).into_filter("price");
1528 let (sql, params) = filter.to_sql(0);
1529 assert_eq!(sql, "price < $1");
1530 assert_eq!(params.len(), 1);
1531 }
1532
1533 #[test]
1534 fn test_scalar_filter_lte() {
1535 let filter = ScalarFilter::Lte(100i32).into_filter("price");
1536 let (sql, params) = filter.to_sql(0);
1537 assert_eq!(sql, "price <= $1");
1538 assert_eq!(params.len(), 1);
1539 }
1540
1541 #[test]
1542 fn test_scalar_filter_gt() {
1543 let filter = ScalarFilter::Gt(0i32).into_filter("quantity");
1544 let (sql, params) = filter.to_sql(0);
1545 assert_eq!(sql, "quantity > $1");
1546 assert_eq!(params.len(), 1);
1547 }
1548
1549 #[test]
1550 fn test_scalar_filter_gte() {
1551 let filter = ScalarFilter::Gte(0i32).into_filter("quantity");
1552 let (sql, params) = filter.to_sql(0);
1553 assert_eq!(sql, "quantity >= $1");
1554 assert_eq!(params.len(), 1);
1555 }
1556
1557 #[test]
1558 fn test_scalar_filter_starts_with() {
1559 let filter = ScalarFilter::StartsWith("prefix".to_string()).into_filter("name");
1560 let (sql, params) = filter.to_sql(0);
1561 assert!(sql.contains("LIKE"));
1562 assert_eq!(params.len(), 1);
1563 if let FilterValue::String(s) = ¶ms[0] {
1564 assert!(s.starts_with("prefix"));
1565 assert!(s.ends_with("%"));
1566 }
1567 }
1568
1569 #[test]
1570 fn test_scalar_filter_ends_with() {
1571 let filter = ScalarFilter::EndsWith("suffix".to_string()).into_filter("name");
1572 let (sql, params) = filter.to_sql(0);
1573 assert!(sql.contains("LIKE"));
1574 assert_eq!(params.len(), 1);
1575 if let FilterValue::String(s) = ¶ms[0] {
1576 assert!(s.starts_with("%"));
1577 assert!(s.ends_with("suffix"));
1578 }
1579 }
1580
1581 #[test]
1582 fn test_scalar_filter_is_null() {
1583 let filter = ScalarFilter::<String>::IsNull.into_filter("deleted_at");
1584 let (sql, params) = filter.to_sql(0);
1585 assert_eq!(sql, "deleted_at IS NULL");
1586 assert!(params.is_empty());
1587 }
1588
1589 #[test]
1590 fn test_scalar_filter_is_not_null() {
1591 let filter = ScalarFilter::<String>::IsNotNull.into_filter("name");
1592 let (sql, params) = filter.to_sql(0);
1593 assert_eq!(sql, "name IS NOT NULL");
1594 assert!(params.is_empty());
1595 }
1596
1597 #[test]
1600 fn test_filter_none() {
1601 let filter = Filter::none();
1602 assert!(filter.is_none());
1603 let (sql, params) = filter.to_sql(0);
1604 assert_eq!(sql, "TRUE"); assert!(params.is_empty());
1606 }
1607
1608 #[test]
1609 fn test_filter_not_equals() {
1610 let filter = Filter::NotEquals("status".into(), "deleted".into());
1611 let (sql, params) = filter.to_sql(0);
1612 assert_eq!(sql, "status != $1");
1613 assert_eq!(params.len(), 1);
1614 }
1615
1616 #[test]
1617 fn test_filter_lte() {
1618 let filter = Filter::Lte("price".into(), FilterValue::Int(100));
1619 let (sql, params) = filter.to_sql(0);
1620 assert_eq!(sql, "price <= $1");
1621 assert_eq!(params.len(), 1);
1622 }
1623
1624 #[test]
1625 fn test_filter_gte() {
1626 let filter = Filter::Gte("quantity".into(), FilterValue::Int(0));
1627 let (sql, params) = filter.to_sql(0);
1628 assert_eq!(sql, "quantity >= $1");
1629 assert_eq!(params.len(), 1);
1630 }
1631
1632 #[test]
1633 fn test_filter_not_in() {
1634 let filter = Filter::NotIn("status".into(), vec!["deleted".into(), "archived".into()]);
1635 let (sql, params) = filter.to_sql(0);
1636 assert!(sql.contains("NOT IN"));
1637 assert_eq!(params.len(), 2);
1638 }
1639
1640 #[test]
1641 fn test_filter_starts_with() {
1642 let filter = Filter::StartsWith("email".into(), "admin".into());
1643 let (sql, params) = filter.to_sql(0);
1644 assert!(sql.contains("LIKE"));
1645 assert_eq!(params.len(), 1);
1646 }
1647
1648 #[test]
1649 fn test_filter_ends_with() {
1650 let filter = Filter::EndsWith("email".into(), "@example.com".into());
1651 let (sql, params) = filter.to_sql(0);
1652 assert!(sql.contains("LIKE"));
1653 assert_eq!(params.len(), 1);
1654 }
1655
1656 #[test]
1657 fn test_filter_is_not_null() {
1658 let filter = Filter::IsNotNull("name".into());
1659 let (sql, params) = filter.to_sql(0);
1660 assert_eq!(sql, "name IS NOT NULL");
1661 assert!(params.is_empty());
1662 }
1663
1664 #[test]
1667 fn test_filter_and_empty() {
1668 let filter = Filter::and([]);
1669 assert!(filter.is_none());
1670 }
1671
1672 #[test]
1673 fn test_filter_and_single() {
1674 let f = Filter::Equals("name".into(), "Alice".into());
1675 let combined = Filter::and([f.clone()]);
1676 assert_eq!(combined, f);
1677 }
1678
1679 #[test]
1680 fn test_filter_and_with_none() {
1681 let f1 = Filter::Equals("name".into(), "Alice".into());
1682 let f2 = Filter::None;
1683 let combined = Filter::and([f1.clone(), f2]);
1684 assert_eq!(combined, f1);
1685 }
1686
1687 #[test]
1688 fn test_filter_or_empty() {
1689 let filter = Filter::or([]);
1690 assert!(filter.is_none());
1691 }
1692
1693 #[test]
1694 fn test_filter_or_single() {
1695 let f = Filter::Equals("status".into(), "active".into());
1696 let combined = Filter::or([f.clone()]);
1697 assert_eq!(combined, f);
1698 }
1699
1700 #[test]
1701 fn test_filter_or_with_none() {
1702 let f1 = Filter::Equals("status".into(), "active".into());
1703 let f2 = Filter::None;
1704 let combined = Filter::or([f1.clone(), f2]);
1705 assert_eq!(combined, f1);
1706 }
1707
1708 #[test]
1709 fn test_filter_not_none() {
1710 let filter = Filter::not(Filter::None);
1711 assert!(filter.is_none());
1712 }
1713
1714 #[test]
1715 fn test_filter_and_then() {
1716 let f1 = Filter::Equals("name".into(), "Alice".into());
1717 let f2 = Filter::Gt("age".into(), FilterValue::Int(18));
1718 let combined = f1.and_then(f2);
1719
1720 let (sql, params) = combined.to_sql(0);
1721 assert!(sql.contains("AND"));
1722 assert_eq!(params.len(), 2);
1723 }
1724
1725 #[test]
1726 fn test_filter_and_then_with_none_first() {
1727 let f1 = Filter::None;
1728 let f2 = Filter::Equals("name".into(), "Bob".into());
1729 let combined = f1.and_then(f2.clone());
1730 assert_eq!(combined, f2);
1731 }
1732
1733 #[test]
1734 fn test_filter_and_then_with_none_second() {
1735 let f1 = Filter::Equals("name".into(), "Alice".into());
1736 let f2 = Filter::None;
1737 let combined = f1.clone().and_then(f2);
1738 assert_eq!(combined, f1);
1739 }
1740
1741 #[test]
1742 fn test_filter_and_then_chained() {
1743 let f1 = Filter::Equals("a".into(), "1".into());
1744 let f2 = Filter::Equals("b".into(), "2".into());
1745 let f3 = Filter::Equals("c".into(), "3".into());
1746 let combined = f1.and_then(f2).and_then(f3);
1747
1748 let (sql, params) = combined.to_sql(0);
1749 assert!(sql.contains("AND"));
1750 assert_eq!(params.len(), 3);
1751 }
1752
1753 #[test]
1754 fn test_filter_or_else() {
1755 let f1 = Filter::Equals("status".into(), "active".into());
1756 let f2 = Filter::Equals("status".into(), "pending".into());
1757 let combined = f1.or_else(f2);
1758
1759 let (sql, _) = combined.to_sql(0);
1760 assert!(sql.contains("OR"));
1761 }
1762
1763 #[test]
1764 fn test_filter_or_else_with_none_first() {
1765 let f1 = Filter::None;
1766 let f2 = Filter::Equals("name".into(), "Bob".into());
1767 let combined = f1.or_else(f2.clone());
1768 assert_eq!(combined, f2);
1769 }
1770
1771 #[test]
1772 fn test_filter_or_else_with_none_second() {
1773 let f1 = Filter::Equals("name".into(), "Alice".into());
1774 let f2 = Filter::None;
1775 let combined = f1.clone().or_else(f2);
1776 assert_eq!(combined, f1);
1777 }
1778
1779 #[test]
1782 fn test_filter_nested_and_or() {
1783 let f1 = Filter::Equals("status".into(), "active".into());
1784 let f2 = Filter::and([
1785 Filter::Gt("age".into(), FilterValue::Int(18)),
1786 Filter::Lt("age".into(), FilterValue::Int(65)),
1787 ]);
1788 let combined = Filter::and([f1, f2]);
1789
1790 let (sql, params) = combined.to_sql(0);
1791 assert!(sql.contains("AND"));
1792 assert_eq!(params.len(), 3);
1793 }
1794
1795 #[test]
1796 fn test_filter_nested_not() {
1797 let inner = Filter::and([
1798 Filter::Equals("status".into(), "deleted".into()),
1799 Filter::Equals("archived".into(), FilterValue::Bool(true)),
1800 ]);
1801 let filter = Filter::not(inner);
1802
1803 let (sql, params) = filter.to_sql(0);
1804 assert!(sql.contains("NOT"));
1805 assert!(sql.contains("AND"));
1806 assert_eq!(params.len(), 2);
1807 }
1808
1809 #[test]
1810 fn test_filter_with_json_value() {
1811 let json_val = serde_json::json!({"key": "value"});
1812 let filter = Filter::Equals("metadata".into(), FilterValue::Json(json_val));
1813 let (sql, params) = filter.to_sql(0);
1814 assert_eq!(sql, "metadata = $1");
1815 assert_eq!(params.len(), 1);
1816 }
1817
1818 #[test]
1819 fn test_filter_in_empty_list() {
1820 let filter = Filter::In("status".into(), vec![]);
1821 let (sql, params) = filter.to_sql(0);
1822 assert!(
1824 sql.contains("FALSE")
1825 || sql.contains("1=0")
1826 || sql.is_empty()
1827 || sql.contains("status")
1828 );
1829 assert!(params.is_empty());
1830 }
1831
1832 #[test]
1833 fn test_filter_with_null_value() {
1834 let filter = Filter::IsNull("deleted_at".into());
1836 let (sql, params) = filter.to_sql(0);
1837 assert!(sql.contains("deleted_at"));
1838 assert!(sql.contains("IS NULL"));
1839 assert!(params.is_empty());
1840 }
1841
1842 #[test]
1845 fn test_and_builder_basic() {
1846 let filter = Filter::and_builder(3)
1847 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
1848 .push(Filter::Gt("score".into(), FilterValue::Int(100)))
1849 .push(Filter::IsNotNull("email".into()))
1850 .build();
1851
1852 let (sql, params) = filter.to_sql(0);
1853 assert!(sql.contains("AND"));
1854 assert_eq!(params.len(), 2); }
1856
1857 #[test]
1858 fn test_and_builder_empty() {
1859 let filter = Filter::and_builder(0).build();
1860 assert!(filter.is_none());
1861 }
1862
1863 #[test]
1864 fn test_and_builder_single() {
1865 let filter = Filter::and_builder(1)
1866 .push(Filter::Equals("id".into(), FilterValue::Int(42)))
1867 .build();
1868
1869 assert!(matches!(filter, Filter::Equals(_, _)));
1871 }
1872
1873 #[test]
1874 fn test_and_builder_filters_none() {
1875 let filter = Filter::and_builder(3)
1876 .push(Filter::None)
1877 .push(Filter::Equals("id".into(), FilterValue::Int(1)))
1878 .push(Filter::None)
1879 .build();
1880
1881 assert!(matches!(filter, Filter::Equals(_, _)));
1883 }
1884
1885 #[test]
1886 fn test_and_builder_push_if() {
1887 let include_deleted = false;
1888 let filter = Filter::and_builder(2)
1889 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
1890 .push_if(include_deleted, Filter::IsNull("deleted_at".into()))
1891 .build();
1892
1893 assert!(matches!(filter, Filter::Equals(_, _)));
1895 }
1896
1897 #[test]
1898 fn test_or_builder_basic() {
1899 let filter = Filter::or_builder(2)
1900 .push(Filter::Equals(
1901 "role".into(),
1902 FilterValue::String("admin".into()),
1903 ))
1904 .push(Filter::Equals(
1905 "role".into(),
1906 FilterValue::String("moderator".into()),
1907 ))
1908 .build();
1909
1910 let (sql, _) = filter.to_sql(0);
1911 assert!(sql.contains("OR"));
1912 }
1913
1914 #[test]
1915 fn test_or_builder_empty() {
1916 let filter = Filter::or_builder(0).build();
1917 assert!(filter.is_none());
1918 }
1919
1920 #[test]
1921 fn test_or_builder_single() {
1922 let filter = Filter::or_builder(1)
1923 .push(Filter::Equals("id".into(), FilterValue::Int(42)))
1924 .build();
1925
1926 assert!(matches!(filter, Filter::Equals(_, _)));
1928 }
1929
1930 #[test]
1931 fn test_fluent_builder_and() {
1932 let filter = Filter::builder()
1933 .eq("status", "active")
1934 .gt("age", 18)
1935 .is_not_null("email")
1936 .build_and();
1937
1938 let (sql, params) = filter.to_sql(0);
1939 assert!(sql.contains("AND"));
1940 assert_eq!(params.len(), 2);
1941 }
1942
1943 #[test]
1944 fn test_fluent_builder_or() {
1945 let filter = Filter::builder()
1946 .eq("role", "admin")
1947 .eq("role", "moderator")
1948 .build_or();
1949
1950 let (sql, _) = filter.to_sql(0);
1951 assert!(sql.contains("OR"));
1952 }
1953
1954 #[test]
1955 fn test_fluent_builder_with_capacity() {
1956 let filter = Filter::builder()
1957 .with_capacity(5)
1958 .eq("a", 1)
1959 .ne("b", 2)
1960 .lt("c", 3)
1961 .lte("d", 4)
1962 .gte("e", 5)
1963 .build_and();
1964
1965 let (sql, params) = filter.to_sql(0);
1966 assert!(sql.contains("AND"));
1967 assert_eq!(params.len(), 5);
1968 }
1969
1970 #[test]
1971 fn test_fluent_builder_string_operations() {
1972 let filter = Filter::builder()
1973 .contains("name", "john")
1974 .starts_with("email", "admin")
1975 .ends_with("domain", ".com")
1976 .build_and();
1977
1978 let (sql, _) = filter.to_sql(0);
1979 assert!(sql.contains("LIKE"));
1980 }
1981
1982 #[test]
1983 fn test_fluent_builder_null_operations() {
1984 let filter = Filter::builder()
1985 .is_null("deleted_at")
1986 .is_not_null("created_at")
1987 .build_and();
1988
1989 let (sql, _) = filter.to_sql(0);
1990 assert!(sql.contains("IS NULL"));
1991 assert!(sql.contains("IS NOT NULL"));
1992 }
1993
1994 #[test]
1995 fn test_fluent_builder_in_operations() {
1996 let filter = Filter::builder()
1997 .is_in("status", vec!["pending", "processing"])
1998 .not_in("role", vec!["banned", "suspended"])
1999 .build_and();
2000
2001 let (sql, _) = filter.to_sql(0);
2002 assert!(sql.contains("IN"));
2003 assert!(sql.contains("NOT IN"));
2004 }
2005
2006 #[test]
2007 fn test_fluent_builder_filter_if() {
2008 let include_archived = false;
2009 let filter = Filter::builder()
2010 .eq("active", true)
2011 .filter_if(
2012 include_archived,
2013 Filter::Equals("archived".into(), FilterValue::Bool(true)),
2014 )
2015 .build_and();
2016
2017 assert!(matches!(filter, Filter::Equals(_, _)));
2019 }
2020
2021 #[test]
2022 fn test_fluent_builder_filter_if_some() {
2023 let maybe_status: Option<Filter> = Some(Filter::Equals("status".into(), "active".into()));
2024 let filter = Filter::builder()
2025 .eq("id", 1)
2026 .filter_if_some(maybe_status)
2027 .build_and();
2028
2029 assert!(matches!(filter, Filter::And(_)));
2030 }
2031
2032 #[test]
2033 fn test_and_builder_extend() {
2034 let extra_filters = vec![
2035 Filter::Gt("score".into(), FilterValue::Int(100)),
2036 Filter::Lt("score".into(), FilterValue::Int(1000)),
2037 ];
2038
2039 let filter = Filter::and_builder(3)
2040 .push(Filter::Equals("active".into(), FilterValue::Bool(true)))
2041 .extend(extra_filters)
2042 .build();
2043
2044 let (sql, params) = filter.to_sql(0);
2045 assert!(sql.contains("AND"));
2046 assert_eq!(params.len(), 3);
2047 }
2048
2049 #[test]
2050 fn test_builder_len_and_is_empty() {
2051 let mut builder = AndFilterBuilder::new();
2052 assert!(builder.is_empty());
2053 assert_eq!(builder.len(), 0);
2054
2055 builder = builder.push(Filter::Equals("id".into(), FilterValue::Int(1)));
2056 assert!(!builder.is_empty());
2057 assert_eq!(builder.len(), 1);
2058 }
2059
2060 #[test]
2063 fn test_and2_both_valid() {
2064 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2065 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2066 let filter = Filter::and2(a, b);
2067
2068 assert!(matches!(filter, Filter::And(_)));
2069 let (sql, params) = filter.to_sql(0);
2070 assert!(sql.contains("AND"));
2071 assert_eq!(params.len(), 2);
2072 }
2073
2074 #[test]
2075 fn test_and2_first_none() {
2076 let a = Filter::None;
2077 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2078 let filter = Filter::and2(a, b.clone());
2079
2080 assert_eq!(filter, b);
2081 }
2082
2083 #[test]
2084 fn test_and2_second_none() {
2085 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2086 let b = Filter::None;
2087 let filter = Filter::and2(a.clone(), b);
2088
2089 assert_eq!(filter, a);
2090 }
2091
2092 #[test]
2093 fn test_and2_both_none() {
2094 let filter = Filter::and2(Filter::None, Filter::None);
2095 assert!(filter.is_none());
2096 }
2097
2098 #[test]
2099 fn test_or2_both_valid() {
2100 let a = Filter::Equals("role".into(), FilterValue::String("admin".into()));
2101 let b = Filter::Equals("role".into(), FilterValue::String("mod".into()));
2102 let filter = Filter::or2(a, b);
2103
2104 assert!(matches!(filter, Filter::Or(_)));
2105 let (sql, _) = filter.to_sql(0);
2106 assert!(sql.contains("OR"));
2107 }
2108
2109 #[test]
2110 fn test_or2_first_none() {
2111 let a = Filter::None;
2112 let b = Filter::Equals("active".into(), FilterValue::Bool(true));
2113 let filter = Filter::or2(a, b.clone());
2114
2115 assert_eq!(filter, b);
2116 }
2117
2118 #[test]
2119 fn test_or2_second_none() {
2120 let a = Filter::Equals("id".into(), FilterValue::Int(1));
2121 let b = Filter::None;
2122 let filter = Filter::or2(a.clone(), b);
2123
2124 assert_eq!(filter, a);
2125 }
2126
2127 #[test]
2128 fn test_or2_both_none() {
2129 let filter = Filter::or2(Filter::None, Filter::None);
2130 assert!(filter.is_none());
2131 }
2132}