ambit

Attribute Macro ambit 

Source
#[ambit]
Expand description

Creates a ranged number newtype, along with various helpers.

#[ambit(range = "0..=10")]
pub struct ZeroToTen;

Generates:

#[repr(transparent)]
pub struct ZeroToTen(u8);
impl ZeroToTen {
    pub const MIN_VALUE: u8 = 0;
    pub const MAX_VALUE: u8 = 10;
    pub const MIN: Self = unsafe { Self::new_unchecked(0) };
    pub const MAX: Self = unsafe { Self::new_unchecked(10) };
    pub fn iter() -> ZeroToTenIter {
        ZeroToTenIter(Some(Self::MIN))
    }
    pub const fn new(n: u8) -> Result<Self, ZeroToTenError> {
        if n < 0 || n > 10 {
            return Err(ZeroToTenError(n));
        }
        Ok(unsafe { Self::new_unchecked(n) })
    }
    pub const unsafe fn new_unchecked(n: u8) -> Self {
        Self(n)
    }
    pub const fn value(&self) -> u8 {
        self.0
    }
    pub const fn is_min(&self) -> bool {
        self.value() == Self::MIN.value()
    }
    pub const fn is_max(&self) -> bool {
        self.value() == Self::MAX.value()
    }
    pub fn pred(&self) -> Option<Self> {
        (!self.is_min()).then(|| unsafe { Self::new_unchecked(self.value() - 1) })
    }
    pub fn succ(&self) -> Option<Self> {
        (!self.is_max()).then(|| unsafe { Self::new_unchecked(self.value() + 1) })
    }
}

impl PartialEq<u8> for ZeroToTen {
    fn eq(&self, &other: &u8) -> bool {
        self.value() == other
    }
}

impl TryFrom<u8> for ZeroToTen {
    type Error = ZeroToTenError;
    fn try_from(n: u8) -> Result<Self, ZeroToTenError> {
        Self::new(n)
    }
}

pub struct ZeroToTenIter(Option<ZeroToTen>);

impl Iterator for ZeroToTenIter {
    type Item = ZeroToTen;
    fn next(&mut self) -> Option<ZeroToTen> {
        if let Some(next) = self.0.as_ref()?.succ() {
            self.0.replace(next)
        } else {
            self.0.take()
        }
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub struct ZeroToTenError(pub u8);

impl std::error::Error for ZeroToTenError {}

impl std::fmt::Display for ZeroToTenError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_fmt(format_args!("invalid ZeroToTen: {0}", self.0))
    }
}

#[macro_export]
macro_rules! zerototen {
    (0) => { unsafe { Self::new_unchecked(0) } };
    (1) => { unsafe { Self::new_unchecked(1) } };
    (2) => { unsafe { Self::new_unchecked(2) } };
    (3) => { unsafe { Self::new_unchecked(3) } };
    (4) => { unsafe { Self::new_unchecked(4) } };
    (5) => { unsafe { Self::new_unchecked(5) } };
    (6) => { unsafe { Self::new_unchecked(6) } };
    (7) => { unsafe { Self::new_unchecked(7) } };
    (8) => { unsafe { Self::new_unchecked(8) } };
    (9) => { unsafe { Self::new_unchecked(9) } };
    (10) => { unsafe { Self::new_unchecked(10) } };
}

The provided range must be an ascending range of unsigned integers. Inclusive (..) and exclusive (..=) ranges are both supported. The internal type (u8, etc) of the newtype will be inferred from the range. If the range does not include zero, the internal type will be a NonZero.

The generated code is not otherwise configurable. I may expand this in the future, in particular adding a way to turn off various parts (the macro_rules, iterators).