django_query/filtering/
ops.rs

1//! Standard operators from Django for use with filtering.
2//!
3//! These have short names, matching their Django names, that can be used in the
4//! derive macro for [Filterable](crate::filtering::Filterable). There are the following operators:
5//!
6//! [`Operator`]       | [`OperatorClass`] | Short name   | Restrictions
7//! -------------------|-------------------|--------------|--------------
8//! [`ExactImpl`]      | [`Exact`]         | `exact`      | T: [`Eq`](core::cmp::Eq)
9//! [`InImpl`]         | [`In`]            | `in`         | T: [`Eq`](core::cmp::Eq)
10//! [`ContainsImpl`]   | [`Contains`]      | `contains`   | T: [`Display`](std::fmt::Display)
11//! [`IContainsImpl`]  | [`IContains`]     | `icontains`  | T: [`Display`](std::fmt::Display)
12//! [`IExactImpl`]     | [`IExact`]        | `iexact`     | T: [`Display`](std::fmt::Display)
13//! [`StartsWithImpl`] | [`StartsWith`]    | `startswith` | T: [`Display`](std::fmt::Display)
14//! [`EndsWithImpl`]   | [`EndsWith`]      | `endswith`   | T: [`Display`](std::fmt::Display)
15//! [`LessImpl`]       | [`Less`]          | `lt`         | T: [`Ord`](core::cmp::Ord)
16//! [`GreaterImpl`]    | [`Greater`]       | `gt`         | T: [`Ord`](core::cmp::Ord)
17//! [`LessEqImpl`]     | [`LessEq`]        | `lte`        | T: [`Ord`](core::cmp::Ord)
18//! [`GreaterEqImpl`]  | [`GreaterEq`]     | `gte`        | T: [`Ord`](core::cmp::Ord)
19//! [`IsNullImpl`]     | [`IsNull`]        | `isnull`     |
20
21use std::fmt::Display;
22use std::str::FromStr;
23
24use crate::filtering::{FilterError, Operable, Operator, OperatorClass};
25
26/// A value that is directly [`Operable`].
27pub trait Scalar {}
28
29impl Scalar for i8 {}
30impl Scalar for u8 {}
31impl Scalar for i16 {}
32impl Scalar for u16 {}
33impl Scalar for i32 {}
34impl Scalar for u32 {}
35impl Scalar for i64 {}
36impl Scalar for u64 {}
37impl Scalar for bool {}
38impl Scalar for String {}
39
40impl<T: chrono::TimeZone> Scalar for chrono::DateTime<T> {}
41
42impl<T> Operable for T
43where
44    T: Scalar,
45{
46    type Base = Self;
47    fn apply<O: Operator<Self::Base>>(&self, op: &O) -> bool {
48        op.apply(self)
49    }
50}
51
52/// Match when the value is equal to the target.
53pub struct ExactImpl<T> {
54    target: T,
55}
56
57impl<T> Operator<T> for ExactImpl<T>
58where
59    T: core::cmp::Eq,
60{
61    fn apply(&self, value: &T) -> bool {
62        value == &self.target
63    }
64}
65
66/// The [`OperatorClass`] that can instantiate [`ExactImpl`].
67pub struct Exact;
68
69impl<T> OperatorClass<T> for Exact
70where
71    T: core::cmp::Eq + FromStr,
72    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
73{
74    type Instance = ExactImpl<T>;
75    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
76        Ok(ExactImpl {
77            target: T::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
78        })
79    }
80}
81
82/// Match when the value is equal to one of the targets.
83pub struct InImpl<T> {
84    targets: Vec<T>,
85}
86
87impl<T> Operator<T> for InImpl<T>
88where
89    T: core::cmp::Eq + FromStr,
90{
91    fn apply(&self, value: &T) -> bool {
92        self.targets.contains(value)
93    }
94}
95
96/// The [`OperatorClass`] that can instantiate [`InImpl`].
97pub struct In;
98
99impl<T> OperatorClass<T> for In
100where
101    T: core::cmp::Eq + FromStr,
102    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
103{
104    type Instance = InImpl<T>;
105    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
106        let mut targets = Vec::new();
107        for elt in rhs.split(',') {
108            targets.push(T::from_str(elt).map_err(|e| FilterError::Instantiation(e.into()))?);
109        }
110        Ok(InImpl { targets })
111    }
112}
113
114/// Match when the string representation of the value contains the target.
115pub struct ContainsImpl {
116    target: String,
117}
118
119impl<T> Operator<T> for ContainsImpl
120where
121    T: Display,
122{
123    fn apply(&self, value: &T) -> bool {
124        value.to_string().contains(&self.target)
125    }
126}
127
128/// The [`OperatorClass`] that can instantiate [`ContainsImpl`].
129pub struct Contains;
130
131impl<T> OperatorClass<T> for Contains
132where
133    T: Display + FromStr,
134    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
135{
136    type Instance = ContainsImpl;
137    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
138        Ok(ContainsImpl {
139            target: rhs.to_string(),
140        })
141    }
142}
143
144/// Match when the string representation of the value contains the target case insensitively.
145pub struct IContainsImpl {
146    target: String,
147}
148
149impl<T> Operator<T> for IContainsImpl
150where
151    T: Display,
152{
153    fn apply(&self, value: &T) -> bool {
154        value.to_string().to_lowercase().contains(&self.target)
155    }
156}
157
158/// The [`OperatorClass`] that can instantiate [`IContainsImpl`].
159pub struct IContains;
160
161impl<T> OperatorClass<T> for IContains
162where
163    T: Display + FromStr,
164    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
165{
166    type Instance = IContainsImpl;
167    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
168        Ok(IContainsImpl {
169            target: rhs.to_lowercase(),
170        })
171    }
172}
173
174/// Match when the string representation of the value is exactly the target, case insensitively.
175pub struct IExactImpl {
176    target: String,
177}
178
179impl<T> Operator<T> for IExactImpl
180where
181    T: Display,
182{
183    fn apply(&self, value: &T) -> bool {
184        value.to_string().to_lowercase() == self.target
185    }
186}
187
188/// The [`OperatorClass`] that can instantiate [`IExactImpl`].
189pub struct IExact;
190
191impl<T> OperatorClass<T> for IExact
192where
193    T: Display + FromStr,
194    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
195{
196    type Instance = IExactImpl;
197    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
198        Ok(IExactImpl {
199            target: rhs.to_lowercase(),
200        })
201    }
202}
203
204/// Match when the string representation of the value starts with the target.
205pub struct StartsWithImpl {
206    target: String,
207}
208
209impl<T> Operator<T> for StartsWithImpl
210where
211    T: Display,
212{
213    fn apply(&self, value: &T) -> bool {
214        value.to_string().starts_with(&self.target)
215    }
216}
217
218/// The [`OperatorClass`] that can instantiate [`StartsWithImpl`].
219pub struct StartsWith;
220
221impl<T> OperatorClass<T> for StartsWith
222where
223    T: Display + FromStr,
224    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
225{
226    type Instance = StartsWithImpl;
227    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
228        Ok(StartsWithImpl {
229            target: rhs.to_string(),
230        })
231    }
232}
233
234/// Match when the string representation of the value ends with the target.
235pub struct EndsWithImpl {
236    target: String,
237}
238
239impl<T> Operator<T> for EndsWithImpl
240where
241    T: Display,
242{
243    fn apply(&self, value: &T) -> bool {
244        value.to_string().ends_with(&self.target)
245    }
246}
247
248/// The [`OperatorClass`] that can instantiate [`EndsWithImpl`].
249pub struct EndsWith;
250
251impl<T> OperatorClass<T> for EndsWith
252where
253    T: Display + FromStr,
254    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
255{
256    type Instance = EndsWithImpl;
257    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
258        Ok(EndsWithImpl {
259            target: rhs.to_string(),
260        })
261    }
262}
263
264/// Match when value is less than the target.
265pub struct LessImpl<T> {
266    target: T,
267}
268
269impl<T> Operator<T> for LessImpl<T>
270where
271    T: core::cmp::Ord,
272{
273    fn apply(&self, value: &T) -> bool {
274        value < &self.target
275    }
276}
277
278/// The [`OperatorClass`] that can instantiate [`LessImpl`].
279pub struct Less;
280
281impl<T> OperatorClass<T> for Less
282where
283    T: Ord + FromStr,
284    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
285{
286    type Instance = LessImpl<T>;
287    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
288        Ok(LessImpl {
289            target: T::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
290        })
291    }
292}
293
294/// Match when value is greater than the target.
295pub struct GreaterImpl<T> {
296    target: T,
297}
298
299impl<T> Operator<T> for GreaterImpl<T>
300where
301    T: core::cmp::Ord,
302{
303    fn apply(&self, value: &T) -> bool {
304        value > &self.target
305    }
306}
307
308/// The [`OperatorClass`] that can instantiate [`GreaterImpl`].
309pub struct Greater;
310
311impl<T> OperatorClass<T> for Greater
312where
313    T: Ord + FromStr,
314    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
315{
316    type Instance = GreaterImpl<T>;
317    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
318        Ok(GreaterImpl {
319            target: T::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
320        })
321    }
322}
323
324/// Match when the value is less than or equal to the target.
325pub struct LessEqImpl<T> {
326    target: T,
327}
328
329impl<T> Operator<T> for LessEqImpl<T>
330where
331    T: core::cmp::Ord,
332{
333    fn apply(&self, value: &T) -> bool {
334        value <= &self.target
335    }
336}
337
338/// The [`OperatorClass`] that can instantiate [`LessEqImpl`].
339pub struct LessEq;
340
341impl<T> OperatorClass<T> for LessEq
342where
343    T: Ord + FromStr,
344    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
345{
346    type Instance = LessEqImpl<T>;
347    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
348        Ok(LessEqImpl {
349            target: T::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
350        })
351    }
352}
353
354/// Match when the value is greater than or equal to the target.
355pub struct GreaterEqImpl<T> {
356    target: T,
357}
358
359impl<T> Operator<T> for GreaterEqImpl<T>
360where
361    T: core::cmp::Ord,
362{
363    fn apply(&self, value: &T) -> bool {
364        value >= &self.target
365    }
366}
367
368/// The [`OperatorClass`] that can instantiate [`GreaterEqImpl`].
369pub struct GreaterEq;
370
371impl<T> OperatorClass<T> for GreaterEq
372where
373    T: Ord + FromStr,
374    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
375{
376    type Instance = GreaterEqImpl<T>;
377    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
378        Ok(GreaterEqImpl {
379            target: T::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
380        })
381    }
382}
383
384/// Match when there is no value, because a containing [`Option`] is [`None`].
385pub struct IsNullImpl {
386    target: bool,
387}
388
389impl<T> Operator<T> for IsNullImpl {
390    fn apply(&self, _value: &T) -> bool {
391        !self.target
392    }
393    fn null_option(&self) -> bool {
394        self.target
395    }
396}
397
398/// The [`OperatorClass`] that can instantiate [`IsNullImpl`].
399pub struct IsNull;
400
401impl<T> OperatorClass<T> for IsNull {
402    type Instance = IsNullImpl;
403    fn instantiate(&self, rhs: &str) -> Result<Self::Instance, FilterError> {
404        Ok(IsNullImpl {
405            target: bool::from_str(rhs).map_err(|e| FilterError::Instantiation(e.into()))?,
406        })
407    }
408}