use num_traits::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedRem};
#[macro_export]
macro_rules! checked_ops {
($($rest:tt)+) => ($crate::cvt!([] [] $($rest)+));
}
#[doc(hidden)]
#[macro_export]
macro_rules! cvt {
([$($exp:expr),*] [$($op:tt)*] ($($inside:tt)+) $($rest:tt)*) =>
($crate::cvt!([$crate::cvt!([] [] $($inside)+) $(, $exp)*]
[$($op)*] $($rest)*));
([$a:expr $(, $exp:expr)*] [$($op:tt)*] as $type:tt $($rest:tt)*) =>
($crate::cvt!([$a.and_then(
|x| std::convert::TryInto::<$type>::try_into(x).ok()) $(, $exp)*]
[$($op)*] $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*] + $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*] + $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*] + $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*] - $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*] - $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*] - $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [+ $($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$crate::add($a, $b) $(, $exp)*] [$($op)*] + $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [- $($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$crate::sub($a, $b) $(, $exp)*] [$($op)*] + $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [+ $($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$crate::add($a, $b) $(, $exp)*] [$($op)*] - $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [- $($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$crate::sub($a, $b) $(, $exp)*] [$($op)*] - $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*] * $($rest:tt)*) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*] * $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*] * $($rest:tt)*) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*] * $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*] * $($rest:tt)*) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*] * $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*] / $($rest:tt)*) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*] / $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*] / $($rest:tt)*) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*] / $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*] / $($rest:tt)*) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*] / $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*] % $($rest:tt)*) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*] % $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*] % $($rest:tt)*) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*] % $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*] % $($rest:tt)*) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*] % $($rest)*));
([$($exp:expr),*] [$($op:tt)*] + $($rest:tt)*) =>
($crate::cvt!([$($exp),*] [+ $($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] - $($rest:tt)*) =>
($crate::cvt!([$($exp),*] [- $($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] * $($rest:tt)*) =>
($crate::cvt!([$($exp),*] [* $($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] / $($rest:tt)*) =>
($crate::cvt!([$($exp),*] [/ $($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] % $($rest:tt)*) =>
($crate::cvt!([$($exp),*] [% $($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] $operand:literal $($rest:tt)*) =>
($crate::cvt!([Some($operand) $(, $exp)*] [$($op)*] $($rest)*));
([$($exp:expr),*] [$($op:tt)*] $operand:ident $($rest:tt)*) =>
($crate::cvt!([Some($operand) $(, $exp)*] [$($op)*] $($rest)*));
([$b:expr, $a:expr $(, $exp:expr)*] [+ $($op:tt)*]) =>
($crate::cvt!([$crate::add($a, $b) $(, $exp)*] [$($op)*]));
([$b:expr, $a:expr $(, $exp:expr)*] [- $($op:tt)*]) =>
($crate::cvt!([$crate::sub($a, $b) $(, $exp)*] [$($op)*]));
([$b:expr, $a:expr $(, $exp:expr)*] [* $($op:tt)*]) =>
($crate::cvt!([$crate::mul($a, $b) $(, $exp)*] [$($op)*]));
([$b:expr, $a:expr $(, $exp:expr)*] [/ $($op:tt)*]) =>
($crate::cvt!([$crate::div($a, $b) $(, $exp)*] [$($op)*]));
([$b:expr, $a:expr $(, $exp:expr)*] [% $($op:tt)*]) =>
($crate::cvt!([$crate::rem($a, $b) $(, $exp)*] [$($op)*]));
([$exp:expr] []) =>
($exp);
}
#[doc(hidden)]
#[inline]
pub fn add<T>(a: Option<T>, b: Option<T>) -> Option<T> where T: CheckedAdd {
a?.checked_add(&b?)
}
#[doc(hidden)]
#[inline]
pub fn sub<T>(a: Option<T>, b: Option<T>) -> Option<T> where T: CheckedSub {
a?.checked_sub(&b?)
}
#[doc(hidden)]
#[inline]
pub fn mul<T>(a: Option<T>, b: Option<T>) -> Option<T> where T: CheckedMul {
a?.checked_mul(&b?)
}
#[doc(hidden)]
#[inline]
pub fn div<T>(a: Option<T>, b: Option<T>) -> Option<T> where T: CheckedDiv {
a?.checked_div(&b?)
}
#[doc(hidden)]
#[inline]
pub fn rem<T>(a: Option<T>, b: Option<T>) -> Option<T> where T: CheckedRem {
a?.checked_rem(&b?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
assert_eq!(checked_ops!(2 * 3 + 4 as i16 - (5 - 6)), Some(11));
}
#[test]
fn associativity() {
let m1 = -1i8;
assert_eq!(checked_ops!(1 + 127 + m1), None);
assert_eq!(checked_ops!(254u8 + 2 - 1), None);
assert_eq!(checked_ops!(7 - 5 + 3), Some(5));
assert_eq!(checked_ops!(7 - 5 - 3), Some(-1));
assert_eq!(checked_ops!(16 * 16 * 0u8), None);
assert_eq!(checked_ops!(7 * 5 / 3), Some(11));
assert_eq!(checked_ops!(7 * 5 % 3), Some(2));
assert_eq!(checked_ops!(7 / 5 * 3), Some(3));
assert_eq!(checked_ops!(7 / 5 / 3), Some(0));
assert_eq!(checked_ops!(7 / 5 % 3), Some(1));
assert_eq!(checked_ops!(7 % 5 * 3), Some(6));
assert_eq!(checked_ops!(7 % 5 / 3), Some(0));
assert_eq!(checked_ops!(7 % 5 % 3), Some(2));
assert_eq!(checked_ops!(1 as u16 as u8), Some(1u8));
}
#[test]
fn precedence() {
assert_eq!(checked_ops!(7 + 5 * 3), Some(22));
assert_eq!(checked_ops!(7 + 5 / 3), Some(8));
assert_eq!(checked_ops!(7 + 5 % 3), Some(9));
assert_eq!(checked_ops!(7 - 5 * 3), Some(-8));
assert_eq!(checked_ops!(7 - 5 / 3), Some(6));
assert_eq!(checked_ops!(7 - 5 % 3), Some(5));
let m1 = -1;
assert_eq!(checked_ops!(m1 + 128 as i8), None);
assert_eq!(checked_ops!(7 - 128 as i8), None);
assert_eq!(checked_ops!(0 * 128 as i8), None);
assert_eq!(checked_ops!(0 / 128 as i8), None);
}
#[test]
fn parentheses() {
assert_eq!(checked_ops!((2 + 3) * 4), Some(20));
assert_eq!(checked_ops!(2 * (3 + 4)), Some(14));
assert_eq!(checked_ops!((2 + 3) * (4 + 5)), Some(45));
}
#[test]
fn variables() {
let a = 1;
assert_eq!(checked_ops!(a + 2), Some(3));
}
#[test]
fn casts() {
let a = 3i32;
assert_eq!(checked_ops!(1 + 2 + a as u32), Some(6u32));
assert_eq!(checked_ops!(1 + a as u32 + 2), Some(6u32));
assert_eq!(checked_ops!(a as u32 + 1 + 2), Some(6u32));
assert_eq!(checked_ops!((1 + a) as u32 + 2), Some(6u32));
}
#[test]
fn overflow() {
assert_eq!(checked_ops!(254u8 + 1), Some(255));
assert_eq!(checked_ops!(255u8 + 1), None);
assert_eq!(checked_ops!(1u32 - 1), Some(0));
assert_eq!(checked_ops!(1u32 - 2), None);
assert_eq!(checked_ops!(15u8 * 17), Some(255));
assert_eq!(checked_ops!(16u8 * 16), None);
assert_eq!(checked_ops!((1i32 - 1) as u32), Some(0));
assert_eq!(checked_ops!((1i32 - 2) as u32), None);
assert_eq!(checked_ops!(255 as u8), Some(255));
assert_eq!(checked_ops!(256 as u8), None);
}
#[test]
fn div0() {
assert_eq!(checked_ops!(1 / 0), None);
assert_eq!(checked_ops!(1 % 0), None);
}
}