1use crate::result::{Error, Result};
2
3macro_rules! range {
4 ($range:ident, $range_ty:ident) => {
5 ::paste::paste! {
6 #[doc = "Represents ranged type over a bounded range of values."]
7 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
8 pub struct $range<const MIN: $range_ty, const MAX: $range_ty>($range_ty);
9
10 impl<const MIN: $range_ty, const MAX: $range_ty> $range<MIN, MAX> {
11 #[doc = "Creates a new [" $range "]."]
12 pub const fn new() -> Self {
13 Self(MIN)
14 }
15
16 pub const fn min() -> $range_ty {
18 MIN
19 }
20
21 pub const fn max() -> $range_ty {
23 MAX
24 }
25
26 #[doc = "Attempts to convert a raw index value into a [" $range "]."]
27 pub const fn try_from_inner(val: $range_ty) -> Result<Self> {
28 if val >= MIN && val <= MAX {
29 Ok(Self(val))
30 } else {
31 Err(Error::invalid_field_value("range", val as usize, MIN as usize, MAX as usize))
32 }
33 }
34
35 #[doc = "Converts a raw index value into a [" $range "] with no bounds check."]
36 #[doc = ""]
37 #[doc = "# Panics"]
38 #[doc = ""]
39 #[doc = "Panics if the provided index is out-of-bounds."]
40 #[doc = ""]
41 #[doc = "Caller must ensure that the index is in the valid range."]
42 pub const fn try_from_unchecked(val: $range_ty) -> Self {
43 match Self::try_from_inner(val) {
44 Ok(i) => i,
45 Err(_) => panic!("ranged value out-of-bounds"),
46 }
47 }
48
49 #[doc = "Converts a [" $range "] into a raw index value."]
50 pub const fn into_inner(self) -> $range_ty {
51 self.0
52 }
53 }
54
55 impl<const MIN: $range_ty, const MAX: $range_ty> Default for $range<MIN, MAX> {
56 fn default() -> Self {
57 Self::new()
58 }
59 }
60
61 impl<const MIN: $range_ty, const MAX: $range_ty> TryFrom<$range_ty> for $range<MIN, MAX> {
62 type Error = Error;
63
64 fn try_from(val: $range_ty) -> Result<Self> {
65 Self::try_from_inner(val)
66 }
67 }
68
69 impl<const MIN: $range_ty, const MAX: $range_ty> From<$range<MIN, MAX>> for $range_ty {
70 fn from(val: $range<MIN, MAX>) -> Self {
71 val.into_inner()
72 }
73 }
74
75 #[cfg(test)]
76 mod [<range_ $range_ty _tests>] {
77 use super::*;
78
79 #[test]
80 fn test_range() {
81 const MIN: $range_ty = 1;
82 const MAX: $range_ty = $range_ty::MAX - 1;
83
84 type TestRange = $range<MIN, MAX>;
85
86 assert_eq!(TestRange::new().into_inner(), MIN);
87 assert_eq!(TestRange::default().into_inner(), MIN);
88
89 [MIN, MIN + 1, MAX - 1, MAX].into_iter().for_each(|index| {
90 let exp_index = TestRange::try_from(index).unwrap();
91
92 assert_eq!(TestRange::try_from_inner(index), Ok(exp_index));
93 assert_eq!(TestRange::try_from(index), Ok(exp_index));
94
95 assert_eq!(exp_index.into_inner(), index);
96 assert_eq!($range_ty::from(exp_index), index);
97 });
98
99 assert!(TestRange::try_from_inner(MIN - 1).is_err());
100 assert!(TestRange::try_from_inner(MAX + 1).is_err());
101 }
102 }
103 }
104 };
105}
106
107range!(RangeU8, u8);
108range!(RangeU16, u16);
109range!(RangeU32, u32);
110range!(RangeU64, u64);
111range!(RangeUsize, usize);
112
113pub type RangedIndex<const MIN: usize, const MAX: usize> = RangeUsize<MIN, MAX>;
115
116range!(RangeI8, i8);
117range!(RangeI16, i16);
118range!(RangeI32, i32);
119range!(RangeI64, i64);
120range!(RangeIsize, isize);