rating 0.1.0

A library that implements multiple rating systems and utilities for multiplayer games
Documentation
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::{Decimal, RoundingStrategy};

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Rating(Decimal);

impl Rating {
    /// Rating rounded to the units
    ///
    /// Useful when we don't want to display `Rating` with decimal places
    pub fn round_to_i32(&self) -> i32 {
        self.round_dp_with_strategy(0, RoundingStrategy::MidpointAwayFromZero)
            .to_i32()
            .unwrap_or(0)
    }

    pub fn rounded(&self, decimal_places: u32) -> f64 {
        self.round_dp_with_strategy(decimal_places, RoundingStrategy::MidpointAwayFromZero)
            .to_f64()
            .unwrap_or(0.0)
    }

    /// Rating rounded to the first decimal digit
    ///
    /// Typically, most rating systems round to one decimal digit
    pub fn value(&self) -> f64 {
        self.rounded(1)
    }
}

impl From<Decimal> for Rating {
    fn from(value: Decimal) -> Self {
        Rating(value)
    }
}

impl From<i32> for Rating {
    fn from(value: i32) -> Self {
        Rating::from(Decimal::from(value))
    }
}

impl From<i64> for Rating {
    fn from(value: i64) -> Self {
        Rating::from(Decimal::from(value))
    }
}

impl std::ops::Deref for Rating {
    type Target = Decimal;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::Add<Decimal> for Rating {
    type Output = Rating;

    fn add(self, rhs: Decimal) -> Self::Output {
        Rating::from(*self + rhs)
    }
}

impl std::ops::Sub<Decimal> for Rating {
    type Output = Rating;

    fn sub(self, rhs: Decimal) -> Self::Output {
        Rating::from(*self - rhs)
    }
}

impl std::fmt::Display for Rating {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.value())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rust_decimal::prelude::FromPrimitive;
    use test_case::test_case;

    #[test_case(1.23 => 1.2)]
    #[test_case(1.25 => 1.3)]
    #[test_case(1.0005 => 1.0)]
    #[test_case(1.89 => 1.9)]
    #[test_case(1.849 => 1.8)]
    fn rounds_to_1_decimal_place_by_default(value: f64) -> f64 {
        Rating::from(Decimal::from_f64(value).unwrap()).value()
    }
}