1use num_traits::{SaturatingAdd, SaturatingMul, SaturatingSub};
5use std::ops::{AddAssign, Div, DivAssign, MulAssign, Sub, SubAssign};
6
7#[derive(Debug)]
9pub struct ClampedValue<T: PartialOrd + Clone> {
10 value: T,
11 min: T,
12 max: T,
13}
14
15impl<T: PartialOrd + Clone> ClampedValue<T> {
16 pub fn new(min: T, value: T, max: T) -> Self {
24 if min > max {
25 panic!("Cannot create a clamped value where the minimum is larger than the maximum");
26 } else if value < min || value > max {
27 panic!("Cannot create a clamped value where the value is not within the minimum and maximum");
28 }
29
30 Self { value, min, max }
31 }
32
33 pub fn value(&self) -> &T {
34 &self.value
35 }
36
37 pub fn min(&self) -> &T {
38 &self.min
39 }
40
41 pub fn max(&self) -> &T {
42 &self.max
43 }
44
45 pub fn set_min(&mut self, new_min: T) {
53 if new_min > self.max {
54 panic!("Cannot set the minimum to a value that is larger than the maximum");
55 } else if new_min > self.value {
56 panic!("Cannot set the minimum to a value that is larger than the current value");
57 }
58
59 self.min = new_min;
60 }
61
62 pub fn set_max(&mut self, new_max: T) {
70 if new_max < self.min {
71 panic!("Cannot set the maximum to a value that is smaller than the minimum");
72 } else if new_max < self.value {
73 panic!("Cannot set the maximum to a value that is smaller than the current value")
74 }
75
76 self.max = new_max;
77 }
78
79 pub fn set(&mut self, new_value: T) {
81 self.value = new_value;
82 self.clamp();
83 }
84
85 fn clamp(&mut self) {
87 if self.value < self.min {
88 self.value = self.min.clone();
89 } else if self.value > self.max {
90 self.value = self.max.clone();
91 }
92 }
93}
94
95impl<T> ClampedValue<T>
96where
97 T: Into<f32> + Sub<Output = T> + PartialOrd + Clone,
98{
99 pub fn percent_f32(&self) -> f32 {
113 self.percent::<f32>()
114 }
115}
116
117impl<T> ClampedValue<T>
118where
119 T: Into<f64> + Sub<Output = T> + PartialOrd + Clone,
120{
121 pub fn percent_f64(&self) -> f64 {
135 self.percent::<f64>()
136 }
137}
138
139impl<T: Sub<Output = T> + PartialOrd + Clone> ClampedValue<T> {
141 fn percent<U>(&self) -> U
142 where
143 U: Div<Output = U>,
144 T: Into<U>,
145 {
146 (self.value.clone() - self.min.clone()).into()
149 / (self.max.clone() - self.min.clone()).into()
150 }
151}
152
153impl<T: SaturatingAdd + PartialOrd + Clone> AddAssign<T> for ClampedValue<T> {
158 fn add_assign(&mut self, rhs: T) {
172 self.value = self.value.saturating_add(&rhs);
173 self.clamp();
174 }
175}
176
177impl<T: SaturatingSub + PartialOrd + Clone> SubAssign<T> for ClampedValue<T> {
178 fn sub_assign(&mut self, rhs: T) {
192 self.value = self.value.saturating_sub(&rhs);
193 self.clamp();
194 }
195}
196
197impl<T: SaturatingMul + PartialOrd + Clone> MulAssign<T> for ClampedValue<T> {
198 fn mul_assign(&mut self, rhs: T) {
212 self.value = self.value.saturating_mul(&rhs);
213 self.clamp();
214 }
215}
216
217impl<T: Div<Output = T> + PartialOrd + Clone> DivAssign<T> for ClampedValue<T> {
218 fn div_assign(&mut self, rhs: T) {
232 self.value = self.value.clone() / rhs;
233 self.clamp();
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use crate::ClampedValue;
240
241 #[test]
242 fn new() {
243 ClampedValue::new(10, 20, 30);
244 }
245
246 #[test]
247 #[should_panic]
248 fn new_min_larger_than_max() {
249 ClampedValue::new(30, 10, 20);
250 }
251
252 #[test]
253 #[should_panic]
254 fn new_value_outside_min_max() {
255 ClampedValue::new(10, 40, 30);
256 }
257
258 #[test]
259 fn set() {
260 let mut clamped_value = ClampedValue::new(10, 50, 110);
261
262 clamped_value.set_min(22);
263 assert_eq!(*clamped_value.min(), 22);
264
265 clamped_value.set_max(99);
266 assert_eq!(*clamped_value.max(), 99);
267
268 clamped_value.set(55);
269 assert_eq!(*clamped_value.value(), 55);
270
271 clamped_value.set(1000);
272 assert_eq!(*clamped_value.value(), *clamped_value.max());
273
274 clamped_value.set(-1000);
275 assert_eq!(*clamped_value.value(), *clamped_value.min());
276 }
277
278 #[test]
279 #[should_panic]
280 fn set_min_larger_than_max() {
281 ClampedValue::new(10, 20, 30).set_min(40);
282 }
283
284 #[test]
285 #[should_panic]
286 fn set_min_larger_than_value() {
287 ClampedValue::new(10, 20, 30).set_min(25);
288 }
289
290 #[test]
291 #[should_panic]
292 fn set_max_smaller_than_min() {
293 ClampedValue::new(10, 20, 30).set_max(0);
294 }
295
296 #[test]
297 #[should_panic]
298 fn set_max_smaller_than_value() {
299 ClampedValue::new(10, 20, 30).set_max(15);
300 }
301
302 #[test]
303 fn percent() {
304 let c = ClampedValue::<u8>::new(75, 100, 125);
306 assert_eq!(c.percent_f32(), 0.5);
307 assert_eq!(c.percent_f64(), 0.5);
308
309 let c = ClampedValue::<i8>::new(-100, -40, -20);
311 assert_eq!(c.percent_f32(), 0.75);
312 assert_eq!(c.percent_f64(), 0.75);
313
314 let c = ClampedValue::<i8>::new(-40, -10, 40);
316 assert_eq!(c.percent_f32(), 0.375);
317 assert_eq!(c.percent_f64(), 0.375);
318 }
319
320 #[test]
321 fn operations() {
322 let mut clamped_value = ClampedValue::new(20, 20, 40);
323
324 clamped_value += 100;
325 assert_eq!(*clamped_value.value(), *clamped_value.max());
326
327 clamped_value -= 100;
328 assert_eq!(*clamped_value.value(), *clamped_value.min(),);
329
330 clamped_value *= 100;
331 assert_eq!(*clamped_value.value(), *clamped_value.max(),);
332
333 clamped_value /= 10;
334 assert_eq!(*clamped_value.value(), *clamped_value.min());
335 }
336}