1use crate::error::{Error, Result};
3use crate::xdr;
4use num_rational::Ratio;
5use num_traits::cast::ToPrimitive;
6use rust_decimal::Decimal;
7use std::convert::TryFrom;
8use std::fmt;
9use std::io::{Read, Write};
10use std::str::FromStr;
11
12const STELLAR_SCALE: u32 = 7;
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
16pub struct Amount(pub(crate) Decimal);
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
20pub struct Stroops(pub(crate) i64);
21
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
24pub struct Price(Ratio<i32>);
25
26impl Amount {
27 pub(crate) fn from_decimal(decimal: Decimal) -> Amount {
28 Amount(decimal)
29 }
30
31 pub fn from_stroops(stroops: &Stroops) -> Result<Amount> {
33 let inner = Decimal::new(stroops.0, STELLAR_SCALE);
34 Ok(Amount(inner))
35 }
36
37 pub fn checked_add(&self, other: &Amount) -> Option<Amount> {
39 self.0.checked_add(other.0).map(Amount)
40 }
41
42 pub fn checked_sub(&self, other: &Amount) -> Option<Amount> {
44 self.0.checked_sub(other.0).map(Amount)
45 }
46
47 pub fn checked_mul(&self, other: &Amount) -> Option<Amount> {
49 self.0.checked_mul(other.0).map(Amount)
50 }
51
52 pub fn checked_div(&self, other: &Amount) -> Option<Amount> {
54 self.0.checked_div(other.0).map(Amount)
55 }
56
57 pub fn checked_rem(&self, other: &Amount) -> Option<Amount> {
59 self.0.checked_rem(other.0).map(Amount)
60 }
61
62 pub fn to_stroops(&self) -> Result<Stroops> {
64 let scale = self.0.scale();
65 if scale != STELLAR_SCALE {
66 return Err(Error::InvalidAmountScale);
67 }
68 let res = self.0 * Decimal::new(100_000_000, 1);
69 match res.to_i64() {
70 Some(stroops) => Ok(Stroops::new(stroops)),
71 None => Err(Error::InvalidStroopsAmount),
72 }
73 }
74}
75
76impl FromStr for Amount {
77 type Err = Error;
78
79 fn from_str(s: &str) -> Result<Amount> {
80 let mut inner = Decimal::from_str(s)?;
81 let scale = inner.scale();
83 if scale > STELLAR_SCALE {
84 Err(Error::InvalidAmountScale)
85 } else {
86 inner.rescale(STELLAR_SCALE);
87 Ok(Amount::from_decimal(inner))
88 }
89 }
90}
91
92impl fmt::Display for Amount {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}", self.0)
95 }
96}
97
98impl Stroops {
99 pub fn new(amount: i64) -> Stroops {
101 Stroops(amount)
102 }
103
104 pub fn max() -> Stroops {
106 Stroops(i64::MAX)
107 }
108
109 pub fn to_i64(&self) -> i64 {
111 self.0
112 }
113
114 pub fn checked_add(&self, other: &Stroops) -> Option<Stroops> {
116 self.0.checked_add(other.0).map(Stroops)
117 }
118
119 pub fn checked_sub(&self, other: &Stroops) -> Option<Stroops> {
121 self.0.checked_sub(other.0).map(Stroops)
122 }
123
124 pub fn checked_mul(&self, other: &Stroops) -> Option<Stroops> {
126 self.0.checked_mul(other.0).map(Stroops)
127 }
128
129 pub fn checked_div(&self, other: &Stroops) -> Option<Stroops> {
131 self.0.checked_div(other.0).map(Stroops)
132 }
133
134 pub fn checked_rem(&self, other: &Stroops) -> Option<Stroops> {
136 self.0.checked_rem(other.0).map(Stroops)
137 }
138
139 pub fn to_xdr_int64(&self) -> Result<xdr::Int64> {
141 Ok(self.0)
142 }
143
144 pub fn to_xdr_uint32(&self) -> Result<xdr::Uint32> {
146 if self.0 >= 0 {
147 Ok(self.0 as u32)
148 } else {
149 Err(Error::NegativeStroops)
150 }
151 }
152
153 pub fn from_xdr_int64(x: xdr::Int64) -> Result<Stroops> {
155 Ok(Stroops(x))
156 }
157
158 pub fn from_xdr_uint32(x: xdr::Uint32) -> Result<Stroops> {
160 Ok(Stroops::new(x as i64))
161 }
162}
163
164impl Price {
165 pub fn new(numerator: i32, denominator: i32) -> Price {
167 let inner = Ratio::new_raw(numerator, denominator);
168 Price(inner)
169 }
170
171 pub fn numerator(&self) -> i32 {
173 *self.0.numer()
174 }
175
176 pub fn denominator(&self) -> i32 {
178 *self.0.denom()
179 }
180
181 pub fn reduced(&self) -> Price {
183 let inner = self.0.reduced();
184 Price(inner)
185 }
186
187 pub fn to_xdr(&self) -> Result<xdr::Price> {
189 Ok(xdr::Price {
190 n: self.numerator(),
191 d: self.denominator(),
192 })
193 }
194
195 pub fn from_xdr(x: &xdr::Price) -> Result<Price> {
197 Ok(Price::new(x.n, x.d))
198 }
199}
200
201impl FromStr for Price {
202 type Err = Error;
203
204 fn from_str(s: &str) -> Result<Price> {
205 if s.is_empty() {
206 return Err(Error::ParsePriceError);
207 }
208 let max_i32 = Decimal::new(i32::MAX as i64, 0);
209 let mut number = Decimal::from_str(s).map_err(|_| Error::ParsePriceError)?;
210 let zero = Decimal::new(0, 0);
211 let one = Decimal::new(1, 0);
212
213 let mut fractions = vec![(zero, one), (one, zero)];
214 let mut i = 2;
215 loop {
216 if number > max_i32 {
217 break;
218 }
219
220 let whole = number.floor();
221 let fract = number - whole;
222 let h = whole * fractions[i - 1].0 + fractions[i - 2].0;
223 let k = whole * fractions[i - 1].1 + fractions[i - 2].1;
224 if (k >= max_i32) || (h >= max_i32) {
225 break;
226 }
227 fractions.push((h, k));
228 if fract == zero {
229 break;
230 }
231 number = one / fract;
232 i += 1;
233 }
234 match fractions.last() {
235 None => Err(Error::ParsePriceError),
236 Some((num, den)) => {
237 let num = num.to_i32();
238 let den = den.to_i32();
239 match (num, den) {
240 (Some(0), _) => Err(Error::ParsePriceError),
241 (_, Some(0)) => Err(Error::ParsePriceError),
242 (Some(num), Some(den)) => Ok(Price::new(num, den)),
243 _ => Err(Error::ParsePriceError),
244 }
245 }
246 }
247 }
248}
249
250impl TryFrom<Amount> for Stroops {
251 type Error = Error;
252
253 fn try_from(amount: Amount) -> std::result::Result<Self, Self::Error> {
254 amount.to_stroops()
255 }
256}
257
258impl TryFrom<Stroops> for Amount {
259 type Error = Error;
260
261 fn try_from(stroops: Stroops) -> std::result::Result<Self, Self::Error> {
262 Amount::from_stroops(&stroops)
263 }
264}
265
266impl xdr::WriteXdr for Price {
267 fn write_xdr<W: Write>(&self, w: &mut xdr::Limited<W>) -> xdr::Result<()> {
268 let xdr_price = self.to_xdr().map_err(|_| xdr::Error::Invalid)?;
269 xdr_price.write_xdr(w)
270 }
271}
272
273impl xdr::ReadXdr for Price {
274 fn read_xdr<R: Read>(r: &mut xdr::Limited<R>) -> xdr::Result<Self> {
275 let xdr_result = xdr::Price::read_xdr(r)?;
276 Self::from_xdr(&xdr_result).map_err(|_| xdr::Error::Invalid)
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::{Amount, Price, Stroops};
283 use crate::xdr::{XDRDeserialize, XDRSerialize};
284 use std::str;
285 use std::str::FromStr;
286
287 #[test]
288 fn test_amount_from_str() {
289 let amount1 = str::parse::<Amount>("123.4567891").unwrap();
290 let amount2 = str::parse::<Amount>("123.4567891").unwrap();
291 let amount3 = str::parse::<Amount>("123.4567890").unwrap();
292
293 assert_eq!(amount1, amount2);
294 assert_ne!(amount1, amount3);
295 assert!(amount3 < amount1);
296 }
297
298 #[test]
299 fn test_error_too_many_decimals() {
300 let res = str::parse::<Amount>("123.45678901");
301 assert!(res.is_err());
302 }
303
304 #[test]
305 fn test_amount_to_stroops() {
306 let amount = str::parse::<Amount>("123.45678").unwrap();
307 let stroops = amount.to_stroops().unwrap();
308 assert_eq!(stroops, Stroops::new(1234567800));
309 }
310
311 #[test]
312 fn test_price_from_str() {
313 let one_22 = "1".repeat(22);
314 let one_big = "1".repeat(1000000);
315 let test_cases = vec![
317 ("0.1", Some((1, 10))),
318 ("0.01", Some((1, 100))),
319 ("0.001", Some((1, 1000))),
320 ("543.017930", Some((54301793, 100000))),
321 ("319.69983", Some((31969983, 100000))),
322 ("0.93", Some((93, 100))),
323 ("0.5", Some((1, 2))),
324 ("1.730", Some((173, 100))),
325 ("0.85334384", Some((5333399, 6250000))),
326 ("5.5", Some((11, 2))),
327 ("2.72783", Some((272783, 100000))),
328 ("638082.0", Some((638082, 1))),
329 ("58.04", Some((1451, 25))),
330 ("41.265", Some((8253, 200))),
331 ("5.1476", Some((12869, 2500))),
332 ("95.14", Some((4757, 50))),
333 ("0.74580", Some((3729, 5000))),
334 ("4119.0", Some((4119, 1))),
335 (&one_22, None),
337 (&one_big, None),
338 ("1E9223372036854775807", None),
340 ("1e9223372036854775807", None),
341 ];
342
343 for (test_str, expected_res) in test_cases {
344 let res = Price::from_str(test_str);
345 match expected_res {
346 None => {
347 assert!(res.is_err());
348 }
349 Some((num, den)) => {
350 let price = res.unwrap();
351 assert_eq!(num, price.numerator());
352 assert_eq!(den, price.denominator());
353 }
354 }
355 }
356 }
357
358 #[test]
359 fn test_price_xdr_ser() {
360 let price = Price::new(123, 456);
361 let xdr = price.xdr_base64().unwrap();
362 assert_eq!("AAAAewAAAcg=", xdr);
363 }
364
365 #[test]
366 fn test_price_xdr_de() {
367 let expected = Price::new(123, 456);
368 let price = Price::from_xdr_base64("AAAAewAAAcg=").unwrap();
369 assert_eq!(expected, price);
370 }
371}