hopr_primitive_types/
bounded.rs

1use std::fmt::{Display, Formatter};
2
3use crate::prelude::GeneralError;
4
5/// Unsigned integer (`usize`) that has an explicit upper bound.
6/// Trying to convert an integer that's above this bound will fail.
7#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash, serde::Serialize, serde::Deserialize)]
8pub struct BoundedSize<const B: usize>(usize);
9
10impl<const B: usize> BoundedSize<B> {
11    /// Maximum value - evaluates to `B`.
12    pub const MAX: Self = Self(B);
13    /// Minimum value - evaluates to 0.
14    pub const MIN: Self = Self(0);
15}
16
17impl<const B: usize> TryFrom<u8> for BoundedSize<B> {
18    type Error = GeneralError;
19
20    fn try_from(value: u8) -> Result<Self, Self::Error> {
21        (value as usize).try_into()
22    }
23}
24
25impl<const B: usize> TryFrom<u16> for BoundedSize<B> {
26    type Error = GeneralError;
27
28    fn try_from(value: u16) -> Result<Self, Self::Error> {
29        (value as usize).try_into()
30    }
31}
32
33impl<const B: usize> TryFrom<u32> for BoundedSize<B> {
34    type Error = GeneralError;
35
36    fn try_from(value: u32) -> Result<Self, Self::Error> {
37        (value as usize).try_into()
38    }
39}
40
41impl<const B: usize> TryFrom<u64> for BoundedSize<B> {
42    type Error = GeneralError;
43
44    fn try_from(value: u64) -> Result<Self, Self::Error> {
45        (value as usize).try_into()
46    }
47}
48
49impl<const B: usize> TryFrom<usize> for BoundedSize<B> {
50    type Error = GeneralError;
51
52    fn try_from(value: usize) -> Result<Self, Self::Error> {
53        if value <= B {
54            Ok(Self(value))
55        } else {
56            Err(GeneralError::InvalidInput)
57        }
58    }
59}
60
61impl<const B: usize> TryFrom<i8> for BoundedSize<B> {
62    type Error = GeneralError;
63
64    fn try_from(value: i8) -> Result<Self, Self::Error> {
65        Self::try_from(value as isize)
66    }
67}
68
69impl<const B: usize> TryFrom<i16> for BoundedSize<B> {
70    type Error = GeneralError;
71
72    fn try_from(value: i16) -> Result<Self, Self::Error> {
73        Self::try_from(value as isize)
74    }
75}
76
77impl<const B: usize> TryFrom<i32> for BoundedSize<B> {
78    type Error = GeneralError;
79
80    fn try_from(value: i32) -> Result<Self, Self::Error> {
81        Self::try_from(value as isize)
82    }
83}
84
85impl<const B: usize> TryFrom<i64> for BoundedSize<B> {
86    type Error = GeneralError;
87
88    fn try_from(value: i64) -> Result<Self, Self::Error> {
89        Self::try_from(value as isize)
90    }
91}
92
93impl<const B: usize> TryFrom<isize> for BoundedSize<B> {
94    type Error = GeneralError;
95
96    fn try_from(value: isize) -> Result<Self, Self::Error> {
97        if value >= 0 {
98            Self::try_from(value as usize)
99        } else {
100            Err(GeneralError::InvalidInput)
101        }
102    }
103}
104
105impl<const B: usize> Display for BoundedSize<B> {
106    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.0)
108    }
109}
110
111impl<const B: usize> From<BoundedSize<B>> for u8 {
112    fn from(value: BoundedSize<B>) -> Self {
113        value.0 as u8
114    }
115}
116
117impl<const B: usize> From<BoundedSize<B>> for u16 {
118    fn from(value: BoundedSize<B>) -> Self {
119        value.0 as u16
120    }
121}
122
123impl<const B: usize> From<BoundedSize<B>> for u32 {
124    fn from(value: BoundedSize<B>) -> Self {
125        value.0 as u32
126    }
127}
128
129impl<const B: usize> From<BoundedSize<B>> for u64 {
130    fn from(value: BoundedSize<B>) -> Self {
131        value.0 as u64
132    }
133}
134
135impl<const B: usize> From<BoundedSize<B>> for usize {
136    fn from(value: BoundedSize<B>) -> Self {
137        value.0
138    }
139}
140
141/// Wrapper for [`Vec`] that has an explicit upper bound on the number of elements.
142/// The Structure remains heap-allocated to avoid blowing up the size of types where it is used.
143#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
144pub struct BoundedVec<T, const N: usize>(Vec<T>);
145
146impl<T, const N: usize> Default for BoundedVec<T, N> {
147    fn default() -> Self {
148        Self(vec![])
149    }
150}
151
152impl<T, const N: usize> TryFrom<Vec<T>> for BoundedVec<T, N> {
153    type Error = GeneralError;
154
155    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
156        if value.len() <= N {
157            Ok(Self(value))
158        } else {
159            Err(GeneralError::InvalidInput)
160        }
161    }
162}
163
164impl<T, const N: usize> IntoIterator for BoundedVec<T, N> {
165    type IntoIter = std::vec::IntoIter<Self::Item>;
166    type Item = T;
167
168    fn into_iter(self) -> Self::IntoIter {
169        self.0.into_iter()
170    }
171}
172
173impl<T, const N: usize> FromIterator<T> for BoundedVec<T, N> {
174    fn from_iter<V: IntoIterator<Item = T>>(iter: V) -> Self {
175        Self(iter.into_iter().take(N).collect())
176    }
177}
178
179impl<T, const N: usize> From<[T; N]> for BoundedVec<T, N> {
180    fn from(value: [T; N]) -> Self {
181        Self(Vec::from(value))
182    }
183}
184
185impl<T, const N: usize> From<BoundedVec<T, N>> for Vec<T> {
186    fn from(value: BoundedVec<T, N>) -> Self {
187        value.0
188    }
189}
190
191impl<T, const N: usize> AsRef<[T]> for BoundedVec<T, N> {
192    fn as_ref(&self) -> &[T] {
193        &self.0
194    }
195}
196
197impl<T: Default + Copy, const N: usize> From<BoundedVec<T, N>> for [T; N] {
198    fn from(value: BoundedVec<T, N>) -> Self {
199        let mut out = [T::default(); N];
200        value.0.into_iter().enumerate().for_each(|(i, e)| out[i] = e);
201        out
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use crate::bounded::{BoundedSize, BoundedVec};
208
209    #[test]
210    fn bounded_size_should_not_allow_bigger_numbers() {
211        let min_bounded_size: usize = BoundedSize::<10>::MIN.into();
212        assert_eq!(0usize, min_bounded_size);
213        let max_bounded_size: usize = BoundedSize::<10>::MAX.into();
214        assert_eq!(10usize, max_bounded_size);
215
216        assert!(BoundedSize::<10>::try_from(5).is_ok_and(|b| u8::from(b) == 5));
217        assert!(BoundedSize::<10>::try_from(11).is_err());
218    }
219
220    #[test]
221    fn bounded_vec_should_not_fit_more_than_allowed() {
222        assert!(BoundedVec::<i32, 3>::try_from(vec![]).is_ok_and(|b| Vec::from(b).is_empty()));
223        assert!(BoundedVec::<i32, 3>::try_from(vec![1, 2]).is_ok_and(|b| Vec::from(b) == vec![1, 2]));
224        assert!(BoundedVec::<i32, 3>::try_from(vec![1, 2, 3]).is_ok_and(|b| Vec::from(b) == vec![1, 2, 3]));
225        assert!(BoundedVec::<i32, 3>::try_from(vec![1, 2, 3, 4]).is_err());
226
227        assert_eq!(
228            vec![1, 2, 3],
229            Vec::from(BoundedVec::<i32, 3>::from_iter(vec![1, 2, 3, 4]))
230        );
231    }
232}