rtm_core/models/
amount.rs

1use rust_decimal::Decimal;
2
3const DECIMAL_PRECISION: u32 = 4;
4
5/// Represents a decimal amount with a fixed precision 4.
6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
7#[repr(transparent)]
8#[must_use]
9pub struct Amount {
10    value: Decimal,
11}
12
13impl Amount {
14    pub fn zero() -> Self {
15        Self::new(Decimal::from(0))
16    }
17
18    fn new(mut value: Decimal) -> Self {
19        value.rescale(DECIMAL_PRECISION);
20        Self { value }
21    }
22}
23
24impl std::fmt::Display for Amount {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "{}", self.value)
27    }
28}
29
30impl From<i32> for Amount {
31    fn from(value: i32) -> Self {
32        Self::new(value.into())
33    }
34}
35
36impl From<u32> for Amount {
37    fn from(value: u32) -> Self {
38        Self::new(value.into())
39    }
40}
41
42impl Default for Amount {
43    fn default() -> Self {
44        Self::zero()
45    }
46}
47
48#[derive(Debug, PartialEq, Eq, Hash)]
49pub struct InvalidNumericalStringError;
50
51impl TryFrom<&str> for Amount {
52    type Error = InvalidNumericalStringError;
53
54    fn try_from(value: &str) -> Result<Self, Self::Error> {
55        match Decimal::try_from(value) {
56            Ok(result) => Ok(Self::new(result)),
57            Err(_) => Err(InvalidNumericalStringError),
58        }
59    }
60}
61
62macro_rules! impl_binary_op {
63    ( $op: ty, $method: ident ) => {
64        impl $op for Amount {
65            type Output = Self;
66
67            fn $method(self, rhs: Self) -> Self {
68                Self::new(self.value.$method(rhs.value))
69            }
70        }
71    };
72}
73
74macro_rules! impl_unary_op {
75    ( $op: ty, $method: ident ) => {
76        impl $op for Amount {
77            fn $method(&mut self, rhs: Self) {
78                self.value.$method(rhs.value);
79            }
80        }
81    };
82}
83
84impl_binary_op!(std::ops::Add, add);
85impl_binary_op!(std::ops::Sub, sub);
86impl_binary_op!(std::ops::Mul, mul);
87impl_binary_op!(std::ops::Div, div);
88impl_unary_op!(std::ops::AddAssign, add_assign);
89impl_unary_op!(std::ops::SubAssign, sub_assign);
90impl_unary_op!(std::ops::MulAssign, mul_assign);
91impl_unary_op!(std::ops::DivAssign, div_assign);
92
93impl std::ops::Neg for Amount {
94    type Output = Self;
95
96    fn neg(self) -> Self {
97        Self::new(self.value.neg())
98    }
99}