hopr_primitive_types/
bounded.rs

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