ubyte 0.10.0

A simple, complete, const-everything, saturating, human-friendly, no_std library for byte units.
Documentation
use core::cmp::Ordering;
use core::ops::{Mul, Add, Shl, Sub, Div, Shr, Rem};

use crate::ByteUnit;

impl<T: Into<ByteUnit>> Mul<T> for ByteUnit {
    type Output = Self;

    #[inline(always)]
    fn mul(self, rhs: T) -> Self::Output {
        ByteUnit(self.0.saturating_mul(rhs.into().0))
    }
}

impl<T: Into<ByteUnit>> Div<T> for ByteUnit {
    type Output = Self;

    #[inline(always)]
    fn div(self, rhs: T) -> Self::Output {
        let value = rhs.into().0;
        match value {
            0 => ByteUnit::max_value(),
            _ => ByteUnit(self.0 / value)
        }
    }
}

impl<T: Into<ByteUnit>> Rem<T> for ByteUnit {
    type Output = Self;

    #[inline(always)]
    fn rem(self, rhs: T) -> Self::Output {
        let value = rhs.into().0;
        match value {
            0 => ByteUnit(0),
            _ => ByteUnit(self.0 % value)
        }
    }
}

impl<T: Into<ByteUnit>> Add<T> for ByteUnit {
    type Output = Self;

    #[inline(always)]
    fn add(self, rhs: T) -> Self::Output {
        ByteUnit(self.0.saturating_add(rhs.into().0))
    }
}

impl<T: Into<ByteUnit>> Sub<T> for ByteUnit {
    type Output = Self;

    #[inline(always)]
    fn sub(self, rhs: T) -> Self::Output {
        ByteUnit(self.0.saturating_sub(rhs.into().0))
    }
}

impl<T: Into<ByteUnit>> Shl<T> for ByteUnit {
    type Output = Self;
    fn shl(self, rhs: T) -> Self::Output {
        let wanted = rhs.into().0;
        let available = self.0.leading_zeros() as u64;
        if wanted > available {
            ByteUnit::max_value()
        } else {
            ByteUnit(self.0 << wanted)
        }
    }
}

impl<T: Into<ByteUnit>> Shr<T> for ByteUnit {
    type Output = Self;
    fn shr(self, rhs: T) -> Self::Output {
        ByteUnit(self.0 >> rhs.into().0)
    }
}

impl<T: Into<ByteUnit> + Copy> PartialEq<T> for ByteUnit {
    fn eq(&self, other: &T) -> bool {
        self.0 == (*other).into().0
    }
}

impl<T: Into<ByteUnit> + Copy> PartialOrd<T> for ByteUnit {
    fn partial_cmp(&self, other: &T) -> Option<Ordering> {
        self.0.partial_cmp(&(*other).into().0)
    }
}

macro_rules! impl_arith_ops_on_core {
    ($T:ty) => (
        impl Mul<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn mul(self, rhs: ByteUnit) -> Self::Output {
                rhs * self
            }
        }

        impl Div<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn div(self, rhs: ByteUnit) -> Self::Output {
                rhs / self
            }
        }

        impl Rem<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn rem(self, rhs: ByteUnit) -> Self::Output {
                rhs % self
            }
        }

        impl Add<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn add(self, rhs: ByteUnit) -> Self::Output {
                rhs + self
            }
        }

        impl Sub<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn sub(self, rhs: ByteUnit) -> Self::Output {
                rhs - self
            }
        }

        impl Shr<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn shr(self, rhs: ByteUnit) -> Self::Output {
                rhs >> self
            }
        }

        impl Shl<ByteUnit> for $T {
            type Output = ByteUnit;

            #[inline(always)]
            fn shl(self, rhs: ByteUnit) -> Self::Output {
                rhs << self
            }
        }

        impl PartialEq<ByteUnit> for $T {
            #[inline(always)]
            fn eq(&self, other: &ByteUnit) -> bool {
                other == self
            }
        }

        impl PartialOrd<ByteUnit> for $T {
            #[inline(always)]
            fn partial_cmp(&self, other: &ByteUnit) -> Option<Ordering> {
                other.partial_cmp(self)
            }
        }
    )
}

impl_arith_ops_on_core!(usize);
impl_arith_ops_on_core!(u8);
impl_arith_ops_on_core!(u16);
impl_arith_ops_on_core!(u32);
impl_arith_ops_on_core!(u64);
impl_arith_ops_on_core!(u128);

impl_arith_ops_on_core!(isize);
impl_arith_ops_on_core!(i8);
impl_arith_ops_on_core!(i16);
impl_arith_ops_on_core!(i32);
impl_arith_ops_on_core!(i64);
impl_arith_ops_on_core!(i128);

#[cfg(test)]
mod tests {
    use crate::ByteUnit;

    #[test]
    fn test_saturation() {
        assert_eq!(ByteUnit::B * -1, 0);
        assert_eq!(ByteUnit::B * -3, 0);
        assert_eq!(ByteUnit::B / -3, ByteUnit::max_value());
        assert_eq!(ByteUnit::B - 100, 0);
        assert_eq!((100 * ByteUnit::B) % -10, 0);

        // These are suprising, but ~correct. Should we document?
        assert_eq!(ByteUnit::B + (-100i32), 1);
        assert_eq!(-100 + ByteUnit::B, 1);
    }
}