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