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
#![crate_type = "lib"] #![crate_name = "argent"] use std::fmt::{Display, Formatter, Result}; use std::ops::{Add, Sub}; #[derive(Debug, Clone, PartialEq)] pub struct Currency { pub iso_code: Option<String>, pub iso_numeric: Option<i16>, pub name: Option<String>, pub symbol: String, pub minor_ratio: i32, pub symbol_first: bool, pub decimal_mark: char, pub group_mark: char } impl Currency { pub fn simple(symbol: &'static str) -> Currency { Currency { symbol: symbol.into(), ..Default::default() } } } #[derive(Debug, Clone)] pub struct Amount<'a> { pub currency: &'a Currency, pub quantity: i64 } impl <'a> Amount<'a> { pub fn of(currency: &'a Currency, quantity: i64) -> Amount<'a> { Amount{ currency: currency, quantity: quantity } } } impl Default for Currency { fn default() -> Currency { Currency { iso_code: None, iso_numeric: None, name: None, symbol: "".to_string(), minor_ratio: 100, symbol_first: true, decimal_mark: '.', group_mark: ',' } } } impl <'a> Display for Amount<'a> { fn fmt(&self, f: &mut Formatter) -> Result { let currency: &Currency = &self.currency; let decimal_count = (currency.minor_ratio as f32).log(10.0) as usize; let raw = format!("{:.*}", decimal_count, (self.quantity as f32 / currency.minor_ratio as f32)); let parts: Vec<&str> = raw.as_str().split(currency.decimal_mark).collect(); if let (Some(significand), Some(mantissa)) = (parts.get(0), parts.get(1)) { let mut groups = (RevChunks { target: significand, size: 3 }).collect::<Vec<_>>(); groups.reverse(); let whole = groups.join(¤cy.group_mark.to_string()); write!(f, "{}{}{}{}", currency.symbol, whole, currency.decimal_mark, mantissa) } else { write!(f, "{}NaN", currency.symbol) } } } impl <'a> Add for Amount<'a> { type Output = Amount<'a>; fn add(self, rhs: Amount) -> Amount<'a> { if rhs.currency == self.currency { Amount { currency: self.currency, quantity: self.quantity + rhs.quantity } } else { self } } } impl <'a> Sub for Amount<'a> { type Output = Amount<'a>; fn sub(self, rhs: Amount) -> Amount<'a> { if rhs.currency == self.currency { Amount { currency: self.currency, quantity: self.quantity - rhs.quantity } } else { self } } } struct RevChunks<'a> { target: &'a str, size: usize } impl<'a> Iterator for RevChunks<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { if self.target.is_empty() { return None; } let mut end = 0; for (n, (i, _)) in self.target.char_indices().rev().enumerate() { if n == self.size { break; } end = i; } let (a, b) = self.target.split_at(end); self.target = a; Some(b) } }