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