1use std::result;
2use std::str::FromStr;
3use std::ops::Mul;
4use num_bigint::BigInt;
5use bigdecimal::BigDecimal;
6use num_traits::cast::{FromPrimitive, ToPrimitive};
7use error::{Error, Result};
8
9const STELLAR_SCALE: i64 = 7;
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
13pub struct Amount {
14 inner: BigDecimal,
15}
16
17impl Amount {
18 pub fn from_stroops(stroops: Stroops) -> Result<Amount> {
20 let data = BigInt::from_i64(stroops.0).ok_or(Error::InvalidStroopsAmount)?;
21 let inner = BigDecimal::new(data, STELLAR_SCALE);
22 Ok(Amount { inner })
23 }
24
25 pub fn as_stroops(&self) -> Result<Stroops> {
27 self.clone().into_stroops()
28 }
29
30 pub fn into_stroops(self) -> Result<Stroops> {
32 let (data, exp) = self.inner.into_bigint_and_exponent();
33 if exp != STELLAR_SCALE {
34 return Err(Error::InvalidAmountScale);
35 }
36 match data.to_i64() {
37 Some(stroops) => Ok(Stroops::new(stroops)),
38 None => Err(Error::InvalidStroopsAmount),
39 }
40 }
41}
42
43impl FromStr for Amount {
44 type Err = Error;
45
46 fn from_str(s: &str) -> result::Result<Amount, Error> {
47 let inner = BigDecimal::from_str(&s)?;
48 let (_, scale) = inner.as_bigint_and_exponent();
50 if scale > STELLAR_SCALE {
51 Err(Error::InvalidAmountScale)
52 } else {
53 let scaled_inner = inner.with_scale(STELLAR_SCALE);
54 Ok(Amount {
55 inner: scaled_inner,
56 })
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
63pub struct Stroops(pub i64);
64
65impl Stroops {
66 pub fn new(amount: i64) -> Stroops {
68 Stroops(amount)
69 }
70}
71
72impl Mul<usize> for Stroops {
73 type Output = Self;
74
75 fn mul(self, rhs: usize) -> Self {
76 Stroops(self.0 * rhs as i64)
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
82pub struct Price {
83 numerator: i32,
84 denominator: i32,
85}
86
87impl Price {
88 pub fn new(numerator: i32, denominator: i32) -> Price {
90 Price {
91 numerator,
92 denominator,
93 }
94 }
95
96 pub fn numerator(&self) -> i32 {
98 self.numerator
99 }
100
101 pub fn denominator(&self) -> i32 {
103 self.denominator
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use std::str;
110 use super::{Amount, Stroops};
111
112 #[test]
113 fn test_amount_from_str() {
114 let amount1 = str::parse::<Amount>("123.4567891").unwrap();
115 let amount2 = str::parse::<Amount>("123.4567891").unwrap();
116 let amount3 = str::parse::<Amount>("123.4567890").unwrap();
117
118 assert_eq!(amount1, amount2);
119 assert_ne!(amount1, amount3);
120 assert!(amount3 < amount1);
121 }
122
123 #[test]
124 fn test_error_too_many_decimals() {
125 let res = str::parse::<Amount>("123.45678901");
126 assert!(res.is_err());
127 }
128
129 #[test]
130 fn test_amount_as_stroops() {
131 let amount = str::parse::<Amount>("123.45678").unwrap();
132 let stroops = amount.as_stroops().unwrap();
133 assert_eq!(stroops, Stroops::new(1234567800));
134 }
135}