option_operations/
ord.rs

1//! Trait for the order [`OptionOperations`].
2
3use core::cmp::Ordering;
4
5use crate::OptionOperations;
6
7/// Trait for values and `Option`s that can be compared for a sort-order.
8///
9/// This implementation is mainly intended at working around the `PartialOrd`
10/// implementation for `Option`, which compares `Option`s
11/// depending on the order of declaration in the `enum`.
12///
13/// ## `PartialOrd` implementation for `Option`
14///
15/// ```
16/// # use core::cmp::Ordering;
17/// let some_0 = Some(0);
18/// let none: Option<u64> = None;
19///
20/// assert_eq!(none.partial_cmp(&some_0), Some(Ordering::Less));
21/// assert_eq!(some_0.partial_cmp(&none), Some(Ordering::Greater));
22/// ```
23///
24/// ## Alternative behavior
25///
26/// In some cases, we might consider that `None` reflects a value which
27/// is not defined and thus can not be compared with `Some(_)`.
28///
29/// ```
30/// # use option_operations::{OptionOperations, OptionOrd};
31/// # let some_0 = Some(0);
32/// # let none: Option<u64> = None;
33/// assert_eq!(none.opt_cmp(&some_0), None);
34/// assert_eq!(some_0.opt_cmp(&none), None);
35/// ```
36///
37/// ## Implementations
38///
39/// Implementing this type leads to the following auto-implementations:
40///
41/// - `OptionOrd<Option<InnerRhs>> for T`.
42/// - `OptionOrd<Rhs> for Option<T>`.
43/// - `OptionOrd<Option<InnerRhs>> for Option<T>`.
44/// - ... and some variants with references.
45///
46/// This trait is auto-implemented for [`OptionOperations`] types
47/// implementing `PartialOrd<Rhs>`.
48pub trait OptionOrd<Rhs, InnerRhs = Rhs> {
49    /// Returns an ordering between `self` and `rhs` values if one exists.
50    ///
51    /// Returns `None` if they can't be compared, e.g. if
52    /// at most one argument is `None`.
53    #[must_use]
54    fn opt_cmp(&self, rhs: Rhs) -> Option<Ordering>;
55
56    /// Tests whether `self` is less than `rhs`.
57    ///
58    /// Returns `None` if they can't be compared, e.g. if
59    /// at most one argument is `None`.
60    #[must_use]
61    fn opt_lt(&self, rhs: Rhs) -> Option<bool> {
62        self.opt_cmp(rhs).map(|ord| matches!(ord, Ordering::Less))
63    }
64
65    /// Tests whether `self` is less or equal to `rhs`.
66    ///
67    /// Returns `None` if they can't be compared, e.g. if
68    /// at most one argument is `None`.
69    #[must_use]
70    fn opt_le(&self, rhs: Rhs) -> Option<bool> {
71        self.opt_cmp(rhs)
72            .map(|ord| matches!(ord, Ordering::Less | Ordering::Equal))
73    }
74
75    /// Tests whether `self` is greater than `rhs`.
76    ///
77    /// Returns `None` if they can't be compared, e.g. if
78    /// at most one argument is `None`.
79    #[must_use]
80    fn opt_gt(&self, rhs: Rhs) -> Option<bool> {
81        self.opt_cmp(rhs)
82            .map(|ord| matches!(ord, Ordering::Greater))
83    }
84
85    /// Tests whether `self` is greater or equal to `rhs`.
86    ///
87    /// Returns `None` if they can't be compared, e.g. if
88    /// at most one argument is `None`.
89    #[must_use]
90    fn opt_ge(&self, rhs: Rhs) -> Option<bool> {
91        self.opt_cmp(rhs)
92            .map(|ord| matches!(ord, Ordering::Greater | Ordering::Equal))
93    }
94}
95
96impl<T, Rhs> OptionOrd<&Rhs, Rhs> for T
97where
98    T: OptionOperations + PartialOrd<Rhs>,
99{
100    fn opt_cmp(&self, rhs: &Rhs) -> Option<Ordering> {
101        self.partial_cmp(rhs)
102    }
103}
104
105impl<T, Rhs> OptionOrd<Rhs> for T
106where
107    T: OptionOperations + for<'a> OptionOrd<&'a Rhs, Rhs>,
108{
109    fn opt_cmp(&self, rhs: Rhs) -> Option<Ordering> {
110        self.opt_cmp(&rhs)
111    }
112}
113
114impl<T, InnerRhs> OptionOrd<&Option<InnerRhs>, InnerRhs> for T
115where
116    T: OptionOperations + for<'a> OptionOrd<&'a InnerRhs, InnerRhs>,
117{
118    fn opt_cmp(&self, rhs: &Option<InnerRhs>) -> Option<Ordering> {
119        rhs.as_ref().and_then(|inner_rhs| self.opt_cmp(inner_rhs))
120    }
121}
122
123impl<T, InnerRhs> OptionOrd<Option<InnerRhs>, InnerRhs> for T
124where
125    T: OptionOperations + for<'a> OptionOrd<&'a InnerRhs, InnerRhs>,
126{
127    fn opt_cmp(&self, rhs: Option<InnerRhs>) -> Option<Ordering> {
128        rhs.as_ref().and_then(|inner_rhs| self.opt_cmp(inner_rhs))
129    }
130}
131
132impl<T, Rhs> OptionOrd<&Rhs, Rhs> for Option<T>
133where
134    T: OptionOperations + for<'a> OptionOrd<&'a Rhs, Rhs>,
135{
136    fn opt_cmp(&self, rhs: &Rhs) -> Option<Ordering> {
137        self.as_ref().and_then(|inner_self| inner_self.opt_cmp(rhs))
138    }
139}
140
141impl<T, Rhs> OptionOrd<Rhs> for Option<T>
142where
143    T: OptionOperations + for<'a> OptionOrd<&'a Rhs, Rhs>,
144{
145    fn opt_cmp(&self, rhs: Rhs) -> Option<Ordering> {
146        self.opt_cmp(&rhs)
147    }
148}
149
150impl<T, InnerRhs> OptionOrd<&Option<InnerRhs>, InnerRhs> for Option<T>
151where
152    T: OptionOperations + for<'a> OptionOrd<&'a InnerRhs, InnerRhs>,
153{
154    fn opt_cmp(&self, rhs: &Option<InnerRhs>) -> Option<Ordering> {
155        match (self, rhs) {
156            (Some(inner_self), Some(inner_rhs)) => inner_self.opt_cmp(inner_rhs),
157            (None, None) => Some(Ordering::Equal),
158            _ => None,
159        }
160    }
161}
162
163impl<T, InnerRhs> OptionOrd<Option<InnerRhs>, InnerRhs> for Option<T>
164where
165    T: OptionOperations + for<'a> OptionOrd<&'a InnerRhs, InnerRhs>,
166{
167    fn opt_cmp(&self, rhs: Option<InnerRhs>) -> Option<Ordering> {
168        match (self, rhs.as_ref()) {
169            (Some(inner_self), Some(inner_rhs)) => inner_self.opt_cmp(inner_rhs),
170            (None, None) => Some(Ordering::Equal),
171            _ => None,
172        }
173    }
174}
175
176#[cfg(test)]
177mod test {
178    use core::cmp::Ordering;
179
180    use super::OptionOrd;
181    use crate::OptionOperations;
182
183    #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
184    struct MyInt(u64);
185
186    impl OptionOperations for MyInt {}
187
188    const MY_1: MyInt = MyInt(1);
189    const MY_2: MyInt = MyInt(2);
190    const SOME_1: Option<MyInt> = Some(MY_1);
191    const SOME_2: Option<MyInt> = Some(MY_2);
192    const NONE: Option<MyInt> = None;
193
194    #[test]
195    fn option_partial_ord_workaround() {
196        // This is the default `partial_cmp` impl for `Option<T>`:
197        assert_eq!(NONE.partial_cmp(&SOME_1), Some(Ordering::Less));
198        assert_eq!(SOME_1.partial_cmp(&NONE), Some(Ordering::Greater));
199
200        // This is what we expect:
201        assert_eq!(NONE.opt_cmp(SOME_1), None);
202        assert_eq!(SOME_1.opt_cmp(NONE), None);
203    }
204
205    #[test]
206    fn opt_cmp() {
207        assert_eq!(NONE.opt_cmp(NONE), Some(Ordering::Equal));
208        assert_eq!(NONE.opt_cmp(&NONE), Some(Ordering::Equal));
209        assert_eq!(SOME_1.opt_cmp(SOME_1), Some(Ordering::Equal));
210        assert_eq!(SOME_1.opt_cmp(SOME_2), Some(Ordering::Less));
211        assert_eq!(SOME_2.opt_cmp(SOME_1), Some(Ordering::Greater));
212
213        assert_eq!(SOME_1.opt_lt(NONE), None);
214        assert_eq!(NONE.opt_lt(SOME_1), None);
215        assert_eq!(NONE.opt_lt(NONE), Some(false));
216        assert_eq!(NONE.opt_le(NONE), Some(true));
217        assert_eq!(SOME_2.opt_lt(SOME_1), Some(false));
218        assert_eq!(SOME_1.opt_le(SOME_2), Some(true));
219
220        assert_eq!(SOME_1.opt_gt(NONE), None);
221        assert_eq!(NONE.opt_gt(SOME_1), None);
222        assert_eq!(NONE.opt_gt(NONE), Some(false));
223        assert_eq!(NONE.opt_ge(NONE), Some(true));
224        assert_eq!(SOME_1.opt_gt(SOME_2), Some(false));
225        assert_eq!(SOME_2.opt_ge(SOME_1), Some(true));
226
227        assert_eq!(SOME_1.opt_cmp(MY_2), Some(Ordering::Less));
228        assert_eq!(SOME_1.opt_cmp(MY_1), Some(Ordering::Equal));
229        assert_eq!(SOME_2.opt_cmp(MY_1), Some(Ordering::Greater));
230        assert_eq!(SOME_2.opt_cmp(&MY_1), Some(Ordering::Greater));
231
232        assert_eq!(MY_1.opt_cmp(NONE), None);
233        assert_eq!(MY_1.opt_cmp(&NONE), None);
234        assert_eq!(MY_1.opt_cmp(MY_2), Some(Ordering::Less));
235        assert_eq!(MY_1.opt_cmp(MY_1), Some(Ordering::Equal));
236        assert_eq!(MY_2.opt_cmp(MY_1), Some(Ordering::Greater));
237        assert_eq!(MY_2.opt_cmp(&MY_1), Some(Ordering::Greater));
238    }
239}