#[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)
}
}
};
}
#[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);
}
}