// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 /// Generates a generic number operation 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: Sized { type Output; fn $method(self, rhs: Rhs) -> $output; } $( impl $name for $ty where $ty: UpcastFrom { type Output = Self; fn $method(self, rhs: Rhs) -> $output { $ty::$method(self, rhs.upcast()) } } )* #[test] fn $method() { $({ type Type = $ty; $test })* } }; } /// Generates a generic assign operation trait 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 { fn $method(&mut self, rhs: Rhs) -> $output; } $( impl $name for $ty where $ty: UpcastFrom { fn $method(&mut self, rhs: Rhs) -> $output { let f: fn(&mut $ty, Rhs) -> $output = $delegate; f(self, rhs) } } )* }; } number_trait!( CheckedAdd, checked_add, Option, assert!(Type::MAX.checked_add(Type::MAX).is_none()) ); number_trait!( CheckedSub, checked_sub, Option, assert!(Type::MIN.checked_sub(Type::MAX).is_none()) ); number_trait!( CheckedMul, checked_mul, Option, assert!(Type::MAX.checked_mul(Type::MAX).is_none()) ); number_trait!( CheckedDiv, checked_div, Option, 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); }, () ); /// Losslessly upcasts from one type to another pub trait UpcastFrom { fn upcast_from(value: T) -> Self; } /// Losslessly upcasts one type into another pub trait Upcast { fn upcast(self) -> T; } /// Implement Upcast automatically for all types that implement UpcastFrom impl Upcast for U where T: UpcastFrom, { fn upcast(self) -> T { UpcastFrom::upcast_from(self) } } macro_rules! upcast_impl { ($($ty:ident),*) => { upcast_impl!(@impl [], [$($ty),*]); }; (@impl [$($prev:ident),*], []) => { // done! }; (@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 UpcastFrom for usize where $unsigned: UpcastFrom, { fn upcast_from(value: Rhs) -> Self { $unsigned::upcast_from(value) as usize } } #[cfg(target_pointer_width = $target_width)] impl UpcastFrom for isize where $signed: UpcastFrom, { 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);