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
use crate::currency::*;
use crate::{Money, MoneyError};
use rust_decimal::Decimal;
use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct Exchange {
map: HashMap<String, ExchangeRate>,
}
impl Exchange {
pub fn new() -> Exchange {
Exchange {
map: HashMap::new(),
}
}
pub fn add_or_update_rate(&mut self, rate: &ExchangeRate) {
let key = Exchange::generate_key(rate.from, rate.to);
self.map.insert(key, *rate);
}
pub fn get_rate(self, from: &'static Currency, to: &'static Currency) -> Option<ExchangeRate> {
let key = Exchange::generate_key(from, to);
match self.map.get(&key) {
Some(v) => Some(*v),
None => None,
}
}
fn generate_key(from: &'static Currency, to: &'static Currency) -> String {
from.to_string() + "-" + &to.to_string()
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct ExchangeRate {
pub from: &'static Currency,
pub to: &'static Currency,
rate: Decimal,
}
impl ExchangeRate {
pub fn new(
from: &'static Currency,
to: &'static Currency,
rate: Decimal,
) -> Result<ExchangeRate, MoneyError> {
if from == to {
return Err(MoneyError::InvalidCurrency);
}
Ok(ExchangeRate { from, to, rate })
}
pub fn convert(&self, amount: Money) -> Result<Money, MoneyError> {
if amount.currency() != self.from {
return Err(MoneyError::InvalidCurrency);
}
let converted_amount = amount.amount() * self.rate;
Ok(Money::from_decimal(converted_amount, self.to))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::money;
use crate::Iso::*;
use rust_decimal_macros::*;
#[test]
fn exchange_stores_rates() {
let usd = Currency::get(USD);
let eur = Currency::get(EUR);
let rate = ExchangeRate::new(usd, eur, dec!(1.5)).unwrap();
let mut exchange = Exchange::new();
exchange.add_or_update_rate(&rate);
let fetched_rate = exchange.get_rate(usd, eur).unwrap();
assert_eq!(fetched_rate.rate, dec!(1.5));
}
#[test]
fn rate_convert() {
let rate = ExchangeRate::new(Currency::get(USD), Currency::get(EUR), dec!(1.5)).unwrap();
let amount = money!(10, "USD");
let expected_amount = money!("15", "EUR");
let converted_rate = rate.convert(amount).unwrap();
assert_eq!(converted_rate, expected_amount);
}
#[test]
fn rate_convert_errors_if_currencies_dont_match() {
let rate =
ExchangeRate::new(Currency::get(Iso::GBP), Currency::get(Iso::EUR), dec!(1.5)).unwrap();
let amount = money!(10, "USD");
assert_eq!(
rate.convert(amount).unwrap_err(),
MoneyError::InvalidCurrency,
);
}
#[test]
fn rate_new_errors_if_currencies_are_equal() {
let rate = ExchangeRate::new(Currency::get(Iso::GBP), Currency::get(Iso::GBP), dec!(1.5));
assert_eq!(rate.unwrap_err(), MoneyError::InvalidCurrency,);
}
}