use super::funejson::Value; fn is_currency_code(code: &str) -> bool { code.len() == 3 && code.chars().all(|c| c.is_ascii_uppercase()) } /// # Panics /// Panics if `currency` is not an uppercase ISO 4217 alphabetic code. pub fn money(minor: i64, currency: &str) -> Money { if !is_currency_code(currency) { panic!("\"{}\" is not an uppercase ISO 4217 currency code", currency); } Money { minor, currency: currency.to_string(), } } /// Guard against the silent nonsense of adding GBP to EUR. /// /// # Panics /// Panics if the two amounts are in different currencies. pub fn assert_same_currency(a: &Money, b: &Money) { if a.currency != b.currency { panic!("currency mismatch: {} and {}", a.currency, b.currency); } } pub fn zero(currency: &str) -> Money { money(0, currency) } pub fn money_to_value(m: &Money) -> Value { Value::obj(vec![ ("minor", Value::Int(m.minor)), ("currency", Value::str(&m.currency)), ]) } pub fn money_from_value(v: &Value) -> Money { Money { minor: v.get("minor").as_i64(), currency: v.get("currency").as_str().to_string(), } } pub fn fune_vector(args: &[Value]) -> Value { // Refuse what the typed signature cannot hold, with the wording TypeScript // and Python use, rather than let the conversion below quietly change it. if let Value::Float(f) = args[0] { if f.fract() != 0.0 { panic!("money() takes integer minor units, received {}", f); } } money_to_value(&money(args[0].as_i64(), args[1].as_str())) }