1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! This provides a simple type to calculate with monetary values.

#![deny(
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    unused_qualifications
    )]

#[cfg(feature="parsing")] extern crate regex;
#[cfg(feature="serialization")] extern crate serde;
#[cfg(feature="serialization")] extern crate serde_json;
#[cfg(feature="serialization")] #[macro_use] extern crate serde_derive;

use std::default::Default;

/// Symbol of a currency
pub type Symbol = char;

pub mod display;
pub mod math;

/// Represents currency through an optional symbol and amount of coin.
///
/// Each 100 coins results in a banknote. (100 is formatted as 1.00)
/// The currency will be formatted as such: `Currency(Some('$'), 432)` ==> "$4.32"
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq)]
#[cfg_attr(feature = "serialization", derive(Serialize))]
pub struct Currency {
    /// Currency symbol
    ///
    /// pick any of `€`, `£`, `$`, `¥` etc...
    pub symbol: Option<Symbol>,
    /// value in the smallest possible unit
    pub value: i64
}

impl Currency {
    /// Creates a blank Currency as Currency(None, 0)
    ///
    /// # Examples
    /// ```
    /// # use claude::Currency;
    /// let mut c = Currency::new();
    /// ```
    #[inline]
    pub fn new() -> Currency {
        Currency{symbol: None, value: 0}
    }

    /// Initialize from `i64`
    pub fn from_value(value: i64) -> Currency {
        Currency{symbol: None, value: value}
    }

    /// Uses a Regular Expression to parse a string literal (&str) and attempts to turn it into a
    /// currency. Returns `Some(Currency)` on a successful conversion, otherwise `None`.
    ///
    /// If the currency is intended to be a negative amount, ensure the '-' is the first symbolacter
    /// in the string.
    /// The Regex recognizes European notation (€1,00)
    ///
    /// # Examples
    /// ```
    /// use claude::Currency;
    ///
    /// assert!(Currency::from_string("$4.32")  == Some(Currency{symbol: Some('$'), value:   432}));
    /// assert!(Currency::from_string("-$4.32") == Some(Currency{symbol: Some('$'), value:  -432}));
    /// assert!(Currency::from_string("424.44") == Some(Currency{symbol: None,      value: 42444}));
    /// assert!(Currency::from_string("£12,00") == Some(Currency{symbol: Some('£'), value:  1200}));
    /// assert!(Currency::from_string("¥12")    == Some(Currency{symbol: Some('¥'), value:  1200}));
    /// ```
    #[cfg(feature="parsing")]
    pub fn from_string(s: &str) -> Option<Currency> {
        use regex::Regex;

        // Shadow s with a trimmed version
        let s = s.trim();
        let re =
            Regex::new(r"(?:\b|(-)?)(\p{Sc})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b")
            .unwrap();

        if !re.is_match(s) {
            return None;
        }

        // Used to negate the final result if the regex matches a negative
        let mut multiplier = 1;
        let mut sign: Option<Symbol> = None;
        let mut coin_str: String = "".to_string();

        // If anyone's looking at this and knows how to do this without a loop, fork this.
        for cap in re.captures_iter(s) {
            if cap.at(0).unwrap_or("") != s {
                return None;
            }

            if cap.at(1).is_some() {
                multiplier = -1;
            }

            if cap.at(2).is_some() {
                if multiplier < 0 {
                    sign = Some(s.chars().skip(1).next().unwrap());
                } else {
                    sign = Some(s.chars().next().unwrap());
                }
            }
            coin_str = cap.at(3).unwrap().replace(".", "").replace(",", "")
                     + cap.at(4).unwrap_or("00");

            break;
        }

        if let Ok(coin) = coin_str.parse::<i64>(){
            return Some(Currency{symbol: sign, value: multiplier * coin});
        }
        None
    }

    /// Returns an object that implements `Display` for different methods of printing currency.
    pub fn postfix(&self) -> Postfix {
        Postfix{money: self}
    }

    /// Returns an object that implements `Display` for different methods of printing currency.
    pub fn prefix(&self) -> Prefix {
        Prefix{money: self}
    }

    /// Returns the value as float
    ///
    /// # Warning, do not use this for calculation, this is for displaying only!
    pub fn as_float(&self) -> f64{
        self.value as f64 / 100.0
    }

    /// Returns the inner value
    pub fn value(&self) -> i64{
        self.value
    }

    /// Returns the inner symbol
    pub fn symbol(&self) -> Option<Symbol>{
        self.symbol
    }
}

use std::ops::Deref;
/// Required for `DerefMut`
impl Deref for Currency{
    type Target = i64;
    fn deref(&self) -> &i64{
        &self.value
    }
}

impl From<i64> for Currency {
    /// converts from a  `i64`
    fn from(value:i64) -> Currency {
        Currency::from_value(value)
    }
}

impl From<(Symbol,i64)> for Currency {
    /// converts from a tuple of `symbol` and `i64`
    fn from(tpl:(Symbol, i64)) -> Currency {
        let (symbol, cents) = tpl;
        Currency{symbol: Some(symbol), value: cents}
    }
}

impl From<(i64,Symbol)> for Currency {
    /// converts from a tuple of `i64` and `symbol`
    fn from(tpl:(i64, Symbol)) -> Currency {
        let (cents, symbol) = tpl;
        Currency{symbol: Some(symbol), value: cents}
    }
}

impl Default for Currency{
    fn default() -> Self{
        Currency{symbol: None, value: 0}
    }
}

/// Implements `Display` with the currency symbol at the end.
pub struct Postfix<'a>{
    money: &'a Currency
}

/// Implements `Display` with the currency symbol at the front.
pub struct Prefix<'a>{
    money: &'a Currency
}