1extern crate lazysort;
2#[allow(unused_imports)]
3#[macro_use]
4extern crate lazy_static;
5extern crate regex;
6extern crate separator;
7extern crate serde;
8#[macro_use]
9extern crate serde_derive;
10extern crate serde_yaml;
11
12use std::collections::HashMap;
13
14pub mod config;
15mod convert;
16pub mod currency;
17
18use currency::Currency;
19use regex::Regex;
20use separator::Separatable;
21
22pub fn add_operation(
23 augend: &str,
24 addend: &str,
25 custom_currency_regex: &Regex,
26 currencies: &[Currency],
27 print_full: bool,
28) -> String {
29 let lhs =
30 convert::calculate_total_copper_value(augend, custom_currency_regex, get_rates(currencies));
31
32 let rhs = convert::calculate_total_copper_value(
33 addend,
34 &custom_currency_regex,
35 get_rates(currencies.as_ref()),
36 );
37
38 convert::exchange_currencies(lhs + rhs, currencies, print_full).join(", ")
39}
40
41pub fn sub_operation(
42 minuend: &str,
43 subtrahend: &str,
44 custom_currency_regex: &Regex,
45 currencies: &[Currency],
46 print_full: bool,
47) -> String {
48 let lhs = convert::calculate_total_copper_value(
49 minuend,
50 custom_currency_regex,
51 get_rates(currencies),
52 );
53
54 let rhs = convert::calculate_total_copper_value(
55 subtrahend,
56 custom_currency_regex,
57 get_rates(currencies),
58 );
59
60 let difference = if lhs > rhs { lhs - rhs } else { rhs - lhs };
61 convert::exchange_currencies(difference, currencies, print_full).join(", ")
62}
63
64pub fn mul_operation(
65 multiplicand: &str,
66 multiplier: usize,
67 custom_currency_regex: &Regex,
68 currencies: &[Currency],
69 print_full: bool,
70) -> String {
71 let lhs = convert::calculate_total_copper_value(
72 multiplicand,
73 custom_currency_regex,
74 get_rates(currencies),
75 );
76
77 convert::exchange_currencies(lhs * multiplier, currencies, print_full).join(", ")
78}
79
80pub fn div_operation(
81 dividend: &str,
82 divisor: usize,
83 custom_currency_regex: &Regex,
84 currencies: &[Currency],
85 print_full: bool,
86) -> String {
87 let lhs = convert::calculate_total_copper_value(
88 dividend,
89 custom_currency_regex,
90 get_rates(currencies),
91 );
92
93 convert::exchange_currencies(lhs / divisor, currencies, print_full).join(", ")
94}
95
96pub fn copper_operation(
97 values: &str,
98 custom_currency_regex: &Regex,
99 currencies: &[Currency],
100) -> String {
101 let copper_value =
102 convert::calculate_total_copper_value(values, custom_currency_regex, get_rates(currencies));
103
104 format!("{}c", copper_value.separated_string())
105}
106
107pub fn default_operation(values: &str, currencies: &[Currency], print_full: bool) -> String {
108 let copper_value = convert::calculate_total_copper_value(
109 values,
110 &Regex::new(r"(0|(?:[1-9](?:\d+|\d{0,2}(?:,\d{3})*)))+([cegps])").unwrap(),
111 get_rates(config::phb_config().as_ref()),
112 );
113
114 let exchanged_currencies = convert::exchange_currencies(copper_value, currencies, print_full);
115
116 exchanged_currencies.join(", ")
117}
118
119fn get_rates(currencies: &[Currency]) -> HashMap<String, usize> {
120 currencies
121 .iter()
122 .map(|c| c.alias.clone())
123 .zip(currencies.iter().map(|c| c.rate))
124 .collect()
125}
126
127#[cfg(test)]
128mod test {
129 use std::collections::HashMap;
130
131 use currency::Currency;
132 use regex::Regex;
133
134 use super::{
135 add_operation, copper_operation, default_operation, div_operation, get_rates,
136 mul_operation, sub_operation,
137 };
138
139 lazy_static! {
140 static ref CURRENCIES: Vec<Currency> = vec![
141 Currency::new("penny", 1, "p", Some("pence".to_owned()), None),
142 Currency::new("shilling", 100, "s", Some("sterling".to_owned()), None),
143 ];
144 static ref CUSTOM_CURRENCY_REGEX: Regex = Regex::new(&format!(
145 "(\\d+)([{}])",
146 CURRENCIES
147 .iter()
148 .map(|c| c.alias.clone())
149 .fold(String::new(), |group, a| group + &a)
150 )).unwrap();
151 }
152
153 #[test]
154 fn test_get_rates() {
155 let rates: HashMap<_, _> = vec![("p".to_owned(), 1usize), ("s".to_owned(), 100usize)]
156 .into_iter()
157 .collect();
158 assert_eq!(rates, get_rates(&CURRENCIES));
159 }
160
161 #[test]
162 fn test_add_operation_same_currencies() {
163 let result = "3p".to_owned();
164 assert_eq!(
165 result,
166 add_operation("1p", "2p", &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
167 );
168 }
169
170 #[test]
171 fn test_add_operation_diff_currencies() {
172 let result = "1s, 1p".to_owned();
173 assert_eq!(
174 result,
175 add_operation("1s", "1p", &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
176 );
177 }
178
179 #[test]
180 fn test_sub_operation_smaller_subtrahend() {
181 let result = "1p".to_owned();
182 assert_eq!(
183 result,
184 sub_operation("2p", "1p", &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
185 );
186 }
187
188 #[test]
189 fn test_sub_operation_larger_subtrahend() {
190 let result = "1p".to_owned();
191 assert_eq!(
192 result,
193 sub_operation("1p", "2p", &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
194 );
195 }
196
197 #[test]
198 fn test_mul_operation() {
199 let result = "6p".to_owned();
200 assert_eq!(
201 result,
202 mul_operation("3p", 2, &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
203 );
204 }
205
206 #[test]
207 fn test_div_operation_even_dividend() {
208 let result = "2p".to_owned();
209 assert_eq!(
210 result,
211 div_operation("4p", 2, &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
212 );
213 }
214
215 #[test]
216 fn test_div_operation_odd_dividend() {
217 let result = "1p".to_owned();
218 assert_eq!(
219 result,
220 div_operation("3p", 2, &CUSTOM_CURRENCY_REGEX, &CURRENCIES, false)
221 );
222 }
223
224 #[test]
225 fn test_copper_operation() {
226 let result = "103c".to_owned();
227 assert_eq!(
228 result,
229 copper_operation("1s 3p", &CUSTOM_CURRENCY_REGEX, &CURRENCIES)
230 );
231 }
232
233 #[test]
234 fn test_default_operation() {
235 let result = "1 shilling";
236 assert_eq!(result, default_operation("1g", &CURRENCIES, true));
237 }
238
239 #[test]
240 fn test_default_operation_plural_output() {
241 let result = "2 sterling";
242 assert_eq!(result, default_operation("2g", &CURRENCIES, true));
243 }
244}