1 | //! Trait for the order [`OptionOperations`]. |
2 | |
3 | use core::cmp::Ordering; |
4 | |
5 | use 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>`. |
48 | pub 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 | |
96 | impl<T, Rhs> OptionOrd<&Rhs, Rhs> for T |
97 | where |
98 | T: OptionOperations + PartialOrd<Rhs>, |
99 | { |
100 | fn opt_cmp(&self, rhs: &Rhs) -> Option<Ordering> { |
101 | self.partial_cmp(rhs) |
102 | } |
103 | } |
104 | |
105 | impl<T, Rhs> OptionOrd<Rhs> for T |
106 | where |
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 | |
114 | impl<T, InnerRhs> OptionOrd<&Option<InnerRhs>, InnerRhs> for T |
115 | where |
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 | |
123 | impl<T, InnerRhs> OptionOrd<Option<InnerRhs>, InnerRhs> for T |
124 | where |
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 | |
132 | impl<T, Rhs> OptionOrd<&Rhs, Rhs> for Option<T> |
133 | where |
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 | |
141 | impl<T, Rhs> OptionOrd<Rhs> for Option<T> |
142 | where |
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 | |
150 | impl<T, InnerRhs> OptionOrd<&Option<InnerRhs>, InnerRhs> for Option<T> |
151 | where |
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 | |
163 | impl<T, InnerRhs> OptionOrd<Option<InnerRhs>, InnerRhs> for Option<T> |
164 | where |
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)] |
177 | mod 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 | |