option_operations/
min_max.rs

1//! Traits for the minimun and maximum [`OptionOperations`].
2
3// Required for doc
4#[allow(unused)]
5use crate::OptionOperations;
6
7use crate::OptionOrd;
8
9/// Trait for values and `Option`s that can be compared
10/// to get the minimum or maximum.
11///
12/// Implementing this type leads to the following auto-implementations:
13///
14/// - `OptionMinMax<Option<InnerRhs>> for T`.
15/// - `OptionMinMax<Rhs> for Option<T>`.
16/// - `OptionMinMax<Option<InnerRhs>> for Option<T>`.
17/// - ... and some variants with references.
18///
19/// This trait is auto-implemented for [`OptionOperations`] types
20/// implementing `OptionOrd<Rhs>`.
21pub trait OptionMinMax<Other, Inner = Other> {
22    /// Compares and returns the minimum of two values.
23    ///
24    /// Returns `None` if they can't be compared, e.g. if
25    /// at most one argument is `None`.
26    #[must_use]
27    fn opt_min(self, other: Other) -> Option<Inner>;
28
29    /// Compares and returns the maximum of two values.
30    ///
31    /// Returns `None` if they can't be compared, e.g. if
32    /// at most one argument is `None`.
33    #[must_use]
34    fn opt_max(self, other: Other) -> Option<Inner>;
35}
36
37impl<T> OptionMinMax<T> for T
38where
39    T: for<'a> OptionOrd<&'a T, T>,
40{
41    fn opt_min(self, other: T) -> Option<T> {
42        self.opt_lt(&other)
43            .map(|is_lt| if is_lt { self } else { other })
44    }
45
46    fn opt_max(self, other: T) -> Option<T> {
47        self.opt_gt(&other)
48            .map(|is_gt| if is_gt { self } else { other })
49    }
50}
51
52impl<T> OptionMinMax<Option<T>, T> for T
53where
54    T: for<'a> OptionOrd<&'a T, T>,
55{
56    fn opt_min(self, other: Option<T>) -> Option<T> {
57        other.and_then(|inner_other| {
58            self.opt_lt(&inner_other)
59                .map(|is_lt| if is_lt { self } else { inner_other })
60        })
61    }
62
63    fn opt_max(self, other: Option<T>) -> Option<T> {
64        other.and_then(|inner_other| {
65            self.opt_gt(&inner_other)
66                .map(|is_gt| if is_gt { self } else { inner_other })
67        })
68    }
69}
70
71impl<T> OptionMinMax<T> for Option<T>
72where
73    T: for<'a> OptionOrd<&'a T, T>,
74{
75    fn opt_min(self, other: T) -> Option<T> {
76        self.and_then(|inner_self| {
77            inner_self
78                .opt_lt(&other)
79                .map(|is_lt| if is_lt { inner_self } else { other })
80        })
81    }
82
83    fn opt_max(self, other: T) -> Option<T> {
84        self.and_then(|inner_self| {
85            inner_self
86                .opt_gt(&other)
87                .map(|is_gt| if is_gt { inner_self } else { other })
88        })
89    }
90}
91
92impl<T> OptionMinMax<Option<T>, T> for Option<T>
93where
94    T: for<'a> OptionOrd<&'a T, T>,
95{
96    fn opt_min(self, other: Option<T>) -> Option<T> {
97        self.zip(other).and_then(|(inner_self, inner_other)| {
98            inner_self
99                .opt_lt(&inner_other)
100                .map(|is_lt| if is_lt { inner_self } else { inner_other })
101        })
102    }
103
104    fn opt_max(self, other: Option<T>) -> Option<T> {
105        self.zip(other).and_then(|(inner_self, inner_other)| {
106            inner_self
107                .opt_gt(&inner_other)
108                .map(|is_gt| if is_gt { inner_self } else { inner_other })
109        })
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::OptionMinMax;
116    use crate::OptionOperations;
117
118    #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
119    struct MyInt(u64);
120
121    impl OptionOperations for MyInt {}
122
123    const MY_1: MyInt = MyInt(1);
124    const MY_2: MyInt = MyInt(2);
125    const SOME_1: Option<MyInt> = Some(MY_1);
126    const SOME_2: Option<MyInt> = Some(MY_2);
127    const NONE: Option<MyInt> = None;
128
129    #[test]
130    fn min() {
131        assert_eq!(SOME_1.opt_min(SOME_2), SOME_1);
132        assert_eq!(SOME_2.opt_min(SOME_1), SOME_1);
133        assert_eq!(SOME_1.opt_min(NONE), None);
134
135        assert_eq!(SOME_1.opt_min(MY_2), SOME_1);
136        assert_eq!(SOME_2.opt_min(MY_1), SOME_1);
137
138        assert_eq!(MY_1.opt_min(MY_2), SOME_1);
139        assert_eq!(MY_2.opt_min(MY_1), SOME_1);
140
141        assert_eq!(MY_1.opt_min(SOME_2), SOME_1);
142        assert_eq!(MY_2.opt_min(SOME_1), SOME_1);
143
144        assert_eq!(MY_1.opt_min(NONE), None);
145        assert_eq!(NONE.opt_min(MY_1), None);
146
147        assert_eq!(SOME_1.opt_min(NONE).or(SOME_1), SOME_1);
148    }
149
150    #[test]
151    fn max() {
152        assert_eq!(SOME_1.opt_max(SOME_2), SOME_2);
153        assert_eq!(SOME_2.opt_max(SOME_1), SOME_2);
154        assert_eq!(SOME_1.opt_max(NONE), None);
155
156        assert_eq!(SOME_1.opt_max(MY_2), SOME_2);
157        assert_eq!(SOME_2.opt_max(MY_1), SOME_2);
158
159        assert_eq!(MY_1.opt_max(MY_2), SOME_2);
160        assert_eq!(MY_2.opt_max(MY_1), SOME_2);
161
162        assert_eq!(MY_1.opt_max(SOME_2), SOME_2);
163        assert_eq!(MY_2.opt_max(SOME_1), SOME_2);
164
165        assert_eq!(MY_1.opt_max(NONE), None);
166        assert_eq!(NONE.opt_max(MY_1), None);
167
168        assert_eq!(SOME_1.opt_max(NONE).or(SOME_1), SOME_1);
169    }
170}