reproto_core/
rp_number.rs1use num_bigint::BigInt;
2use num_integer::Integer;
3use num_traits::Signed;
4use num_traits::cast::ToPrimitive;
5use serde;
6use std::fmt;
7use std::result;
8
9macro_rules! convert_method {
10 ($ty:ty, $method:ident) => {
11 pub fn $method(&self) -> Option<$ty> {
12 let m = self.multiple();
13 self.digits.checked_div(&m).and_then(|r| r.$method())
14 }
15 };
16}
17
18#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct RpNumber {
20 pub digits: BigInt,
22 pub decimal: usize,
24}
25
26impl RpNumber {
27 convert_method!(i32, to_i32);
28 convert_method!(i64, to_i64);
29 convert_method!(u32, to_u32);
30 convert_method!(u64, to_u64);
31 convert_method!(usize, to_usize);
32
33 fn multiple(&self) -> BigInt {
35 let mut multiple: BigInt = 1.into();
36
37 for _ in 0..self.decimal {
38 let ten: BigInt = 10.into();
39 multiple = multiple * ten;
40 }
41
42 multiple
43 }
44
45 pub fn to_bigint(&self) -> Option<&BigInt> {
47 if self.decimal != 0 {
48 return None;
49 }
50
51 Some(&self.digits)
52 }
53
54 pub fn to_f64(&self) -> Option<f64> {
55 let multiple = self.multiple();
56 let (base, decimal) = self.digits.div_mod_floor(&multiple);
57
58 base.to_f64().and_then(|base| {
59 decimal.to_f64().and_then(|decimal| {
60 multiple
61 .to_f64()
62 .map(|multiple| base + (decimal / multiple))
63 })
64 })
65 }
66}
67
68impl From<u32> for RpNumber {
69 fn from(value: u32) -> RpNumber {
70 RpNumber {
71 digits: value.into(),
72 decimal: 0usize,
73 }
74 }
75}
76
77impl From<u64> for RpNumber {
78 fn from(value: u64) -> RpNumber {
79 RpNumber {
80 digits: value.into(),
81 decimal: 0usize,
82 }
83 }
84}
85
86impl From<i32> for RpNumber {
87 fn from(value: i32) -> RpNumber {
88 RpNumber {
89 digits: value.into(),
90 decimal: 0usize,
91 }
92 }
93}
94
95impl From<i64> for RpNumber {
96 fn from(value: i64) -> RpNumber {
97 RpNumber {
98 digits: value.into(),
99 decimal: 0usize,
100 }
101 }
102}
103
104impl From<BigInt> for RpNumber {
105 fn from(value: BigInt) -> RpNumber {
106 RpNumber {
107 digits: value,
108 decimal: 0usize,
109 }
110 }
111}
112
113impl fmt::Debug for RpNumber {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 write!(f, "RpNumber({})", self)
116 }
117}
118
119impl fmt::Display for RpNumber {
120 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 if self.decimal == 0 {
122 return write!(f, "{}", self.digits);
123 }
124
125 let multiple = self.multiple();
126 let (base, decimal) = self.digits.abs().div_mod_floor(&multiple);
127
128 let decimal = format!("{}", decimal);
129
130 let decimal = if decimal.len() < self.decimal {
132 let mut s = String::new();
133
134 for _ in decimal.len()..self.decimal {
135 s.push('0');
136 }
137
138 s.push_str(&decimal);
139 s
140 } else {
141 decimal
142 };
143
144 if self.digits.is_negative() {
145 write!(f, "-{}.{}", base, decimal)
146 } else {
147 write!(f, "{}.{}", base, decimal)
148 }
149 }
150}
151
152impl serde::Serialize for RpNumber {
153 fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
154 where
155 S: serde::Serializer,
156 {
157 let n = self.to_f64().unwrap();
158 serializer.serialize_f64(n)
159 }
160}
161
162#[cfg(test)]
163mod test_numbers {
164 use super::*;
165
166 #[test]
167 fn test_number() {
168 let n = RpNumber {
169 digits: 104321.into(),
170 decimal: 2,
171 };
172
173 assert_eq!(Some(1043.21), n.to_f64());
174 assert_eq!(Some(1043), n.to_u32());
175 assert_eq!(Some(1043), n.to_u64());
176 assert_eq!(Some(1043), n.to_i32());
177 assert_eq!(Some(1043), n.to_i64());
178 }
179
180 #[test]
181 fn test_negative() {
182 let n = RpNumber {
183 digits: (-104321).into(),
184 decimal: 2,
185 };
186
187 assert_eq!(None, n.to_u64());
188 assert_eq!(Some(-1043.21), n.to_f64());
189 assert_eq!(Some(-1043), n.to_i32());
190 assert_eq!(Some(-1043), n.to_i64());
191 assert_eq!(None, n.to_u32());
192 assert_eq!(None, n.to_u64());
193 }
194
195 #[test]
196 fn test_display() {
197 let n = RpNumber {
198 digits: (104321).into(),
199 decimal: 2,
200 };
201
202 assert_eq!("1043.21", format!("{}", n));
203
204 let n2 = RpNumber {
205 digits: (104321).into(),
206 decimal: 0,
207 };
208
209 assert_eq!("104321", format!("{}", n2));
210
211 let n3 = RpNumber {
212 digits: (104321).into(),
213 decimal: 10,
214 };
215
216 assert_eq!("0.0000104321", format!("{}", n3));
217 }
218}