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