sequin/
bounded.rs

1use derive_more::{Display, Error};
2
3use super::sequential::Sequential;
4
5macro_rules! impl_bounded_uint_types {
6    ($int_type:ty, $at_least:ident, $at_most:ident, $between:ident) => {
7        #[repr(transparent)]
8        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9        pub struct $between<const MIN: $int_type, const MAX: $int_type>($int_type);
10
11        pub type $at_least<const MIN: $int_type> = $between<MIN, { <$int_type>::MAX }>;
12        pub type $at_most<const MAX: $int_type> = $between<{ <$int_type>::MIN }, MAX>;
13
14        impl<const MIN: $int_type, const MAX: $int_type> $between<MIN, MAX> {
15            const MIN_INDEX: usize = MIN as _;
16
17            pub const fn new(value: $int_type) -> Option<Self> {
18                if MIN <= value && value <= MAX {
19                    Some(Self(value))
20                } else {
21                    None
22                }
23            }
24
25            pub const unsafe fn new_unchecked(value: $int_type) -> Self {
26                Self(value)
27            }
28
29            pub const fn get(&self) -> $int_type {
30                self.0
31            }
32        }
33
34        unsafe impl<const MIN: $int_type, const MAX: $int_type> Sequential for $between<MIN, MAX>
35        where
36            $int_type: Sequential,
37        {
38            const CARDINALITY: usize = (MAX.saturating_sub(MIN) as usize) + 1;
39
40            fn to_index(&self) -> usize {
41                let full_index = self.0.to_index();
42                full_index - Self::MIN_INDEX
43            }
44
45            unsafe fn from_index_unchecked(index: usize) -> Self {
46                let full_index = index + Self::MIN_INDEX;
47                let value = unsafe { <$int_type as Sequential>::from_index_unchecked(full_index) };
48                unsafe { Self::new_unchecked(value) }
49            }
50        }
51
52        impl<const MIN: $int_type, const MAX: $int_type> From<$between<MIN, MAX>> for $int_type {
53            fn from(value: $between<MIN, MAX>) -> Self {
54                value.get()
55            }
56        }
57
58        impl<const MIN: $int_type, const MAX: $int_type> TryFrom<$int_type> for $between<MIN, MAX> {
59            type Error = TryFromUnboundedError;
60
61            fn try_from(value: $int_type) -> Result<Self, Self::Error> {
62                Self::new(value).ok_or(TryFromUnboundedError(()))
63            }
64        }
65    };
66}
67
68impl_bounded_uint_types!(u8, AtLeastU8, AtMostU8, BetweenU8);
69impl_bounded_uint_types!(u16, AtLeastU16, AtMostU16, BetweenU16);
70impl_bounded_uint_types!(u32, AtLeastU32, AtMostU32, BetweenU32);
71
72#[derive(Debug, Clone, Copy, Display, Error)]
73#[display("TODO")]
74pub struct TryFromUnboundedError(#[error(not(source))] ());