use crate::*;
pub trait Step: Clone + PartialOrd + Sized {
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>);
fn forward_checked(start: Self, count: usize) -> Option<Self>;
fn backward_checked(start: Self, count: usize) -> Option<Self>;
fn forward(start: Self, count: usize) -> Self {
Step::forward_checked(start, count).expect(msg::NO_OVF)
}
fn backward(start: Self, count: usize) -> Self {
Step::backward_checked(start, count).expect(msg::NO_OVF)
}
}
macro_rules! impl_step_for_int {
($ty:ty as $mode:ident) => {
impl Step for $ty {
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
if *start > *end {
(0, Some(0))
} else {
let steps = usize::try_from(end.abs_diff(*start)).ok();
(steps.unwrap_or(usize::MAX), steps)
}
}
fn forward_checked(start: Self, count: usize) -> Option<Self> {
forward_checked!(start, count.try_into().ok()?, $mode)
}
fn backward_checked(start: Self, count: usize) -> Option<Self> {
backward_checked!(start, count.try_into().ok()?, $mode)
}
}
};
}
macro_rules! forward_checked {
($x:expr, $y:expr, signed) => {
$x.checked_add_unsigned($y)
};
($x:expr, $y:expr, unsigned) => {
$x.checked_add($y)
};
}
macro_rules! backward_checked {
($x:expr, $y:expr, signed) => {
$x.checked_sub_unsigned($y)
};
($x:expr, $y:expr, unsigned) => {
$x.checked_sub($y)
};
}
impl_step_for_int!(i8 as signed);
impl_step_for_int!(u8 as unsigned);
impl_step_for_int!(i16 as signed);
impl_step_for_int!(u16 as unsigned);
impl_step_for_int!(i32 as signed);
impl_step_for_int!(u32 as unsigned);
impl_step_for_int!(i64 as signed);
impl_step_for_int!(u64 as unsigned);
impl_step_for_int!(i128 as signed);
impl_step_for_int!(u128 as unsigned);
impl_step_for_int!(isize as signed);
impl_step_for_int!(usize as unsigned);