easy-int 0.1.8

A Rust crate with macros for easy implementation of integer aliases.
Documentation
#[macro_export]
macro_rules! implement_trait {
    ($type:ident, $inner:ty, $int:ty, $trait:ident, $method:ident) => {
        impl $trait<$int> for $type {
            type Output = $type;
            fn $method(self, other: $int) -> Self::Output {
                Self(self.0.$method(other as $inner))
            }
        }
        impl $trait<$type> for $int {
            type Output = $type;
            fn $method(self, other: $type) -> Self::Output {
                $type((self as $inner).$method(other.0))
            }
        }
        impl $trait for $type {
            type Output = $type;
            fn $method(self, other: $type) -> Self::Output {
                Self(self.0.$method(other.0))
            }
        }
    };
}

#[macro_export]
macro_rules! implement_eq_trait {
    ($type:ident, $inner:ty, $int:ty) => {
        impl PartialEq<$int> for $type {
            fn eq(&self, other: &$int) -> bool {
                self.0 == *other as $inner
            }
        }
        impl PartialEq<$type> for $int {
            fn eq(&self, other: &$type) -> bool {
                (*self as $inner) == other.0
            }
        }
    };
}

#[macro_export]
macro_rules! implement_ord_trait {
    ($type:ident, $inner:ty, $int:ty) => {
        impl PartialOrd<$int> for $type {
            fn partial_cmp(&self, other: &$int) -> Option<std::cmp::Ordering> {
                self.0.partial_cmp(&(*other as $inner))
            }
        }
        impl PartialOrd<$type> for $int {
            fn partial_cmp(&self, other: &$type) -> Option<std::cmp::Ordering> {
                (*self as $inner).partial_cmp(&other.0)
            }
        }
    };
}

#[macro_export]
macro_rules! implement_from {
    ($type:ident, $inner:ty, $($int:ty),*) => {
        $(impl From<$int> for $type {
            fn from(value: $int) -> Self {
                Self(value as $inner)
            }
        })*
    };
}

#[macro_export]
macro_rules! implement_special_methods {
    ($type:ident, $inner:ty) => {
        impl $type {
            pub fn saturating_add(self, rhs: impl Into<$type>) -> Self {
                let rhs: $inner = rhs.into().0;
                Self(self.0.saturating_add(rhs))
            }
            pub fn saturating_sub(self, rhs: impl Into<$type>) -> Self {
                let rhs: $inner = rhs.into().0;
                Self(self.0.saturating_sub(rhs))
            }
            pub fn wrapping_add(self, rhs: impl Into<$type>) -> Self {
                let rhs: $inner = rhs.into().0;
                Self(self.0.wrapping_add(rhs))
            }
            pub fn wrapping_sub(self, rhs: impl Into<$type>) -> Self {
                let rhs: $inner = rhs.into().0;
                Self(self.0.wrapping_sub(rhs))
            }
            pub fn checked_add(self, rhs: impl Into<$type>) -> Option<Self> {
                let rhs: $inner = rhs.into().0;
                self.0.checked_add(rhs).map(Self)
            }
            pub fn checked_sub(self, rhs: impl Into<$type>) -> Option<Self> {
                let rhs: $inner = rhs.into().0;
                self.0.checked_sub(rhs).map(Self)
            }
            pub fn overflowing_add(self, rhs: impl Into<$type>) -> (Self, bool) {
                let rhs: $inner = rhs.into().0;
                let (val, overflow) = self.0.overflowing_add(rhs);
                (Self(val), overflow)
            }
            pub fn overflowing_sub(self, rhs: impl Into<$type>) -> (Self, bool) {
                let rhs: $inner = rhs.into().0;
                let (val, overflow) = self.0.overflowing_sub(rhs);
                (Self(val), overflow)
            }
        }
    };
}

/// A macros for implementing integer types.
/// When you defining a wrapper over ineger type you can
/// Use this macros and all basic operations like conversions,
/// arithmetic, will be implemented for you:
/// ```
/// struct MyIntWrapper(u64);
/// implement_int!(MyIntWrapper, u64);
/// ```
#[macro_export]
macro_rules! implement_int {
    ($type:ident, $inner:ty) => {
        $crate::implement_from!($type, $inner, u8, u16, u32, u64, i8, i16, i32, i64, usize);

        impl From<$type> for $inner {
            fn from(value: $type) -> Self {
                value.0
            }
        }

        impl std::fmt::Display for $type {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}", self.0)
            }
        }

        $crate::implement_trait!($type, $inner, $inner, Add, add);
        $crate::implement_trait!($type, $inner, $inner, Sub, sub);
        $crate::implement_trait!($type, $inner, $inner, Mul, mul);
        $crate::implement_trait!($type, $inner, $inner, Div, div);
        $crate::implement_trait!($type, $inner, $inner, Rem, rem);
        $crate::implement_eq_trait!($type, $inner, $inner);
        $crate::implement_ord_trait!($type, $inner, $inner);

        $crate::implement_special_methods!($type, $inner);
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cmp::{PartialEq, PartialOrd};
    use std::convert::From;
    use std::ops::{Add, Div, Mul, Rem, Sub};

    #[derive(Debug, Clone, Copy)]
    struct TestIntU64(u64);
    #[derive(Debug, Clone, Copy)]
    struct TestIntU32(u32);

    #[derive(Debug, Clone, Copy)]
    struct TetIntI32(i32);

    implement_int!(TetIntI32, i32);
    implement_int!(TestIntU64, u64);
    implement_int!(TestIntU32, u32);

    #[test]
    fn test_basic_add() {
        let a = TestIntU64(3);
        let b = 4;
        let c = a + b;
        assert_eq!(c, 7);
    }

    #[test]
    #[should_panic]
    fn test_add_overflow() {
        let a = TestIntU32(u32::max_value());
        let b: u32 = 1;
        let _ = a + b;
    }

    #[test]
    fn test_conversion_from_different_type() {
        let _: TestIntU64 = 10u8.into();
    }

    #[test]
    fn all_side_add_works() {
        let a = TestIntU32(13);
        let b = 1u32;
        let _ = a + b;
        let _ = b + a;
        let a = TestIntU32(3);
        let b = TestIntU32(1);
        let _ = a + b;
    }

    #[test]
    fn all_side_cmp_works() {
        let a = TestIntU32(13);
        let b = 1u32;
        let _ = a < b;
        let _ = b < a;
        let _ = a > b;
        let _ = b > a;
        let _ = b == a;
        let _ = a == b;
        let _ = a >= b;
        let _ = b >= a;
        let _ = b != a;
        let _ = a != b;
    }

    #[test]
    fn print_works() {
        let a = TetIntI32(-3);
        let formatted = format!("{}", a);
        assert_eq!(formatted, "-3");
    }

    #[test]
    fn test_conversion() {
        let x: TestIntU32 = 10u32.into();
        let y: u32 = x.into();
        assert_eq!(y, 10);
    }

    #[test]
    fn test_saturating_sub() {
        let x = TestIntU32(5);
        assert_eq!(x.saturating_sub(10).0, 0);
    }

    #[test]
    fn test_wrapping_add() {
        let x = TestIntU32(u32::MAX);
        assert_eq!(x.wrapping_add(1).0, 0);
    }

    #[test]
    fn test_checked_add() {
        let x = TestIntU32(u32::MAX);
        assert!(x.checked_add(1).is_none());
    }

    #[test]
    fn test_overflowing_sub() {
        let x = TestIntU32(0);
        let (result, overflow) = x.overflowing_sub(1);
        assert_eq!(result.0, u32::MAX);
        assert!(overflow);
    }
}