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: &InnerRhs| 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: &InnerRhs| 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: &T| 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: &T), Some(inner_rhs: &InnerRhs)) => 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: &T), Some(inner_rhs: &InnerRhs)) => 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}
240