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}