sterling_ops/
lib.rs

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}