claude/
lib.rs

1//! This provides a simple type to calculate with monetary values.
2
3#![deny(
4    missing_docs,
5    trivial_casts,
6    trivial_numeric_casts,
7    unstable_features,
8    unused_import_braces,
9    unused_qualifications
10)]
11
12#[cfg(feature = "parsing")]
13extern crate regex;
14#[cfg(feature = "serialization")]
15extern crate serde;
16#[cfg(feature = "serialization")]
17extern crate serde_json;
18#[cfg(feature = "serialization")]
19#[macro_use]
20extern crate serde_derive;
21
22use std::default::Default;
23
24/// Symbol of a currency
25pub type Symbol = char;
26
27pub mod display;
28pub mod math;
29
30/// Represents currency through an optional symbol and amount of coin.
31///
32/// Each 100 coins results in a banknote. (100 is formatted as 1.00)
33/// The currency will be formatted as such: `Currency(Some('$'), 432)` ==> "$4.32"
34#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq)]
35#[cfg_attr(feature = "serialization", derive(Serialize))]
36pub struct Currency {
37    /// Currency symbol
38    ///
39    /// pick any of `€`, `£`, `$`, `¥` etc...
40    pub symbol: Option<Symbol>,
41    /// value in the smallest possible unit
42    pub value: i64,
43}
44
45impl Currency {
46    /// Creates a blank Currency as Currency(None, 0)
47    ///
48    /// # Examples
49    /// ```
50    /// # use claude::Currency;
51    /// let mut c = Currency::new();
52    /// ```
53    #[inline]
54    pub fn new() -> Currency {
55        Currency {
56            symbol: None,
57            value: 0,
58        }
59    }
60
61    /// Initialize from `i64`
62    pub fn from_value(value: i64) -> Currency {
63        Currency {
64            symbol: None,
65            value,
66        }
67    }
68
69    /// Uses a Regular Expression to parse a string literal (&str) and attempts to turn it into a
70    /// currency. Returns `Some(Currency)` on a successful conversion, otherwise `None`.
71    ///
72    /// If the currency is intended to be a negative amount, ensure the '-' is the first symbolacter
73    /// in the string.
74    /// The Regex recognizes European notation (€1,00)
75    ///
76    /// # Examples
77    /// ```
78    /// use claude::Currency;
79    ///
80    /// assert!(Currency::from_string("$4.32")  == Some(Currency{symbol: Some('$'), value:   432}));
81    /// assert!(Currency::from_string("-$4.32") == Some(Currency{symbol: Some('$'), value:  -432}));
82    /// assert!(Currency::from_string("424.44") == Some(Currency{symbol: None,      value: 42444}));
83    /// assert!(Currency::from_string("£12,00") == Some(Currency{symbol: Some('£'), value:  1200}));
84    /// assert!(Currency::from_string("¥12")    == Some(Currency{symbol: Some('¥'), value:  1200}));
85    /// ```
86    #[cfg(feature = "parsing")]
87    pub fn from_string(s: &str) -> Option<Currency> {
88        use regex::Regex;
89
90        // Shadow s with a trimmed version
91        let s = s.trim();
92        let re =
93            Regex::new(r"^(?:\b|(-)?)(\p{Currency_Symbol})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$")
94            .unwrap();
95
96        // Used to negate the final result if the regex matches a negative
97        let mut multiplier = 1;
98        let mut sign: Option<Symbol> = None;
99        let coin_str: String;
100
101        match re.captures(s) {
102            None => return None,
103            Some(caps) => {
104                if caps.get(1).is_some() {
105                    multiplier = -1;
106                }
107
108                if caps.get(2).is_some() {
109                    if multiplier < 0 {
110                        sign = Some(s.chars().skip(1).next().unwrap());
111                    } else {
112                        sign = Some(s.chars().next().unwrap());
113                    }
114                }
115                coin_str = caps
116                    .get(3)
117                    .map(|m| m.as_str())
118                    .unwrap()
119                    .replace(".", "")
120                    .replace(",", "")
121                    + caps.get(4).map(|m| m.as_str()).unwrap_or("00");
122            }
123        }
124
125        if let Ok(coin) = coin_str.parse::<i64>() {
126            return Some(Currency {
127                symbol: sign,
128                value: multiplier * coin,
129            });
130        }
131        None
132    }
133
134    /// Returns an object that implements `Display` for different methods of printing currency.
135    pub fn postfix(&self) -> Postfix<'_> {
136        Postfix { money: self }
137    }
138
139    /// Returns an object that implements `Display` for different methods of printing currency.
140    pub fn prefix(&self) -> Prefix<'_> {
141        Prefix { money: self }
142    }
143
144    /// Returns the value as float
145    ///
146    /// # Warning, do not use this for calculation, this is for displaying only!
147    pub fn as_float(&self) -> f64 {
148        self.value as f64 / 100.0
149    }
150
151    /// Returns the inner value
152    pub fn value(&self) -> i64 {
153        self.value
154    }
155
156    /// Returns the inner symbol
157    pub fn symbol(&self) -> Option<Symbol> {
158        self.symbol
159    }
160}
161
162use std::ops::Deref;
163/// Required for `DerefMut`
164impl Deref for Currency {
165    type Target = i64;
166    fn deref(&self) -> &i64 {
167        &self.value
168    }
169}
170
171impl From<i64> for Currency {
172    /// converts from a  `i64`
173    fn from(value: i64) -> Currency {
174        Currency::from_value(value)
175    }
176}
177
178impl From<(Symbol, i64)> for Currency {
179    /// converts from a tuple of `symbol` and `i64`
180    fn from(tpl: (Symbol, i64)) -> Currency {
181        let (symbol, cents) = tpl;
182        Currency {
183            symbol: Some(symbol),
184            value: cents,
185        }
186    }
187}
188
189impl From<(i64, Symbol)> for Currency {
190    /// converts from a tuple of `i64` and `symbol`
191    fn from(tpl: (i64, Symbol)) -> Currency {
192        let (cents, symbol) = tpl;
193        Currency {
194            symbol: Some(symbol),
195            value: cents,
196        }
197    }
198}
199
200impl Default for Currency {
201    fn default() -> Self {
202        Currency {
203            symbol: None,
204            value: 0,
205        }
206    }
207}
208
209/// Implements `Display` with the currency symbol at the end.
210pub struct Postfix<'a> {
211    money: &'a Currency,
212}
213
214/// Implements `Display` with the currency symbol at the front.
215pub struct Prefix<'a> {
216    money: &'a Currency,
217}