1use std::convert::TryFrom;
43use std::fmt;
44use std::fmt::Formatter;
45use std::ops::{Add, Mul, Sub};
46use std::str::FromStr;
47
48use rust_decimal::prelude::*;
49use rust_decimal::Decimal;
50use serde::{Deserialize, Serialize};
51
52#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
66pub struct Percentage(Decimal);
67
68impl From<Decimal> for Percentage {
69 fn from(p: Decimal) -> Self {
70 Percentage(p)
71 }
72}
73
74impl From<f64> for Percentage {
75 fn from(p: f64) -> Self {
76 Percentage(Decimal::from_f64(p).unwrap())
77 }
78}
79
80impl From<f32> for Percentage {
81 fn from(p: f32) -> Self {
82 Percentage(Decimal::from_f32(p).unwrap())
83 }
84}
85
86impl TryFrom<&str> for Percentage {
87 type Error = rust_decimal::Error;
88 fn try_from(p: &str) -> Result<Self, Self::Error> {
89 Ok(Percentage(Decimal::from_str(p)?))
90 }
91}
92
93impl FromStr for Percentage {
94 type Err = rust_decimal::Error;
95 fn from_str(p: &str) -> Result<Self, Self::Err> {
96 Self::try_from(p)
97 }
98}
99
100impl fmt::Debug for Percentage {
101 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
102 f.write_str(&format!("{}%", self.0 * Decimal::from(100)))
103 }
104}
105
106impl fmt::Display for Percentage {
107 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
108 f.write_str(&format!("{:?}", self))
109 }
110}
111
112impl Mul for Percentage {
113 type Output = Self;
114 fn mul(self, rhs: Self) -> Self::Output {
115 Percentage::from(self.0 * rhs.0)
116 }
117}
118
119impl<'a, 'b> Mul<&'a Percentage> for &'b Percentage {
120 type Output = Percentage;
121 fn mul(self, rhs: &'a Percentage) -> Self::Output {
122 Percentage::from(rhs.0 * self.0)
123 }
124}
125
126impl Mul<Decimal> for Percentage {
127 type Output = Decimal;
128 fn mul(self, rhs: Decimal) -> Self::Output {
129 self.0 * rhs
130 }
131}
132
133impl Mul<Percentage> for Decimal {
134 type Output = Decimal;
135 fn mul(self, rhs: Percentage) -> Self::Output {
136 rhs * self
137 }
138}
139
140macro_rules! impl_mul {
141 ($T:ty, $from_ty:path, $to_ty:path) => {
142 impl Mul<$T> for Percentage {
143 type Output = $T;
144 fn mul(self, rhs: $T) -> Self::Output {
145 let d: Decimal = $from_ty(rhs).unwrap();
146 $to_ty(&(self.0 * d)).unwrap()
147 }
148 }
149 impl Mul<Percentage> for $T {
150 type Output = $T;
151 fn mul(self, rhs: Percentage) -> Self::Output {
152 let d: Decimal = $from_ty(self).unwrap();
153 $to_ty(&(rhs.0 * d)).unwrap()
154 }
155 }
156 };
157}
158
159impl_mul!(isize, FromPrimitive::from_isize, ToPrimitive::to_isize);
160impl_mul!(i8, FromPrimitive::from_i8, ToPrimitive::to_i8);
161impl_mul!(i16, FromPrimitive::from_i16, ToPrimitive::to_i16);
162impl_mul!(i32, FromPrimitive::from_i32, ToPrimitive::to_i32);
163impl_mul!(i64, FromPrimitive::from_i64, ToPrimitive::to_i64);
164impl_mul!(usize, FromPrimitive::from_usize, ToPrimitive::to_usize);
165impl_mul!(u8, FromPrimitive::from_u8, ToPrimitive::to_u8);
166impl_mul!(u16, FromPrimitive::from_u16, ToPrimitive::to_u16);
167impl_mul!(u32, FromPrimitive::from_u32, ToPrimitive::to_u32);
168impl_mul!(u64, FromPrimitive::from_u64, ToPrimitive::to_u64);
169impl_mul!(f64, FromPrimitive::from_f64, ToPrimitive::to_f64);
170impl_mul!(f32, FromPrimitive::from_f32, ToPrimitive::to_f32);
171
172impl Add for Percentage {
173 type Output = Self;
174 fn add(self, rhs: Self) -> Self::Output {
175 let sum = self.0 + rhs.0;
176
177 Percentage::from(sum)
178 }
179}
180
181impl Add<Decimal> for Percentage {
182 type Output = Self;
183 fn add(self, rhs: Decimal) -> Self::Output {
184 self + Percentage::from(rhs)
185 }
186}
187
188impl Add<Percentage> for Decimal {
189 type Output = Percentage;
190 fn add(self, rhs: Percentage) -> Self::Output {
191 rhs + Percentage::from(self)
192 }
193}
194
195macro_rules! impl_add {
196 ($T:ty, $from_ty:path) => {
197 impl Add<$T> for Percentage {
198 type Output = Percentage;
199 fn add(self, rhs: $T) -> Self::Output {
200 let d: Decimal = $from_ty(rhs).unwrap();
201 self + Percentage::from(d)
202 }
203 }
204 impl Add<Percentage> for $T {
205 type Output = Percentage;
206 fn add(self, rhs: Percentage) -> Self::Output {
207 let d: Decimal = $from_ty(self).unwrap();
208 rhs + Percentage::from(d)
209 }
210 }
211 };
212}
213
214impl_add!(isize, FromPrimitive::from_isize);
215impl_add!(i8, FromPrimitive::from_i8);
216impl_add!(i16, FromPrimitive::from_i16);
217impl_add!(i32, FromPrimitive::from_i32);
218impl_add!(i64, FromPrimitive::from_i64);
219impl_add!(usize, FromPrimitive::from_usize);
220impl_add!(u8, FromPrimitive::from_u8);
221impl_add!(u16, FromPrimitive::from_u16);
222impl_add!(u32, FromPrimitive::from_u32);
223impl_add!(u64, FromPrimitive::from_u64);
224impl_add!(f64, FromPrimitive::from_f64);
225impl_add!(f32, FromPrimitive::from_f32);
226
227impl Sub for Percentage {
228 type Output = Self;
229
230 fn sub(self, rhs: Self) -> Self::Output {
231 let dif = self.0 - rhs.0;
232
233 Percentage::from(dif)
234 }
235}
236
237macro_rules! impl_sub {
238 ($T:ty, $from_ty:path) => {
239 impl Sub<$T> for Percentage {
240 type Output = Percentage;
241 fn sub(self, rhs: $T) -> Self::Output {
242 let d: Decimal = $from_ty(rhs).unwrap();
243 self - Percentage::from(d)
244 }
245 }
246 impl Sub<Percentage> for $T {
247 type Output = Percentage;
248 fn sub(self, rhs: Percentage) -> Self::Output {
249 let d: Decimal = $from_ty(self).unwrap();
250 Percentage::from(d) - rhs
251 }
252 }
253 };
254}
255
256impl_sub!(isize, FromPrimitive::from_isize);
257impl_sub!(i8, FromPrimitive::from_i8);
258impl_sub!(i16, FromPrimitive::from_i16);
259impl_sub!(i32, FromPrimitive::from_i32);
260impl_sub!(i64, FromPrimitive::from_i64);
261impl_sub!(usize, FromPrimitive::from_usize);
262impl_sub!(u8, FromPrimitive::from_u8);
263impl_sub!(u16, FromPrimitive::from_u16);
264impl_sub!(u32, FromPrimitive::from_u32);
265impl_sub!(u64, FromPrimitive::from_u64);
266impl_sub!(f64, FromPrimitive::from_f64);
267impl_sub!(f32, FromPrimitive::from_f32);
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn percentage() {
275 let p1 = Percentage::from(0.5);
276 let p2 = Percentage::try_from("0.00000015").unwrap();
277 let p3 = Percentage::from(0.2);
278 let p4 = Percentage::from(1.2);
279
280 assert_eq!(1 - p1, Percentage::from(0.5));
281 assert_eq!(p1 - 0.2, Percentage::from(0.3));
282 assert_eq!(1u8 - p1, Percentage::from(0.5));
283 assert_eq!(p1 - 0.2f32, Percentage::from(0.3));
284 assert_eq!(1 + p1, Percentage::from(1.5));
285 assert_eq!(p1 + 1, Percentage::from(1.5));
286
287 assert_eq!(p1 - p3, Percentage::from(0.3));
288 assert_eq!(p1 + p2, Percentage::try_from("0.50000015").unwrap());
289 assert_eq!(p4 - p3, 1.0.into());
290
291 assert_eq!(p1 * 100, 50);
292 assert_eq!(p1 * 100u8, 50u8);
293 assert_eq!(p1 * -100i8, -50i8);
294 assert_eq!(p1 * 100u16, 50u16);
295 assert_eq!(p1 * -100i16, -50i16);
296 assert_eq!(p1 * 100u32, 50u32);
297 assert_eq!(p1 * -100i32, -50i32);
298 assert_eq!(p1 * 100u64, 50u64);
299 assert_eq!(p1 * -100i64, -50i64);
300 assert_eq!(p1 * 100usize, 50usize);
301 assert_eq!(p1 * -100isize, -50isize);
302 assert_eq!(p1 * 90.0, 45.0);
303 assert_eq!(p1 * 90.0f32, 45.0f32);
304
305 assert_eq!(100 * p1, 50);
306 assert_eq!(100u8 * p1, 50u8);
307 assert_eq!(-100i8 * p1, -50i8);
308 assert_eq!(100u16 * p1, 50u16);
309 assert_eq!(-100i16 * p1, -50i16);
310 assert_eq!(100u32 * p1, 50u32);
311 assert_eq!(-100i32 * p1, -50i32);
312 assert_eq!(100u64 * p1, 50u64);
313 assert_eq!(-100i64 * p1, -50i64);
314 assert_eq!(100usize * p1, 50usize);
315 assert_eq!(-100isize * p1, -50isize);
316 assert_eq!(90.0 * p1, 45.0);
317 assert_eq!(90.0f32 * p1, 45.0f32);
318 }
319}