macro_rules! number_trait {
($name:ident, $method:ident, $output:ty, $test:stmt) => {
number_trait!($name, $method, $output, $test, [
u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, u128, i128
]);
};
($name:ident, $method:ident, $output:ty, $test:stmt, [$($ty:ident),*]) => {
pub trait $name<Rhs = Self>: Sized {
type Output;
fn $method(self, rhs: Rhs) -> $output;
}
$(
impl<Rhs> $name<Rhs> for $ty
where $ty: UpcastFrom<Rhs>
{
type Output = Self;
fn $method(self, rhs: Rhs) -> $output {
$ty::$method(self, rhs.upcast())
}
}
)*
#[test]
fn $method() {
$({
type Type = $ty;
$test
})*
}
};
}
macro_rules! assign_trait {
($name:ident, $method:ident, $delegate:expr, $output:ty) => {
assign_trait!($name, $method, $delegate, $output, [
u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, u128, i128
]);
};
($name:ident, $method:ident, $delegate:expr, $output:ty, [$($ty:ident),*]) => {
pub trait $name<Rhs = Self> {
fn $method(&mut self, rhs: Rhs) -> $output;
}
$(
impl<Rhs> $name<Rhs> for $ty
where $ty: UpcastFrom<Rhs>
{
fn $method(&mut self, rhs: Rhs) -> $output {
let f: fn(&mut $ty, Rhs) -> $output = $delegate;
f(self, rhs)
}
}
)*
};
}
number_trait!(
CheckedAdd,
checked_add,
Option<Self::Output>,
assert!(Type::MAX.checked_add(Type::MAX).is_none())
);
number_trait!(
CheckedSub,
checked_sub,
Option<Self::Output>,
assert!(Type::MIN.checked_sub(Type::MAX).is_none())
);
number_trait!(
CheckedMul,
checked_mul,
Option<Self::Output>,
assert!(Type::MAX.checked_mul(Type::MAX).is_none())
);
number_trait!(
CheckedDiv,
checked_div,
Option<Self::Output>,
assert!(Type::MAX.checked_div(0).is_none())
);
assign_trait!(
CheckedAddAssign,
checked_add_assign,
|value, rhs| {
*value = CheckedAdd::checked_add(*value, rhs)?;
Some(())
},
Option<()>
);
assign_trait!(
CheckedSubAssign,
checked_sub_assign,
|value, rhs| {
*value = CheckedSub::checked_sub(*value, rhs)?;
Some(())
},
Option<()>
);
assign_trait!(
CheckedMulAssign,
checked_mul_assign,
|value, rhs| {
*value = CheckedMul::checked_mul(*value, rhs)?;
Some(())
},
Option<()>
);
number_trait!(
SaturatingAdd,
saturating_add,
Self::Output,
assert_eq!(Type::MAX.saturating_add(Type::MAX), Type::MAX)
);
number_trait!(
SaturatingSub,
saturating_sub,
Self::Output,
assert_eq!(Type::MIN.saturating_sub(Type::MAX), Type::MIN)
);
number_trait!(
SaturatingMul,
saturating_mul,
Self::Output,
assert_eq!(Type::MAX.saturating_mul(Type::MAX), Type::MAX)
);
assign_trait!(
SaturatingAddAssign,
saturating_add_assign,
|value, rhs| {
*value = SaturatingAdd::saturating_add(*value, rhs);
},
()
);
assign_trait!(
SaturatingSubAssign,
saturating_sub_assign,
|value, rhs| {
*value = SaturatingSub::saturating_sub(*value, rhs);
},
()
);
assign_trait!(
SaturatingMulAssign,
saturating_mul_assign,
|value, rhs| {
*value = SaturatingMul::saturating_mul(*value, rhs);
},
()
);
pub trait UpcastFrom<T> {
fn upcast_from(value: T) -> Self;
}
pub trait Upcast<T> {
fn upcast(self) -> T;
}
impl<T, U> Upcast<T> for U
where
T: UpcastFrom<U>,
{
fn upcast(self) -> T {
UpcastFrom::upcast_from(self)
}
}
macro_rules! upcast_impl {
($($ty:ident),*) => {
upcast_impl!(@impl [], [$($ty),*]);
};
(@impl [$($prev:ident),*], []) => {
};
(@impl [$($prev:ident),*], [$current:ident $(, $rest:ident)*]) => {
impl UpcastFrom<$current> for $current {
fn upcast_from(value: Self) -> Self {
value
}
}
impl UpcastFrom<&$current> for $current {
fn upcast_from(value: &Self) -> Self {
*value
}
}
$(
impl UpcastFrom<$prev> for $current {
fn upcast_from(value: $prev) -> Self {
value as $current
}
}
impl UpcastFrom<&$prev> for $current {
fn upcast_from(value: &$prev) -> Self {
(*value) as $current
}
}
)*
upcast_impl!(@impl [$current $(, $prev)*], [$($rest),*]);
};
}
upcast_impl!(u8, u16, u32, u64, u128);
upcast_impl!(i8, i16, i32, i64, i128);
macro_rules! upcast_usize {
($target_width:literal, $unsigned:ident, $signed:ident) => {
#[cfg(target_pointer_width = $target_width)]
impl<Rhs> UpcastFrom<Rhs> for usize
where
$unsigned: UpcastFrom<Rhs>,
{
fn upcast_from(value: Rhs) -> Self {
$unsigned::upcast_from(value) as usize
}
}
#[cfg(target_pointer_width = $target_width)]
impl<Rhs> UpcastFrom<Rhs> for isize
where
$signed: UpcastFrom<Rhs>,
{
fn upcast_from(value: Rhs) -> Self {
$signed::upcast_from(value) as isize
}
}
};
}
upcast_usize!("8", u8, i8);
upcast_usize!("16", u16, u16);
upcast_usize!("32", u32, i32);
upcast_usize!("64", u64, i64);
upcast_usize!("128", u128, i128);