1#![warn(missing_docs)]
2
3#[cfg(feature = "serde")]
7extern crate serde;
8#[cfg(feature = "serde_json")]
9extern crate serde_json;
10#[cfg(feature = "strason")]
11extern crate strason;
12
13use std::error;
14use std::fmt::{self, Display, Formatter};
15
16use std::ops::{Add, Div, Mul, Sub};
17
18use std::num::ParseFloatError;
19use std::str::FromStr;
20
21type Inner = i64;
23
24pub const SAT_PER_BTC: i64 = 100_000_000;
26
27pub const SAT_PER_BTC_FP: f64 = 100_000_000.0;
29
30pub const MAX: Amount = Amount(Inner::max_value());
32pub const MIN: Amount = Amount(Inner::min_value());
34
35#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
37pub struct Amount(Inner);
38
39impl Amount {
40 pub fn from_btc<T>(btc: T) -> Amount
42 where T:
43 IntoBtc,
44 {
45 btc.into_btc()
46 }
47
48 pub fn from_sat(sat: Inner) -> Amount {
50 Amount(sat)
51 }
52
53 pub fn zero() -> Amount {
55 Amount(0)
56 }
57
58 pub fn one() -> Amount {
60 Amount(1)
61 }
62
63 pub fn max_value() -> Amount { MAX }
65
66 pub fn min_value() -> Amount { MIN }
68
69 pub fn into_inner(self) -> Inner {
71 self.0
72 }
73}
74
75impl Add for Amount {
76 type Output = Amount;
77
78 fn add(self, rhs: Amount) -> Self::Output {
79 Amount::from_sat(self.0 + rhs.0)
80 }
81}
82
83impl Div for Amount {
84 type Output = Amount;
85
86 fn div(self, rhs: Amount) -> Self::Output {
87 Amount::from_sat(self.0 / rhs.0)
88 }
89}
90
91impl Mul for Amount {
92 type Output = Amount;
93
94 fn mul(self, rhs: Amount) -> Self::Output {
95 Amount::from_sat(self.0 * rhs.0)
96 }
97}
98
99impl Sub for Amount {
100 type Output = Amount;
101
102 fn sub(self, rhs: Amount) -> Self::Output {
103 Amount::from_sat(self.0 - rhs.0)
104 }
105}
106
107#[cfg(feature = "serde")]
108impl<'de> serde::Deserialize<'de> for Amount {
109 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
110 where
111 D: serde::de::Deserializer<'de>
112 {
113 Inner::deserialize(deserializer).map(Amount)
114 }
115}
116
117#[cfg(feature = "serde")]
118impl serde::Serialize for Amount {
119 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120 where
121 S: serde::ser::Serializer
122 {
123 Inner::serialize(&self.0, serializer)
124 }
125}
126
127impl FromStr for Amount {
128 type Err = ParseAmountError;
129
130 fn from_str(s: &str) -> Result<Self, Self::Err> {
131 let btc = f64::from_str(s).map_err(ParseAmountError)?;
132
133 Ok(Amount::from_btc(btc))
134 }
135}
136
137#[derive(Debug)]
139pub struct ParseAmountError(ParseFloatError);
140
141impl Display for ParseAmountError {
142 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
143 write!(fmt, "invalid floating point integer: {}", self.0)
144 }
145}
146
147impl error::Error for ParseAmountError {
148 fn cause(&self) -> Option<&error::Error> {
149 Some(&self.0)
150 }
151
152 fn description(&self) -> &'static str {
153 "floating point error"
154 }
155}
156
157fn round_and_to_sat(v: f64) -> Inner {
158 if v < 0.0 {
159 ((v * SAT_PER_BTC_FP) - 0.5) as Inner
160 } else {
161 ((v * SAT_PER_BTC_FP) + 0.5) as Inner
162 }
163}
164
165pub trait IntoBtc {
171 fn into_btc(self) -> Amount;
173}
174
175impl<'a> IntoBtc for &'a f64 {
176 fn into_btc(self) -> Amount {
177 let sat = round_and_to_sat(*self);
178 Amount::from_sat(sat)
179 }
180}
181
182impl IntoBtc for f64 {
183 fn into_btc(self) -> Amount {
184 let sat = round_and_to_sat(self);
185 Amount::from_sat(sat)
186 }
187}
188
189#[cfg(feature = "serde_json")]
190impl<'a> IntoBtc for &'a serde_json::value::Number {
191 fn into_btc(self) -> Amount {
192 let num = format!("{}", self);
193 Amount::from_str(&*num).unwrap()
194 }
195}
196
197#[cfg(feature = "serde_json")]
198impl IntoBtc for serde_json::value::Number {
199 fn into_btc(self) -> Amount {
200 let num = format!("{}", self);
201 Amount::from_str(&*num).unwrap()
202 }
203}
204
205#[cfg(feature = "strason")]
206impl<'a> IntoBtc for &'a strason::Json {
207 fn into_btc(self) -> Amount {
208 Amount::from_str(self.num().unwrap()).unwrap()
209 }
210}
211
212#[cfg(feature = "strason")]
213impl IntoBtc for strason::Json {
214 fn into_btc(self) -> Amount {
215 Amount::from_str(self.num().unwrap()).unwrap()
216 }
217}
218
219#[cfg(test)]
220pub mod tests {
221 use std::str::FromStr;
222
223 use super::*;
224
225 #[test]
226 fn amount_from_btc() {
227 assert_eq!(Amount::from_btc(0.00253583).0, 253583);
228 }
229
230 #[test]
231 fn amount_from_sat() {
232 assert_eq!(Amount::from_sat(253583).0, 253583);
233 }
234
235 #[test]
236 fn amount_from_str() {
237 let amt = Amount::from_str("0.00253583").unwrap();
238 assert_eq!(amt, Amount::from_sat(253583));
239 let amt = Amount::from_str("0.10000000").unwrap();
240 assert_eq!(amt, Amount::from_sat(10_000_000));
241 }
242
243 #[test]
244 fn amount_add_div_mul_sub() {
245 let res = ((Amount::from_btc(0.0025) +
246 Amount::from_btc(0.0005)) * (Amount::from_btc(2.0))) /
247 Amount::from_btc(2.0);
248
249 assert_eq!(res, Amount::from_btc(0.003));
250 }
251}