Skip to main content

spru_util/counter/
range_type.rs

1use std::ops::{self, RangeBounds as _};
2
3use super::CounterType;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub enum RangeType<T> {
7    Full(ops::RangeFull),
8    From(ops::RangeFrom<T>),
9    Inclusive(ops::RangeInclusive<T>),
10    ToInclusive(ops::RangeToInclusive<T>),
11    Exclusive(ops::Range<T>),
12    ToExclusive(ops::RangeTo<T>),
13}
14
15impl<T: CounterType> RangeType<T> {
16    pub fn constrain(&self, value: T) -> T {
17        let start = match self.start_bound() {
18            ops::Bound::Included(start) => Some(start.clone()),
19            ops::Bound::Excluded(_) | ops::Bound::Unbounded => None,
20        };
21        let end = match self.end_bound() {
22            ops::Bound::Included(end) => Some(end.clone()),
23            ops::Bound::Excluded(end) => Some(end.clone() - num_traits::One::one()),
24            ops::Bound::Unbounded => None,
25        };
26        match (start, end) {
27            (None, None) => value,
28            (None, Some(end)) => num_traits::clamp_max(value, end),
29            (Some(start), None) => num_traits::clamp_min(value, start),
30            (Some(start), Some(end)) => num_traits::clamp(value, start, end),
31        }
32    }
33}
34
35impl<T: serde::Serialize> serde::Serialize for RangeType<T> {
36    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37    where
38        S: serde::Serializer,
39    {
40        let start = self.start_bound();
41        let end = self.end_bound();
42        (start, end).serialize(serializer)
43    }
44}
45
46impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for RangeType<T> {
47    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48    where
49        D: serde::Deserializer<'de>,
50    {
51        let (start, end) = <(ops::Bound<T>, ops::Bound<T>)>::deserialize(deserializer)?;
52        let value = match (start, end) {
53            // Technically a fully-iterated `..=` has an `Excluded` end `Bound`,
54            // but even if we are given such a range, we don't care about preserving
55            // that property
56            (ops::Bound::Included(start), ops::Bound::Included(end)) => {
57                Self::Inclusive(start..=end)
58            }
59            (ops::Bound::Included(start), ops::Bound::Excluded(end)) => Self::Exclusive(start..end),
60            (ops::Bound::Included(start), ops::Bound::Unbounded) => Self::From(start..),
61            (ops::Bound::Unbounded, ops::Bound::Included(end)) => Self::ToInclusive(..=end),
62            (ops::Bound::Unbounded, ops::Bound::Excluded(end)) => Self::ToExclusive(..end),
63            (ops::Bound::Unbounded, ops::Bound::Unbounded) => Self::Full(ops::RangeFull),
64            (ops::Bound::Excluded(_), _) => {
65                return Err(<D::Error as serde::de::Error>::invalid_value(
66                    serde::de::Unexpected::Other("excluded start bound"),
67                    &"included or unbounded start",
68                ));
69            }
70        };
71        Ok(value)
72    }
73}
74
75impl<T> Default for RangeType<T> {
76    fn default() -> Self {
77        Self::Full(ops::RangeFull)
78    }
79}
80
81impl<T> ops::RangeBounds<T> for RangeType<T> {
82    fn start_bound(&self) -> ops::Bound<&T> {
83        match self {
84            RangeType::Full(r) => r.start_bound(),
85            RangeType::From(r) => r.start_bound(),
86            RangeType::Inclusive(r) => r.start_bound(),
87            RangeType::ToInclusive(r) => r.start_bound(),
88            RangeType::Exclusive(r) => r.start_bound(),
89            RangeType::ToExclusive(r) => r.start_bound(),
90        }
91    }
92
93    fn end_bound(&self) -> ops::Bound<&T> {
94        match self {
95            RangeType::Full(r) => r.end_bound(),
96            RangeType::From(r) => r.end_bound(),
97            RangeType::Inclusive(r) => r.end_bound(),
98            RangeType::ToInclusive(r) => r.end_bound(),
99            RangeType::Exclusive(r) => r.end_bound(),
100            RangeType::ToExclusive(r) => r.end_bound(),
101        }
102    }
103}
104
105impl<T> From<ops::RangeFull> for RangeType<T> {
106    fn from(value: ops::RangeFull) -> Self {
107        Self::Full(value)
108    }
109}
110
111impl<T> From<ops::RangeFrom<T>> for RangeType<T> {
112    fn from(value: ops::RangeFrom<T>) -> Self {
113        Self::From(value)
114    }
115}
116
117impl<T> From<ops::RangeInclusive<T>> for RangeType<T> {
118    fn from(value: ops::RangeInclusive<T>) -> Self {
119        Self::Inclusive(value)
120    }
121}
122
123impl<T> From<ops::RangeToInclusive<T>> for RangeType<T> {
124    fn from(value: ops::RangeToInclusive<T>) -> Self {
125        Self::ToInclusive(value)
126    }
127}
128
129impl<T> From<ops::Range<T>> for RangeType<T> {
130    fn from(value: ops::Range<T>) -> Self {
131        Self::Exclusive(value)
132    }
133}
134
135impl<T> From<ops::RangeTo<T>> for RangeType<T> {
136    fn from(value: ops::RangeTo<T>) -> Self {
137        Self::ToExclusive(value)
138    }
139}