1use rust_decimal::prelude::ToPrimitive;
2use rust_decimal::{Decimal, RoundingStrategy};
3
4#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
5pub struct Rating(Decimal);
6
7impl Rating {
8 pub fn round_to_i32(&self) -> i32 {
12 self.round_dp_with_strategy(0, RoundingStrategy::MidpointAwayFromZero)
13 .to_i32()
14 .unwrap_or(0)
15 }
16
17 pub fn rounded(&self, decimal_places: u32) -> f64 {
18 self.round_dp_with_strategy(decimal_places, RoundingStrategy::MidpointAwayFromZero)
19 .to_f64()
20 .unwrap_or(0.0)
21 }
22
23 pub fn value(&self) -> f64 {
27 self.rounded(1)
28 }
29}
30
31impl From<Decimal> for Rating {
32 fn from(value: Decimal) -> Self {
33 Rating(value)
34 }
35}
36
37impl From<i32> for Rating {
38 fn from(value: i32) -> Self {
39 Rating::from(Decimal::from(value))
40 }
41}
42
43impl From<i64> for Rating {
44 fn from(value: i64) -> Self {
45 Rating::from(Decimal::from(value))
46 }
47}
48
49impl std::ops::Deref for Rating {
50 type Target = Decimal;
51
52 fn deref(&self) -> &Self::Target {
53 &self.0
54 }
55}
56
57impl std::ops::Add<Decimal> for Rating {
58 type Output = Rating;
59
60 fn add(self, rhs: Decimal) -> Self::Output {
61 Rating::from(*self + rhs)
62 }
63}
64
65impl std::ops::Sub<Decimal> for Rating {
66 type Output = Rating;
67
68 fn sub(self, rhs: Decimal) -> Self::Output {
69 Rating::from(*self - rhs)
70 }
71}
72
73impl std::fmt::Display for Rating {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.value())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use rust_decimal::prelude::FromPrimitive;
83 use test_case::test_case;
84
85 #[test_case(1.23 => 1.2)]
86 #[test_case(1.25 => 1.3)]
87 #[test_case(1.0005 => 1.0)]
88 #[test_case(1.89 => 1.9)]
89 #[test_case(1.849 => 1.8)]
90 fn rounds_to_1_decimal_place_by_default(value: f64) -> f64 {
91 Rating::from(Decimal::from_f64(value).unwrap()).value()
92 }
93}