burbomath 0.0.1

Burbokop's rust math library
Documentation
use super::{Floor, IsPositive, NonNeg, Pi, Sqrt};
use core::ops::{DivAssign, MulAssign, SubAssign};
use core::{
    error::Error,
    fmt::{Debug, Display},
    ops::{Add, AddAssign, Div, Mul, Sub},
};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Cannot store negative numbers
///
/// Can be constructed by macro that checks at compile time that it is indeed positive
/// ```
/// use burbomath::positive;
/// positive!(1_i32);
/// ```
/// ```compile_fail
/// use burbomath::positive;
/// burbomath::positive!(0_i32);
/// ```
#[derive(Clone, Copy, Debug, Ord, Eq)]
pub struct Positive<T> {
    pub(super) value: T,
}

#[cfg(feature = "serde")]
impl<T: Serialize> Serialize for Positive<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.value.serialize(serializer)
    }
}

#[cfg(feature = "serde")]
impl<'de, T: Deserialize<'de> + IsPositive + Debug> Deserialize<'de> for Positive<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = T::deserialize(deserializer)?;
        if value.is_positive() {
            Ok(Self { value })
        } else {
            #[cfg(feature = "std")]
            let err = Err(serde::de::Error::custom(&std::format!(
                "Can not deserialize {:?} as Positive because it is not a positive number.",
                value
            )));
            #[cfg(not(feature = "std"))]
            let err = Err(serde::de::Error::custom("Not a positive number."));
            err
        }
    }
}

impl<T: Display> Display for Positive<T> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.value.fmt(f)
    }
}

#[derive(Debug)]
pub struct NotPositiveError<T> {
    original_value: T,
}

impl<T> NotPositiveError<T> {
    pub fn original_value(self) -> T {
        self.original_value
    }
}

impl<T> Display for NotPositiveError<T> {
    fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        todo!()
    }
}

impl<T> Error for NotPositiveError<T> where T: Debug {}

impl<T> Positive<T> {
    pub fn new(value: T) -> Result<Self, NotPositiveError<T>>
    where
        T: IsPositive,
    {
        if value.is_positive() {
            Ok(Self { value })
        } else {
            Err(NotPositiveError {
                original_value: value,
            })
        }
    }

    pub const unsafe fn new_const_unchecked(value: T) -> Self {
        Self { value }
    }

    pub fn into_inner(self) -> T {
        self.value
    }

    #[deprecated(note = "Better use NonNeg::into_inner")]
    pub fn unwrap(self) -> T {
        self.value
    }

    pub fn sqrt(self) -> Positive<<T as Sqrt>::Output>
    where
        T: Sqrt,
    {
        Positive {
            value: self.value.sqrt(),
        }
    }

    pub fn floor(self) -> Positive<<T as Floor>::Output>
    where
        T: Floor,
    {
        Positive {
            value: self.value.floor(),
        }
    }

    pub fn sub_assign(&mut self, rhs: Self) -> Result<(), Self>
    where
        T: SubAssign + PartialOrd,
    {
        if self.value > rhs.value {
            Ok(self.value -= rhs.value)
        } else {
            Err(rhs)
        }
    }
}

impl<T, U> PartialEq<Positive<U>> for Positive<T>
where
    T: PartialEq<U>,
{
    fn eq(&self, other: &Positive<U>) -> bool {
        self.value.eq(&other.value)
    }
}

impl<T, U> PartialEq<NonNeg<U>> for Positive<T>
where
    T: PartialEq<U>,
{
    fn eq(&self, other: &NonNeg<U>) -> bool {
        self.value.eq(&other.value)
    }
}

impl<T, U> PartialOrd<Positive<U>> for Positive<T>
where
    T: PartialOrd<U>,
{
    fn partial_cmp(&self, other: &Positive<U>) -> Option<core::cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl<T, U> PartialOrd<NonNeg<U>> for Positive<T>
where
    T: PartialOrd<U>,
{
    fn partial_cmp(&self, other: &NonNeg<U>) -> Option<core::cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl<T, U> Add<Positive<U>> for Positive<T>
where
    T: Add<U>,
{
    type Output = Positive<<T as Add<U>>::Output>;

    fn add(self, rhs: Positive<U>) -> Self::Output {
        Self::Output {
            value: self.value + rhs.value,
        }
    }
}

impl<T, U> Add<NonNeg<U>> for Positive<T>
where
    T: Add<U>,
{
    type Output = Positive<<T as Add<U>>::Output>;

    fn add(self, rhs: NonNeg<U>) -> Self::Output {
        Self::Output {
            value: self.value + rhs.value,
        }
    }
}

impl<T, U> AddAssign<Positive<U>> for Positive<T>
where
    T: AddAssign<U>,
{
    fn add_assign(&mut self, rhs: Positive<U>) {
        self.value += rhs.value
    }
}

impl<T, U> AddAssign<NonNeg<U>> for Positive<T>
where
    T: AddAssign<U>,
{
    fn add_assign(&mut self, rhs: NonNeg<U>) {
        self.value += rhs.value
    }
}

impl<T, U> Sub<Positive<U>> for Positive<T>
where
    T: Sub<U>,
{
    type Output = <T as Sub<U>>::Output;

    fn sub(self, rhs: Positive<U>) -> Self::Output {
        self.value - rhs.value
    }
}

impl<T, U> Sub<NonNeg<U>> for Positive<T>
where
    T: Sub<U>,
{
    type Output = <T as Sub<U>>::Output;

    fn sub(self, rhs: NonNeg<U>) -> Self::Output {
        self.value - rhs.value
    }
}

impl<T, U> Mul<Positive<U>> for Positive<T>
where
    T: Mul<U>,
{
    type Output = Positive<<T as Mul<U>>::Output>;

    fn mul(self, rhs: Positive<U>) -> Self::Output {
        Self::Output {
            value: self.value * rhs.value,
        }
    }
}

impl<T, U> MulAssign<Positive<U>> for Positive<T>
where
    T: MulAssign<U>,
{
    fn mul_assign(&mut self, rhs: Positive<U>) {
        self.value *= rhs.value;
    }
}

impl<T, U> Div<Positive<U>> for Positive<T>
where
    T: Div<U>,
{
    type Output = Positive<<T as Div<U>>::Output>;

    fn div(self, rhs: Positive<U>) -> Self::Output {
        Self::Output {
            value: self.value / rhs.value,
        }
    }
}

impl<T, U> DivAssign<Positive<U>> for Positive<T>
where
    T: DivAssign<U>,
{
    fn div_assign(&mut self, rhs: Positive<U>) {
        self.value /= rhs.value;
    }
}

impl<T> Pi for Positive<T>
where
    T: Pi,
{
    fn pi() -> Self {
        Self { value: T::pi() }
    }
}

impl From<u32> for Positive<i64> {
    fn from(value: u32) -> Self {
        Self {
            value: value as i64,
        }
    }
}

impl<T: IsPositive> TryFrom<NonNeg<T>> for Positive<T> {
    type Error = NotPositiveError<T>;

    fn try_from(value: NonNeg<T>) -> Result<Self, Self::Error> {
        Self::new(value.value)
    }
}

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

    #[test]
    fn eq() {
        assert!(Positive { value: 1_u32 }.eq(&Positive { value: 1_u32 }));
        assert!(!Positive { value: 1_u32 }.eq(&Positive { value: 2_u32 }));
    }
}