use crate::result::{Error, Result};
macro_rules! range {
($range:ident, $range_ty:ident) => {
::paste::paste! {
#[doc = "Represents ranged type over a bounded range of values."]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct $range<const MIN: $range_ty, const MAX: $range_ty>($range_ty);
impl<const MIN: $range_ty, const MAX: $range_ty> $range<MIN, MAX> {
#[doc = "Creates a new [" $range "]."]
pub const fn new() -> Self {
Self(MIN)
}
pub const fn min() -> $range_ty {
MIN
}
pub const fn max() -> $range_ty {
MAX
}
#[doc = "Attempts to convert a raw index value into a [" $range "]."]
pub const fn try_from_inner(val: $range_ty) -> Result<Self> {
if val >= MIN && val <= MAX {
Ok(Self(val))
} else {
Err(Error::invalid_field_value("range", val as usize, MIN as usize, MAX as usize))
}
}
#[doc = "Converts a raw index value into a [" $range "] with no bounds check."]
#[doc = ""]
#[doc = "# Panics"]
#[doc = ""]
#[doc = "Panics if the provided index is out-of-bounds."]
#[doc = ""]
#[doc = "Caller must ensure that the index is in the valid range."]
pub const fn try_from_unchecked(val: $range_ty) -> Self {
match Self::try_from_inner(val) {
Ok(i) => i,
Err(_) => panic!("ranged value out-of-bounds"),
}
}
#[doc = "Converts a [" $range "] into a raw index value."]
pub const fn into_inner(self) -> $range_ty {
self.0
}
}
impl<const MIN: $range_ty, const MAX: $range_ty> Default for $range<MIN, MAX> {
fn default() -> Self {
Self::new()
}
}
impl<const MIN: $range_ty, const MAX: $range_ty> TryFrom<$range_ty> for $range<MIN, MAX> {
type Error = Error;
fn try_from(val: $range_ty) -> Result<Self> {
Self::try_from_inner(val)
}
}
impl<const MIN: $range_ty, const MAX: $range_ty> From<$range<MIN, MAX>> for $range_ty {
fn from(val: $range<MIN, MAX>) -> Self {
val.into_inner()
}
}
#[cfg(test)]
mod [<range_ $range_ty _tests>] {
use super::*;
#[test]
fn test_range() {
const MIN: $range_ty = 1;
const MAX: $range_ty = $range_ty::MAX - 1;
type TestRange = $range<MIN, MAX>;
assert_eq!(TestRange::new().into_inner(), MIN);
assert_eq!(TestRange::default().into_inner(), MIN);
[MIN, MIN + 1, MAX - 1, MAX].into_iter().for_each(|index| {
let exp_index = TestRange::try_from(index).unwrap();
assert_eq!(TestRange::try_from_inner(index), Ok(exp_index));
assert_eq!(TestRange::try_from(index), Ok(exp_index));
assert_eq!(exp_index.into_inner(), index);
assert_eq!($range_ty::from(exp_index), index);
});
assert!(TestRange::try_from_inner(MIN - 1).is_err());
assert!(TestRange::try_from_inner(MAX + 1).is_err());
}
}
}
};
}
range!(RangeU8, u8);
range!(RangeU16, u16);
range!(RangeU32, u32);
range!(RangeU64, u64);
range!(RangeUsize, usize);
pub type RangedIndex<const MIN: usize, const MAX: usize> = RangeUsize<MIN, MAX>;
range!(RangeI8, i8);
range!(RangeI16, i16);
range!(RangeI32, i32);
range!(RangeI64, i64);
range!(RangeIsize, isize);