icydb_core/db/query/builder/
field.rs1use crate::{db::query::expr::FilterExpr, traits::FieldValue};
7use derive_more::Deref;
8
9#[derive(Clone, Copy, Deref, Eq, Hash, PartialEq)]
18pub struct FieldRef(&'static str);
19
20impl FieldRef {
21 #[must_use]
23 pub const fn new(name: &'static str) -> Self {
24 Self(name)
25 }
26
27 #[must_use]
29 pub const fn as_str(self) -> &'static str {
30 self.0
31 }
32
33 fn cmp_field(
35 self,
36 other: impl AsRef<str>,
37 build: impl FnOnce(String, String) -> FilterExpr,
38 ) -> FilterExpr {
39 build(self.0.to_string(), other.as_ref().to_string())
40 }
41
42 #[must_use]
48 pub fn eq(self, value: impl FieldValue) -> FilterExpr {
49 FilterExpr::eq(self.0, value)
50 }
51
52 #[must_use]
54 pub fn text_eq_ci(self, value: impl FieldValue) -> FilterExpr {
55 FilterExpr::eq_ci(self.0, value)
56 }
57
58 #[must_use]
60 pub fn ne(self, value: impl FieldValue) -> FilterExpr {
61 FilterExpr::ne(self.0, value)
62 }
63
64 #[must_use]
66 pub fn lt(self, value: impl FieldValue) -> FilterExpr {
67 FilterExpr::lt(self.0, value)
68 }
69
70 #[must_use]
72 pub fn lte(self, value: impl FieldValue) -> FilterExpr {
73 FilterExpr::lte(self.0, value)
74 }
75
76 #[must_use]
78 pub fn gt(self, value: impl FieldValue) -> FilterExpr {
79 FilterExpr::gt(self.0, value)
80 }
81
82 #[must_use]
84 pub fn gte(self, value: impl FieldValue) -> FilterExpr {
85 FilterExpr::gte(self.0, value)
86 }
87
88 #[must_use]
90 pub fn eq_field(self, other: impl AsRef<str>) -> FilterExpr {
91 self.cmp_field(other, FilterExpr::eq_field)
92 }
93
94 #[must_use]
96 pub fn ne_field(self, other: impl AsRef<str>) -> FilterExpr {
97 self.cmp_field(other, FilterExpr::ne_field)
98 }
99
100 #[must_use]
102 pub fn lt_field(self, other: impl AsRef<str>) -> FilterExpr {
103 self.cmp_field(other, FilterExpr::lt_field)
104 }
105
106 #[must_use]
108 pub fn lte_field(self, other: impl AsRef<str>) -> FilterExpr {
109 self.cmp_field(other, FilterExpr::lte_field)
110 }
111
112 #[must_use]
114 pub fn gt_field(self, other: impl AsRef<str>) -> FilterExpr {
115 self.cmp_field(other, FilterExpr::gt_field)
116 }
117
118 #[must_use]
120 pub fn gte_field(self, other: impl AsRef<str>) -> FilterExpr {
121 self.cmp_field(other, FilterExpr::gte_field)
122 }
123
124 #[must_use]
126 pub fn in_list<I, V>(self, values: I) -> FilterExpr
127 where
128 I: IntoIterator<Item = V>,
129 V: FieldValue,
130 {
131 FilterExpr::in_list(self.0, values)
132 }
133
134 #[must_use]
140 pub fn is_null(self) -> FilterExpr {
141 FilterExpr::is_null(self.0)
142 }
143
144 #[must_use]
146 pub fn is_not_null(self) -> FilterExpr {
147 FilterExpr::is_not_null(self.0)
148 }
149
150 #[must_use]
152 pub fn is_missing(self) -> FilterExpr {
153 FilterExpr::is_missing(self.0)
154 }
155
156 #[must_use]
158 pub fn is_empty(self) -> FilterExpr {
159 FilterExpr::is_empty(self.0)
160 }
161
162 #[must_use]
164 pub fn is_not_empty(self) -> FilterExpr {
165 FilterExpr::is_not_empty(self.0)
166 }
167
168 #[must_use]
170 pub fn text_contains(self, value: impl FieldValue) -> FilterExpr {
171 FilterExpr::text_contains(self.0, value)
172 }
173
174 #[must_use]
176 pub fn text_contains_ci(self, value: impl FieldValue) -> FilterExpr {
177 FilterExpr::text_contains_ci(self.0, value)
178 }
179
180 #[must_use]
182 pub fn text_starts_with(self, value: impl FieldValue) -> FilterExpr {
183 FilterExpr::starts_with(self.0, value)
184 }
185
186 #[must_use]
188 pub fn text_starts_with_ci(self, value: impl FieldValue) -> FilterExpr {
189 FilterExpr::starts_with_ci(self.0, value)
190 }
191
192 #[must_use]
194 pub fn between(self, lower: impl FieldValue, upper: impl FieldValue) -> FilterExpr {
195 FilterExpr::and(vec![self.gte(lower), self.lte(upper)])
196 }
197
198 #[must_use]
200 pub fn between_fields(self, lower: impl AsRef<str>, upper: impl AsRef<str>) -> FilterExpr {
201 FilterExpr::and(vec![self.gte_field(lower), self.lte_field(upper)])
202 }
203
204 #[must_use]
206 pub fn not_between(self, lower: impl FieldValue, upper: impl FieldValue) -> FilterExpr {
207 FilterExpr::or(vec![self.lt(lower), self.gt(upper)])
208 }
209
210 #[must_use]
212 pub fn not_between_fields(self, lower: impl AsRef<str>, upper: impl AsRef<str>) -> FilterExpr {
213 FilterExpr::or(vec![self.lt_field(lower), self.gt_field(upper)])
214 }
215}
216
217impl AsRef<str> for FieldRef {
222 fn as_ref(&self) -> &str {
223 self.0
224 }
225}
226
227#[cfg(test)]
232mod tests {
233 use super::{FieldRef, FilterExpr};
234
235 #[test]
236 fn field_ref_text_starts_with_uses_strict_prefix_compare() {
237 assert_eq!(
238 FieldRef::new("name").text_starts_with("Al"),
239 FilterExpr::starts_with("name", "Al"),
240 );
241 }
242
243 #[test]
244 fn field_ref_text_starts_with_ci_uses_casefold_prefix_compare() {
245 assert_eq!(
246 FieldRef::new("name").text_starts_with_ci("AL"),
247 FilterExpr::starts_with_ci("name", "AL"),
248 );
249 }
250
251 #[test]
252 fn field_ref_gt_field_builds_field_compare_filter_expr() {
253 assert_eq!(
254 FieldRef::new("age").gt_field("rank"),
255 FilterExpr::gt_field("age", "rank"),
256 );
257 }
258
259 #[test]
260 fn field_ref_not_between_builds_outside_range_filter_expr() {
261 assert_eq!(
262 FieldRef::new("age").not_between(10_u64, 20_u64),
263 FilterExpr::or(vec![
264 FilterExpr::lt("age", 10_u64),
265 FilterExpr::gt("age", 20_u64),
266 ])
267 );
268 }
269
270 #[test]
271 fn field_ref_between_fields_builds_field_bound_range_filter_expr() {
272 assert_eq!(
273 FieldRef::new("age").between_fields("min_age", "max_age"),
274 FilterExpr::and(vec![
275 FilterExpr::gte_field("age", "min_age"),
276 FilterExpr::lte_field("age", "max_age"),
277 ])
278 );
279 }
280}