/****************************************************************************** * This program test the Currency class. * * Copyright © 2021 Richard Lesh. All rights reserved. *****************************************************************************/ #![allow(dead_code)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #[macro_use] extern crate lazy_static; #[macro_use] mod utils; use regex::Regex; use std::collections::HashMap; use std::sync::atomic::AtomicU32; lazy_static! { static ref mut Currency_CONVERSION_RATES:HashMap; static ref mut Currency_CURRENCY_SYMBOLS:HashMap = [ ("AUD", "$\u{A0}"), ("CAD", "$\u{A0}"), ("CNY", "¥\u{A0}"), ("EUR", "\u{A0}€"), ("GBP", "£\u{A0}"), ("INR", "₹\u{A0}"), ("JPY", "¥\u{A0}"), ("MXN", "$\u{A0}"), ("RUB", "\u{A0}₽"), ("USD", "$"), ("XBT", "\u{A0}BTC") ].into(); static ref mut Currency_CURRENCY_SEPARATORS:HashMap = [ ("AUD", ",."), ("CAD", ",."), ("CNY", ",."), ("EUR", ".,"), ("GBP", ",."), ("INR", ",."), ("JPY", ",."), ("MXN", ",."), ("RUB", ".,"), ("USD", ",."), ("XBT", ",.") ].into(); static ref mut Currency_CURRENCY_CODES:Vec; } static Currency_NONBREAKING_SPACE:AtomicU32 = AtomicU32::new(0xA0); struct Currency { // Internal representation is in USD value:f64 = 0.0f64, // List of legal currency codes. // Constructor // getter // string conversion } impl Currency { fn new(value:f64, currency_code:&str) -> Currency { let mut conversion_rate:f64 = 1.0f64; if Currency_CONVERSION_RATES.contains_key(currency_code) { conversion_rate = Currency_CONVERSION_RATES.get(currency_code).unwrap(); } else { return Err(utils::CustomError::RuntimeError(String::from("Illegal currency code: ") + currency_code)); } Currency { value: value / conversion_rate } return Ok(()); } fn get(&mut self, currency_code:&str) -> Result { let mut conversion_rate:f64 = 1.0f64; if Currency_CONVERSION_RATES.contains_key(currency_code) { conversion_rate = Currency_CONVERSION_RATES.get(currency_code).unwrap(); } else { return Err(utils::CustomError::RuntimeError(String::from("Illegal currency code: ") + currency_code)); } return Ok(self.value * conversion_rate); } fn add_separators(&mut self, d:f64, sep:&str) -> String { let s:String = format!("{0:.2}", d); let comma:char = sep[0]; let period:char = sep[1]; let parts:Vec = utils::regex_split(Regex::new(r"\.").unwrap(), s); let mut dollars:String = parts[0] + period; let mut matches:Vec = Vec::new(); let pattern:Regex = Regex::new(r"(\d+?)(\d{3,3}[,.].*)").unwrap(); matches = utils::regex_find_first(pattern, dollars); while matches.len() != 0 { dollars = matches[1] + comma + &matches[2]; matches = utils::regex_find_first(pattern, dollars); } return dollars + &parts[1]; } fn get_string(&mut self, currency_code:&str) -> Result { let mut currency_symbol:&'static str = "$"; let mut currency_separator:&'static str = ",."; if Currency_CURRENCY_SYMBOLS.contains_key(currency_code) { currency_symbol = &(Currency_CURRENCY_SYMBOLS.get(currency_code).unwrap()); currency_separator = &(Currency_CURRENCY_SEPARATORS.get(currency_code).unwrap()); } let mut format_str:&'static str = ""; // If non-breaking space is first in symbol then it goes at end if strlen(currency_symbol) > 1 && find(currency_symbol,Currency_NONBREAKING_SPACE) == 0 { format_str = "{1:s}{0:s}"; } else { format_str = "{0:s}{1:s}"; } let currency_value:String = self.add_separators(self.get(currency_code), currency_separator); return Ok(format!(format_str, currency_symbol, currency_value)); } pub fn set_conversion_rates(rates:&HashMap) -> () { Currency_CONVERSION_RATES = rates; Currency_CURRENCY_CODES = Currency_CONVERSION_RATES.keys().cloned().collect(); } } fn main() { let mut todays_rates:HashMap = [ ("AUD", 1.26f64), ("CAD", 1.26f64), ("CNY", 6.47f64), ("EUR", 0.82f64), ("GBP", 0.71f64), ("INR", 72.49f64), ("JPY", 105.07f64), ("MXN", 20.68f64), ("RUB", 74.29f64), ("USD", 1.0f64), ("XBT", 0.00001900f64) ].into(); Currency::set_conversion_rates(todays_rates); println!("Today's currency rates set."); match (|| -> Result<(), utils::CustomError>{ let mut c:Currency = Currency::new(1.0f64, "USD"); for code in Currency::CURRENCY_CODES.iter() { println!("{0} to {1}: {2} = {3}", "USD", code, c.get_string("USD"), c.get_string(&code)); } println!(""); c = Currency::new(1234.56f64, "EUR"); for code in Currency::CURRENCY_CODES.iter() { println!("{0} to {1}: {2} = {3}", "USD", code, c.get_string("USD"), c.get_string(&code)); } return Ok(()); })() { Ok(()) => {}, Err(ex) => { match ex { utils::CustomError::RuntimeError(ex) => { println!("Error: {}", format!("{}", ex)); } } } }; }