ambit 0.1.2

Attribute macro for ranged number newtypes.
Documentation
//! Attribute macro for ranged number newtypes.

/// Creates a ranged number newtype, along with various helpers.
///
/// ```rust
/// # use ambit_macros::ambit;
/// #[ambit(range = "0..=10")]
/// pub struct ZeroToTen;
/// ```
///
/// Generates:
///
/// ```rust
/// #[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).
pub use ambit_macros::ambit;