batbox_cmp/
lib.rs

1//! Extra utilities for [Ord] and [PartialOrd] types
2#![warn(missing_docs)]
3
4use batbox_range::*;
5
6pub use std::cmp::{max, min};
7
8/// Extension trait for getting minimum/maximum of values grouped together
9pub trait MinMax: Sized {
10    /// Type of a single value
11    type T;
12
13    /// Find (min, max)
14    fn min_max(self) -> (Self::T, Self::T);
15
16    /// Find min
17    fn min(self) -> Self::T {
18        self.min_max().0
19    }
20
21    /// Find max
22    fn max(self) -> Self::T {
23        self.min_max().1
24    }
25}
26
27impl<T: Ord> MinMax for (T, T) {
28    type T = T;
29    fn min_max(self) -> (T, T) {
30        let (a, b) = self;
31        if a.cmp(&b) == std::cmp::Ordering::Less {
32            (a, b)
33        } else {
34            (b, a)
35        }
36    }
37}
38
39/// Compares arguments and returns (min, max)
40pub fn min_max<T: Ord>(a: T, b: T) -> (T, T) {
41    (a, b).min_max()
42}
43
44/// Extension trait for getting minimum/maximum of values grouped together
45pub trait PartialMinMax: Sized {
46    /// Type of a single value
47    type T;
48
49    /// Find (min, max)
50    fn partial_min_max(self) -> (Self::T, Self::T);
51
52    /// Find min
53    fn partial_min(self) -> Self::T {
54        self.partial_min_max().0
55    }
56
57    /// Find max
58    fn partial_max(self) -> Self::T {
59        self.partial_min_max().1
60    }
61}
62
63impl<T: PartialOrd> PartialMinMax for (T, T) {
64    type T = T;
65    fn partial_min_max(self) -> (T, T) {
66        let (a, b) = self;
67        if a.partial_cmp(&b) == Some(std::cmp::Ordering::Less) {
68            (a, b)
69        } else {
70            (b, a)
71        }
72    }
73}
74
75/// Compares and returns the minimum of two values
76pub fn partial_min<T: PartialOrd>(a: T, b: T) -> T {
77    (a, b).partial_min()
78}
79
80/// Compares and returns the maximum of two values
81pub fn partial_max<T: PartialOrd>(a: T, b: T) -> T {
82    (a, b).partial_max()
83}
84
85/// Compares arguments and returns (min, max)
86pub fn partial_min_max<T: PartialOrd>(a: T, b: T) -> (T, T) {
87    (a, b).partial_min_max()
88}
89
90/// Provides methods for clamping values
91pub trait Clamp: Sized + PartialOrd {
92    /// Clamps a value in range.
93    /// # Examples
94    /// ```
95    /// # use batbox_cmp::*;
96    /// assert_eq!(2.0.clamp_range(0.0..=1.0), 1.0);
97    /// assert_eq!(2.0.clamp_range(3.0..), 3.0);
98    /// assert_eq!(2.0.clamp_range(..=0.0), 0.0);
99    /// ```
100    fn clamp_range(mut self, range: impl FixedRangeBounds<Self>) -> Self
101    where
102        Self: Clone,
103    {
104        match range.start_bound() {
105            FixedBound::Included(start) => self = partial_max(self, start.clone()),
106            FixedBound::Unbounded => (),
107        }
108        match range.end_bound() {
109            FixedBound::Included(end) => self = partial_min(self, end.clone()),
110            FixedBound::Unbounded => (),
111        }
112        self
113    }
114
115    /// Clamp the absolute value. Same as self.clamp_range(-max..=max)
116    fn clamp_abs(self, max: Self) -> Self
117    where
118        Self: std::ops::Neg<Output = Self> + Copy,
119    {
120        self.clamp_range(-max..=max)
121    }
122
123    /// Clamp by maximum value
124    ///
125    /// If self is greater than max then return max, otherwise return self
126    fn clamp_max(self, max: Self) -> Self {
127        partial_min(self, max)
128    }
129
130    /// Clamp by minimum value
131    ///
132    /// If self is less than min then return min, otherwise return self
133    fn clamp_min(self, min: Self) -> Self {
134        partial_max(self, min)
135    }
136}
137
138impl<T: PartialOrd> Clamp for T {}