Skip to main content

ramn_currency/
lib.rs

1// TODO issues with precision. truncation all over the place
2
3// Copyright (c) 2016 Tyler Berry All Rights Reserved.
4//
5// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
6// This file may not be copied, modified, or distributed except according to those terms.
7
8//! A `Currency` is a combination of an optional character (`Option<char>``) and a big integer
9//! (`BigInt`).
10//!
11//! Common operations are overloaded to make numerical operations easy.
12//!
13//! Perhaps the most useful part of this crate is the `Currency::from_str` function, which can
14//! convert international currency representations such as "$1,000.42" and "£10,99" into a
15//! usable `Currency` instance.
16//!
17//! ## Example
18//!
19//! ```
20//! extern crate currency;
21//!
22//! fn main() {
23//!     use currency::Currency;
24//!
25//!     let sock_price = Currency::from_str("$11.99").unwrap();
26//!     let toothbrush_price = Currency::from_str("$1.99").unwrap();
27//!     let subtotal = sock_price + toothbrush_price;
28//!     let tax_rate = 0.07;
29//!     let total = &subtotal + (&subtotal * tax_rate);
30//!     assert_eq!(format!("{}", total), "$14.95");
31//! }
32//! ```
33//!
34//! ## Limitations
35//!
36//! This crate cannot lookup conversion data dynamically. It does supply a `convert` function, but
37//! the conversion rates will need to be input by the user.
38//!
39//! This crate also does not handle rounding or precision. Values are truncated during
40//! multiplication, division, and extra precision in a parse (such as gas prices).
41
42extern crate num;
43
44use std::{ops, fmt, str, error};
45
46use num::bigint::{BigInt, BigUint, Sign};
47use num::Zero;
48use num::traits::FromPrimitive;
49
50const DECIMAL_PLACES: usize = 2;
51const SECTION_LEN: usize = 3; // 1,323.00 <- "323" is a section
52
53/// Represents currency through an optional symbol and amount of coin.
54///
55/// Every 100 coins represents a banknote. (coin: 100 => 1.00)
56#[derive(Debug, Clone, Hash, Default, PartialEq, Eq, PartialOrd)]
57pub struct Currency {
58    symbol: Option<char>,
59    coin: BigInt
60}
61
62impl Currency {
63    /// Creates a blank Currency with no symbol and 0 coin.
64    pub fn new() -> Self {
65        Currency {
66            symbol: None,
67            coin: BigInt::zero()
68        }
69    }
70
71    /// Parses a string literal (&str) and attempts to convert it into a currency. Returns
72    /// `Ok(Currency)` on a successful conversion, otherwise `Err(ParseCurrencyError)`.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use currency::Currency;
78    ///
79    /// let c1 = Currency::from_str("$42.32").unwrap();
80    /// let c2 = Currency::from_str("$0.10").unwrap();
81    /// assert_eq!(c1 + c2, Currency::from_str("$42.42").unwrap());
82    /// ```
83    pub fn from_str(s: &str) -> Result<Currency, ParseCurrencyError> {
84        use std::str::FromStr;
85        use num::bigint::{BigUint, Sign};
86
87        let err = ParseCurrencyError::new(s);
88
89        fn is_symbol(c: char) -> bool {
90            !c.is_digit(10) && c != '-' && c != '.' && c != ','
91        }
92
93        fn is_delimiter(c: char) -> bool {
94            c == '.' || c == ','
95        }
96
97        let mut digits = String::new();
98        let mut symbol = None;
99        let mut sign = Sign::Plus;
100
101        let mut last_delimiter = None;
102        let mut last_streak_len = 0;
103        for c in s.chars() {
104            if c == '-' && digits.len() == 0 {
105                sign = Sign::Minus;
106            } else if is_delimiter(c) {
107                last_streak_len = 0;
108                last_delimiter = Some(c);
109            } else if is_symbol(c) {
110                if symbol.is_none() {
111                    symbol = Some(c);
112                }
113            } else {
114                last_streak_len += 1;
115                digits.push(c);
116            }
117        }
118
119        let unsigned_bigint = if digits.len() > 0 {
120            let parse_result = BigUint::from_str(&digits);
121            match parse_result {
122                Ok(int) => int,
123                Err(_) => {
124                    println!("{:?}", digits);
125                    return Err(err)
126                }
127            }
128        } else {
129            BigUint::zero()
130        };
131        let mut coin = BigInt::from_biguint(sign, unsigned_bigint);
132
133        // decimal adjustment
134        if last_delimiter.is_none() || last_streak_len == 3 { // no decimal at all
135            let big_int_factor = BigInt::from(100);
136            coin = coin * big_int_factor;
137        } else if last_streak_len < 2 { // specifying less cents than needed
138            let factor = 10u32.pow(2 - last_streak_len);
139            let big_int_factor = BigInt::from(factor);
140            coin = coin * big_int_factor;
141        } else if last_streak_len > 2 { // specifying more cents than we can hold
142            let divisor = 10u32.pow(last_streak_len - 2);
143            let big_int = BigInt::from(divisor);
144            coin = coin / big_int;
145        } // else the user has valid cents, no adjustment needed
146
147        let currency = Currency {
148            symbol: symbol,
149            coin: coin
150        };
151
152        Ok(currency)
153    }
154
155    /// Returns the `Sign` of the `BigInt` holding the coins.
156    pub fn sign(&self) -> Sign {
157        self.coin.sign()
158    }
159
160    /// Returns the number of coins held in the `Currency` as `&BigInt`.
161    ///
162    /// Should you need ownership of the returned `BigInt`, call `clone()` on it.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// extern crate num;
168    /// extern crate currency;
169    ///
170    /// fn main() {
171    ///     use num::traits::ToPrimitive;
172    ///     use currency::Currency;
173    ///
174    ///     let c1 = Currency::new();
175    ///     assert_eq!(c1.value().to_u32().unwrap(), 0);
176    ///
177    ///     let c2 = Currency::from_str("$1.42").unwrap();
178    ///     assert_eq!(c2.value().to_u32().unwrap(), 142);
179    /// }
180    /// ```
181    pub fn value(&self) -> &BigInt {
182        &self.coin
183    }
184
185    /// Returns a new `Currency` by multiplying the coin by the conversion rate and changing the
186    /// symbol.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use currency::Currency;
192    ///
193    /// let dollars = Currency::from_str("$10.00").unwrap();
194    /// let conv_rate = 0.89;
195    /// let euros = dollars.convert(0.89, '€');
196    /// assert_eq!(euros, Currency::from_str("€8.90").unwrap());
197    /// ```
198    pub fn convert(&self, conversion_rate: f64, currency_symbol: char) -> Currency {
199        let mut result = self * conversion_rate;
200        result.symbol = Some(currency_symbol);
201        result
202    }
203
204    // TODO
205    // - to_str with comma delimiting
206    // - to_str with euro delimiting
207}
208
209///////////////////////////////////////////////////////////////////////////////////////////////////
210// fmt trait implementations
211///////////////////////////////////////////////////////////////////////////////////////////////////
212
213/// Allows any Currency to be displayed as a String. The format includes comma delimiting with a
214/// two digit precision decimal.
215///
216/// # Example
217///
218/// ```
219/// use currency::Currency;
220///
221/// let dollars = Currency::from_str("$12.10").unwrap();
222/// assert_eq!(dollars.to_string(), "$12.10");
223///
224/// let euros = Currency::from_str("£1.000").unwrap();
225/// assert_eq!(format!("{:e}", euros), "£1.000,00");
226/// ```
227impl fmt::Display for Currency {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        use num::traits::Signed;
230
231        let mut result = String::new();
232
233        if self.coin.sign() == Sign::Minus {
234            result.push('-');
235        }
236
237        if self.symbol.is_some() {
238            result.push(self.symbol.unwrap());
239        }
240
241        let digit_str = self.coin.abs().to_str_radix(10);
242
243        // put symbol before first digit
244        let n_digits = digit_str.len();
245        if n_digits <= DECIMAL_PLACES { // gotta put 0.xx or 0.0x
246            result.push_str("0.");
247            if n_digits == 1 {
248                result.push('0');
249            }
250            result.push_str(&digit_str);
251        } else {
252            let n_before_dec = n_digits - DECIMAL_PLACES;
253            let int_digit_str = &digit_str[0..n_before_dec];
254            let dec_digit_str = &digit_str[n_before_dec..n_digits];
255
256            let first_section_len = n_before_dec % SECTION_LEN;
257            let mut counter = SECTION_LEN - first_section_len;
258            for digit in int_digit_str.chars() {
259                if counter == SECTION_LEN && n_digits > 5{
260                    counter = 0;
261                    result.push(',');
262                }
263                result.push(digit);
264                counter += 1;
265            }
266            result.push('.');
267            result.push_str(dec_digit_str);
268        }
269
270        write!(f, "{}", result)
271    }
272}
273
274impl str::FromStr for Currency {
275    type Err = ParseCurrencyError;
276
277    fn from_str(s: &str) -> Result<Currency, ParseCurrencyError> {
278        Currency::from_str(s)
279    }
280}
281
282#[derive(Debug, Clone, PartialEq)]
283pub struct ParseCurrencyError {
284    source: String
285}
286
287impl ParseCurrencyError {
288    fn new(s: &str) -> Self {
289        ParseCurrencyError {
290            source: s.to_string()
291        }
292    }
293}
294
295impl fmt::Display for ParseCurrencyError {
296    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297        write!(f, "Could not parse {} into a currency.", self.source)
298    }
299}
300
301impl error::Error for ParseCurrencyError {
302    fn description(&self) -> &str {
303        "Failed to parse currency"
304    }
305}
306
307/// Identical to the implementation of Display, but replaces the "." with a ",". Access this
308/// formatting by using "{:e}".
309///
310/// # Example
311///
312/// ```
313/// use currency::Currency;
314///
315/// let euros = Currency::from_str("£1000,99").unwrap();
316/// println!("{:e}", euros);
317/// ```
318/// Which prints:
319/// ```text
320/// "£1.000,99"
321/// ```
322impl fmt::LowerExp for Currency {
323    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324        let temp = format!("{}", self).replace(".", "x");
325        let almost = temp.replace(",", ".");
326        let there_we_go = almost.replace("x", ",");
327        write!(f, "{}", there_we_go)
328    }
329}
330
331///////////////////////////////////////////////////////////////////////////////////////////////////
332// ops trait implementations
333// macros based on bigint: http://rust-num.github.io/num/src/num_bigint/bigint/src/lib.rs.html
334///////////////////////////////////////////////////////////////////////////////////////////////////
335
336macro_rules! impl_all_trait_combinations_for_currency {
337    ($module:ident::$imp:ident, $method:ident) => {
338        impl<'a, 'b> $module::$imp<&'b Currency> for &'a Currency {
339            type Output = Currency;
340
341            #[inline]
342            fn $method(self, other: &'b Currency) -> Currency {
343                if self.symbol == other.symbol {
344                    Currency {
345                        symbol: self.symbol.clone(),
346                        coin: self.coin.clone().$method(other.coin.clone())
347                    }
348                } else {
349                    panic!("Cannot do arithmetic on two different types of currency.");
350                }
351            }
352        }
353
354        impl<'a> $module::$imp<Currency> for &'a Currency {
355            type Output = Currency;
356
357            #[inline]
358            fn $method(self, other: Currency) -> Currency {
359                if self.symbol == other.symbol {
360                    Currency {
361                        symbol: self.symbol.clone(),
362                        coin: self.coin.clone().$method(other.coin)
363                    }
364                } else {
365                    panic!("Cannot do arithmetic on two different types of currency.");
366                }
367            }
368        }
369
370        impl<'a> $module::$imp<&'a Currency> for Currency {
371            type Output = Currency;
372
373            #[inline]
374            fn $method(self, other: &'a Currency) -> Currency {
375                if self.symbol == other.symbol {
376                    Currency {
377                        symbol: self.symbol,
378                        coin: self.coin.$method(other.coin.clone())
379                    }
380                } else {
381                    panic!("Cannot do arithmetic on two different types of currency.");
382                }
383            }
384        }
385
386        impl $module::$imp<Currency> for Currency {
387            type Output = Currency;
388
389            #[inline]
390            fn $method(self, other: Currency) -> Currency {
391                if self.symbol == other.symbol {
392                    Currency {
393                        symbol: self.symbol,
394                        coin: self.coin.$method(other.coin)
395                    }
396                } else {
397                    panic!("Cannot do arithmetic on two different types of currency.");
398                }
399            }
400        }
401    }
402}
403
404impl_all_trait_combinations_for_currency!(ops::Add, add);
405impl_all_trait_combinations_for_currency!(ops::Sub, sub);
406// impl_all_trait_combinations_for_currency!(ops::Mul, mul); TODO decide whether this should exist
407
408// other type must implement Into<BigInt>
409macro_rules! impl_all_trait_combinations_for_currency_into_bigint {
410    ($module:ident::$imp:ident, $method:ident, $other:ty) => {
411        impl<'a, 'b> $module::$imp<&'b $other> for &'a Currency {
412            type Output = Currency;
413
414            #[inline]
415            fn $method(self, other: &'b $other) -> Currency {
416                let big_int: BigInt = other.clone().into();
417                Currency {
418                    symbol: self.symbol.clone(),
419                    coin: self.coin.clone().$method(big_int)
420                }
421            }
422        }
423
424        impl<'a> $module::$imp<$other> for &'a Currency {
425            type Output = Currency;
426
427            #[inline]
428            fn $method(self, other: $other) -> Currency {
429                let big_int: BigInt = other.into();
430                Currency {
431                    symbol: self.symbol.clone(),
432                    coin: self.coin.clone().$method(big_int)
433                }
434            }
435        }
436
437        impl<'a> $module::$imp<&'a $other> for Currency {
438            type Output = Currency;
439
440            #[inline]
441            fn $method(self, other: &'a $other) -> Currency {
442                let big_int: BigInt = other.clone().into();
443                Currency {
444                    symbol: self.symbol,
445                    coin: self.coin.$method(big_int)
446                }
447            }
448        }
449
450        impl $module::$imp<$other> for Currency {
451            type Output = Currency;
452
453            #[inline]
454            fn $method(self, other: $other) -> Currency {
455                let big_int: BigInt = other.into();
456                Currency {
457                    symbol: self.symbol,
458                    coin: self.coin.$method(big_int)
459                }
460            }
461        }
462
463        impl<'a, 'b> $module::$imp<&'b Currency> for &'a $other {
464            type Output = Currency;
465
466            #[inline]
467            fn $method(self, other: &'b Currency) -> Currency {
468                let big_int: BigInt = self.clone().into();
469                Currency {
470                    symbol: other.symbol.clone(),
471                    coin: other.coin.clone().$method(big_int)
472                }
473            }
474        }
475
476        impl<'a> $module::$imp<Currency> for &'a $other {
477            type Output = Currency;
478
479            #[inline]
480            fn $method(self, other: Currency) -> Currency {
481                let big_int: BigInt = self.clone().into();
482                Currency {
483                    symbol: other.symbol,
484                    coin: other.coin.$method(big_int)
485                }
486            }
487        }
488
489        impl<'a> $module::$imp<&'a Currency> for $other {
490            type Output = Currency;
491
492            #[inline]
493            fn $method(self, other: &'a Currency) -> Currency {
494                let big_int: BigInt = self.into();
495                Currency {
496                    symbol: other.symbol.clone(),
497                    coin: other.coin.clone().$method(big_int)
498                }
499            }
500        }
501
502        impl $module::$imp<Currency> for $other {
503            type Output = Currency;
504
505            #[inline]
506            fn $method(self, other: Currency) -> Currency {
507                let big_int: BigInt = self.into();
508                Currency {
509                    symbol: other.symbol,
510                    coin: other.coin.$method(big_int)
511                }
512            }
513        }
514    }
515}
516
517impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, BigUint);
518impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, u8);
519impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, u16);
520impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, u32);
521impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, u64);
522impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, usize);
523impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, i8);
524impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, i16);
525impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, i32);
526impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, i64);
527impl_all_trait_combinations_for_currency_into_bigint!(ops::Mul, mul, isize);
528
529impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, BigUint);
530impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, u8);
531impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, u16);
532impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, u32);
533impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, u64);
534impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, usize);
535impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, i8);
536impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, i16);
537impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, i32);
538impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, i64);
539impl_all_trait_combinations_for_currency_into_bigint!(ops::Div, div, isize);
540
541macro_rules! impl_all_trait_combinations_for_currency_conv_bigint {
542    ($module:ident::$imp:ident, $method:ident, $other:ty, $conv_method:ident) => {
543        impl<'a, 'b> $module::$imp<&'b $other> for &'a Currency {
544            type Output = Currency;
545
546            #[inline]
547            fn $method(self, other: &'b $other) -> Currency {
548                let big_int = BigInt::$conv_method(other.clone() * 100.0).unwrap();
549                Currency {
550                    symbol: self.symbol.clone(),
551                    coin: self.coin.clone().$method(big_int) / BigInt::from(100)
552                }
553            }
554        }
555
556        impl<'a> $module::$imp<$other> for &'a Currency {
557            type Output = Currency;
558
559            #[inline]
560            fn $method(self, other: $other) -> Currency {
561                let big_int = BigInt::$conv_method(other * 100.0).unwrap();
562                Currency {
563                    symbol: self.symbol.clone(),
564                    coin: self.coin.clone().$method(big_int) / BigInt::from(100)
565                }
566            }
567        }
568
569        impl<'a> $module::$imp<&'a $other> for Currency {
570            type Output = Currency;
571
572            #[inline]
573            fn $method(self, other: &'a $other) -> Currency {
574                let big_int = BigInt::$conv_method(other.clone() * 100.0).unwrap();
575                Currency {
576                    symbol: self.symbol,
577                    coin: self.coin.$method(big_int) / BigInt::from(100)
578                }
579            }
580        }
581
582        impl $module::$imp<$other> for Currency {
583            type Output = Currency;
584
585            #[inline]
586            fn $method(self, other: $other) -> Currency {
587                let big_int = BigInt::$conv_method(other * 100.0).unwrap();
588                Currency {
589                    symbol: self.symbol,
590                    coin: self.coin.$method(big_int) / BigInt::from(100)
591                }
592            }
593        }
594
595        impl<'a, 'b> $module::$imp<&'b Currency> for &'a $other {
596            type Output = Currency;
597
598            #[inline]
599            fn $method(self, other: &'b Currency) -> Currency {
600                let big_int = BigInt::$conv_method(self.clone() * 100.0).unwrap();
601                Currency {
602                    symbol: other.symbol.clone(),
603                    coin: other.coin.clone().$method(big_int) / BigInt::from(100)
604                }
605            }
606        }
607
608        impl<'a> $module::$imp<Currency> for &'a $other {
609            type Output = Currency;
610
611            #[inline]
612            fn $method(self, other: Currency) -> Currency {
613                let big_int = BigInt::$conv_method(self.clone() * 100.0).unwrap();
614                Currency {
615                    symbol: other.symbol,
616                    coin: other.coin.$method(big_int) / BigInt::from(100)
617                }
618            }
619        }
620
621        impl<'a> $module::$imp<&'a Currency> for $other {
622            type Output = Currency;
623
624            #[inline]
625            fn $method(self, other: &'a Currency) -> Currency {
626                let big_int = BigInt::$conv_method(self * 100.0).unwrap();
627                Currency {
628                    symbol: other.symbol.clone(),
629                    coin: other.coin.clone().$method(big_int) / BigInt::from(100)
630                }
631            }
632        }
633
634        impl $module::$imp<Currency> for $other {
635            type Output = Currency;
636
637            #[inline]
638            fn $method(self, other: Currency) -> Currency {
639                let big_int = BigInt::$conv_method(self * 100.0).unwrap();
640                Currency {
641                    symbol: other.symbol,
642                    coin: other.coin.$method(big_int) / BigInt::from(100)
643                }
644            }
645        }
646    }
647}
648
649impl_all_trait_combinations_for_currency_conv_bigint!(ops::Mul, mul, f32, from_f32);
650impl_all_trait_combinations_for_currency_conv_bigint!(ops::Mul, mul, f64, from_f64);
651
652impl_all_trait_combinations_for_currency_conv_bigint!(ops::Div, div, f32, from_f32);
653impl_all_trait_combinations_for_currency_conv_bigint!(ops::Div, div, f64, from_f64);
654
655/// Overloads the '/' operator between two borrowed Currency objects.
656///
657/// # Panics
658/// Panics if they aren't the same type of currency, as denoted by the currency's symbol.
659impl<'a, 'b> ops::Div<&'b Currency> for &'a Currency {
660    type Output = BigInt;
661
662    fn div(self, other: &'b Currency) -> BigInt {
663        if self.symbol == other.symbol {
664            self.coin.clone() / other.coin.clone()
665        } else {
666            panic!("Cannot divide two different types of currency.");
667        }
668    }
669}
670
671/// Overloads the '/' operator between a borrowed Currency object and an owned one.
672///
673/// # Panics
674/// Panics if they aren't the same type of currency, as denoted by the currency's symbol.
675impl<'a> ops::Div<Currency> for &'a Currency {
676    type Output = BigInt;
677
678    fn div(self, other: Currency) -> BigInt {
679        if self.symbol == other.symbol {
680            self.coin.clone() / other.coin
681        } else {
682            panic!("Cannot divide two different types of currency.");
683        }
684    }
685}
686
687/// Overloads the '/' operator between an owned Currency object and a borrowed one.
688///
689/// # Panics
690/// Panics if they aren't the same type of currency, as denoted by the currency's symbol.
691impl<'a> ops::Div<&'a Currency> for Currency {
692    type Output = BigInt;
693
694    fn div(self, other: &'a Currency) -> BigInt {
695        if self.symbol == other.symbol {
696            self.coin / other.coin.clone()
697        } else {
698            panic!("Cannot divide two different types of currency.");
699        }
700    }
701}
702
703/// Overloads the '/' operator between two owned Currency objects.
704///
705/// # Panics
706/// Panics if they aren't the same type of currency, as denoted by the currency's symbol.
707impl ops::Div<Currency> for Currency {
708    type Output = BigInt;
709
710    fn div(self, other: Currency) -> BigInt {
711        if self.symbol == other.symbol {
712            self.coin / other.coin
713        } else {
714            panic!("Cannot divide two different types of currency.");
715        }
716    }
717}
718
719impl ops::Neg for Currency {
720    type Output = Currency;
721
722    fn neg(self) -> Currency {
723        Currency {
724            symbol: self.symbol,
725            coin: -self.coin
726        }
727    }
728}
729
730impl<'a> ops::Neg for &'a Currency {
731    type Output = Currency;
732
733    fn neg(self) -> Currency {
734        Currency {
735            symbol: self.symbol.clone(),
736            coin: -self.coin.clone()
737        }
738    }
739}
740
741// TODO
742// - rem
743// - signed
744
745#[cfg(test)]
746mod tests {
747    use super::Currency;
748    use num::bigint::BigInt;
749
750    #[test]
751    fn test_from_str() {
752        let expected = Currency { symbol: Some('$'), coin: BigInt::from(1210) };
753        let actual = Currency::from_str("$12.10").unwrap();
754        assert_eq!(expected, actual);
755        let actual = Currency::from_str("$12.100000").unwrap();
756        assert_eq!(expected, actual);
757        let actual = Currency::from_str("$12.1").unwrap();
758        assert_eq!(expected, actual);
759
760        let expected = Currency { symbol: None, coin: BigInt::from(1210) };
761        let actual = Currency::from_str("12.10").unwrap();
762        assert_eq!(expected, actual);
763        let actual = Currency::from_str("12.100000").unwrap();
764        assert_eq!(expected, actual);
765        let actual = Currency::from_str("12.1").unwrap();
766        assert_eq!(expected, actual);
767
768        let expected = Currency { symbol: Some('$'), coin: BigInt::from(121000) };
769        let actual = Currency::from_str("$1210").unwrap();
770        assert_eq!(expected, actual);
771        let actual = Currency::from_str("$1,210").unwrap();
772        assert_eq!(expected, actual);
773        let actual = Currency::from_str("$1,210.00").unwrap();
774        assert_eq!(expected, actual);
775        let actual = Currency::from_str("$1210.").unwrap();
776        assert_eq!(expected, actual);
777        let actual = Currency::from_str("$1,210.0").unwrap();
778        assert_eq!(expected, actual);
779        let actual = Currency::from_str("$1.210,0").unwrap();
780        assert_eq!(expected, actual);
781
782        let expected = Currency { symbol: Some('$'), coin: BigInt::from(1200099) };
783        let actual = Currency::from_str("$12,000.99").unwrap();
784        assert_eq!(expected, actual);
785
786        let expected = Currency { symbol: Some('£'), coin: BigInt::from(1200099) };
787        let actual = Currency::from_str("£12,000.99").unwrap();
788        assert_eq!(expected, actual);
789
790        let expected = Currency { symbol: Some('$'), coin: BigInt::from(-1210) };
791        let actual = Currency::from_str("-$12.10").unwrap();
792        assert_eq!(expected, actual);
793        let actual = Currency::from_str("$-12.10").unwrap();
794        assert_eq!(expected, actual);
795
796        let expected = Currency { symbol: Some('€'), coin: BigInt::from(-12000) };
797        let actual = Currency::from_str("-€120.00").unwrap();
798        assert_eq!(expected, actual);
799        let actual = Currency::from_str("-€120").unwrap();
800        assert_eq!(expected, actual);
801        let actual = Currency::from_str("-€-120.0").unwrap();
802        assert_eq!(expected, actual);
803        let actual = Currency::from_str("-€120").unwrap();
804        assert_eq!(expected, actual);
805
806        let expected = Currency { symbol: Some('€'), coin: BigInt::from(0) };
807        let actual = Currency::from_str("€0").unwrap();
808        assert_eq!(expected, actual);
809        let actual = Currency::from_str("€00.00").unwrap();
810        assert_eq!(expected, actual);
811        let actual = Currency::from_str("€.00000000").unwrap();
812        assert_eq!(expected, actual);
813        let actual = Currency::from_str("€0.0").unwrap();
814        assert_eq!(expected, actual);
815        let actual = Currency::from_str("€000,000.00").unwrap();
816        assert_eq!(expected, actual);
817        let actual = Currency::from_str("€000,000").unwrap();
818        assert_eq!(expected, actual);
819        let actual = Currency::from_str("€").unwrap();
820        assert_eq!(expected, actual);
821
822        let expected = Currency { symbol: Some('$'), coin: BigInt::from(1000) };
823        let actual = Currency::from_str("$10.0001").unwrap();
824        assert_eq!(expected, actual);
825
826        // TODO rounding
827        // let expected = Currency { symbol: Some('$'), coin: BigInt::from(1001) };
828        // let actual = Currency::from_str("$10.0099").unwrap();
829        // assert_eq!(expected, actual);
830    }
831
832    #[test]
833    fn test_eq() {
834        let a = Currency { symbol: Some('$'), coin: BigInt::from(1210) };
835        let b = Currency { symbol: Some('$'), coin: BigInt::from(1210) };
836        let c = Currency { symbol: Some('$'), coin: BigInt::from(1251) };
837
838        assert!(a == b);
839        assert!(b == b);
840        assert!(b == a);
841        assert!(a != c);
842    }
843
844    #[test]
845    fn test_ord() {
846        use std::cmp::Ordering;
847
848        let a = Currency { symbol: Some('$'), coin: BigInt::from(1210) };
849        let b = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
850        let c = Currency { symbol: Some('$'), coin: BigInt::from(1311) };
851        let d = Currency { symbol: Some('$'), coin: BigInt::from(1210) };
852
853        assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
854        assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
855        assert_eq!(a.partial_cmp(&d), Some(Ordering::Equal));
856        assert_eq!(c.partial_cmp(&a), Some(Ordering::Greater));
857
858        assert!(a < b);
859        assert!(a < c);
860        assert!(a <= a);
861        assert!(a <= c);
862        assert!(b > a);
863        assert!(c > a);
864        assert!(a >= a);
865        assert!(c >= a);
866    }
867
868    #[test]
869    fn test_add() {
870        let a = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
871        let b = Currency { symbol: Some('$'), coin: BigInt::from(1311) };
872        let expected_sum = Currency { symbol: Some('$'), coin: BigInt::from(2522) };
873        let actual_sum = a + b;
874        assert_eq!(expected_sum, actual_sum);
875    }
876
877    #[test]
878    fn test_add_commutative() {
879        let a = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
880        let b = Currency { symbol: Some('$'), coin: BigInt::from(1311) };
881        assert!(&a + &b == &b + &a);
882    }
883
884    #[test]
885    fn test_sub() {
886        let a = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
887        let b = Currency { symbol: Some('$'), coin: BigInt::from(1311) };
888
889        let expected = Currency { symbol: Some('$'), coin: BigInt::from(-100) };
890        let actual = &a - &b;
891        assert_eq!(expected, actual);
892
893        let expected = Currency { symbol: Some('$'), coin: BigInt::from(100) };
894        let actual = b - a;
895        assert_eq!(expected, actual);
896    }
897
898    #[test]
899    fn test_mul() {
900        let a = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
901        let f = 0.97;
902        let expected = Currency { symbol: Some('$'), coin: BigInt::from(1174) };
903        let actual = a * f;
904        assert_eq!(expected, actual);
905    }
906
907    #[test]
908    fn test_mul_commutative() {
909        let a = Currency { symbol: Some('$'), coin: BigInt::from(1211) };
910        let f = 0.97;
911        assert_eq!(&a * &f, &f * &a);
912    }
913
914    #[test]
915    fn test_div() {
916        let a = Currency { symbol: Some('$'), coin: BigInt::from(2500) };
917        let b = Currency { symbol: Some('$'), coin: BigInt::from(500) };
918        let expected = BigInt::from(5);
919        let actual = a / b;
920        assert_eq!(expected, actual);
921
922        let a = Currency { symbol: Some('$'), coin: BigInt::from(3248) };
923        let b = Currency { symbol: Some('$'), coin: BigInt::from(888) };
924        let expected = BigInt::from(3);
925        let actual = a / b;
926        assert_eq!(expected, actual);
927    }
928
929    #[test]
930    fn test_neg() {
931        let c = Currency { symbol: Some('$'), coin: BigInt::from(3248) };
932        let expected = Currency { symbol: Some('$'), coin: BigInt::from(-3248) };
933        let actual = -c;
934        assert_eq!(expected, actual);
935
936        let c = Currency { symbol: Some('$'), coin: BigInt::from(-3248) };
937        let expected = Currency { symbol: Some('$'), coin: BigInt::from(3248) };
938        let actual = -c;
939        assert_eq!(expected, actual);
940
941        let c = Currency { symbol: Some('$'), coin: BigInt::from(0) };
942        let expected = Currency { symbol: Some('$'), coin: BigInt::from(0) };
943        let actual = -c;
944        assert_eq!(expected, actual);
945    }
946
947    #[test]
948    fn test_convert() {
949        let dollars = Currency::from_str("$12.50").unwrap();
950        let euro_conversion_rate = 0.89;
951        let euros = dollars.convert(euro_conversion_rate, '€');
952        let expected = Currency { symbol: Some('€'), coin: BigInt::from(1112) };
953        assert_eq!(expected, euros);
954    }
955
956    #[test]
957    fn test_display() {
958        use num::traits::Num;
959
960        assert_eq!(
961            Currency { symbol: Some('$'), coin: BigInt::from(0) }.to_string(),
962            "$0.00"
963        );
964
965        assert_eq!(
966            Currency { symbol: Some('$'), coin: BigInt::from(-1) }.to_string(),
967            "-$0.01"
968        );
969
970        assert_eq!(
971            Currency { symbol: None, coin: BigInt::from(11) }.to_string(),
972            "0.11"
973        );
974
975        assert_eq!(
976            Currency { symbol: None, coin: BigInt::from(1210) }.to_string(),
977            "12.10"
978        );
979
980        assert_eq!(
981            Currency { symbol: Some('$'), coin: BigInt::from(1210) }.to_string(),
982            "$12.10"
983        );
984
985        assert_eq!(
986            Currency { symbol: Some('£'), coin: BigInt::from(100010) }.to_string(),
987            "£1,000.10"
988        );
989
990        assert_eq!(
991            Currency {
992                symbol: Some('$'),
993                coin: BigInt::from_str_radix("123456789001", 10).unwrap()
994            }.to_string(),
995            "$1,234,567,890.01"
996        );
997
998        assert_eq!(
999            Currency {
1000                symbol: Some('$'),
1001                coin: BigInt::from_str_radix("-123456789001", 10).unwrap()
1002            }.to_string(),
1003            "-$1,234,567,890.01"
1004        );
1005
1006        let ccy: Currency = "100".parse().unwrap();
1007        assert_eq!(ccy.to_string(), "100.00");
1008
1009        let ccy: Currency = "1200".parse().unwrap();
1010        assert_eq!(ccy.to_string(), "1,200.00");
1011    }
1012
1013    #[test]
1014    fn test_foreign_display() {
1015        assert_eq!(
1016            format!("{:e}", Currency { symbol: Some('£'), coin: BigInt::from(100000) }),
1017            "£1.000,00"
1018        );
1019
1020        assert_eq!(
1021            format!("{:e}", Currency { symbol: Some('£'), coin: BigInt::from(123400101) }),
1022            "£1.234.001,01"
1023        );
1024    }
1025}