safe-num 0.0.1

Safe number arithmetics with ergonomics
Documentation
use std::ops::Add;

#[derive(Clone, Copy)]
pub enum Operation {
    Add,
    Sub,
    Mul,
    Div,
    Sqrt
}

#[derive(Clone, Copy)]
pub struct ArithmeticError<T> {
    pub left: T,
    pub right: T,
    pub op: Operation,
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Safe<T>(T);

#[derive(Clone, Copy)]
pub struct SafeResult<T>(Result<Safe<T>, ArithmeticError<T>>);

/// Marker trait to identify a Safe type
pub trait IsSafe {}
impl<T> IsSafe for Safe<T> {}

/// Trait with all operations over a safe number
pub trait SafeNum<T>: IsSafe + Add<T> + Add<Safe<T>> + Add<SafeResult<T>> {}
impl<T> SafeNum<T> for T
    where T: IsSafe
        + Add<T>
        + Add<Safe<T>>
        + Add<SafeResult<T>>
{}

impl Add<u64> for Safe<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: u64) -> Self::Output {
        match self.0.checked_add(other) {
            Some(r) => SafeResult(Ok(Safe(r))),
            None => SafeResult(Err(
                ArithmeticError {
                    left: self.0,
                    right: other,
                    op: Operation::Add,
                }
            ))
        }
    }
}

impl Add for Safe<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: Safe<u64>) -> Self::Output {
        self + other.0
    }
}

impl Add<SafeResult<u64>> for Safe<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: SafeResult<u64>) -> Self::Output {
        match other.0 {
            Ok(other) => self + other,
            Err(e) => SafeResult(Err(e))
        }
    }
}

impl Add<u64> for SafeResult<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: u64) -> Self::Output {
        match self.0 {
            Ok(this) => this + other,
            Err(e) => SafeResult(Err(e))
        }
    }
}

impl Add<Safe<u64>> for SafeResult<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: Safe<u64>) -> Self::Output {
        match self.0 {
            Ok(this) => this + other,
            Err(e) => SafeResult(Err(e))
        }
    }
}

impl Add for SafeResult<u64> {
    type Output = SafeResult<u64>;

    fn add(self, other: SafeResult<u64>) -> Self::Output {
        match self.0 {
            Ok(this) => this + other,
            Err(e) => SafeResult(Err(e))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn vector_length(x: Safe<u64>, y: Safe<u64>) -> SafeResult<u64> {
        (x + y) + (x + y)
    }

    #[test]
    fn test_vector_length() {
        let r = vector_length(Safe(4u64), Safe(5u64));
    }
}