use super::funejson::Value; use super::money_amount::{money_from_value, Money}; use super::text_integer_to_words::integer_to_words; /// English unit names: (code, major one, major many, minor one, minor many). /// All three currencies have two decimal places, so the split below is by 100. /// A currency is not added here without its real unit names: they cannot be /// derived from a code. const UNITS: [(&str, &str, &str, &str, &str); 3] = [ ("GBP", "pound", "pounds", "penny", "pence"), ("EUR", "euro", "euros", "cent", "cents"), ("USD", "dollar", "dollars", "cent", "cents"), ]; /// Write a money amount in words, cheque style: 123456 GBP minor units is /// "one thousand two hundred and thirty-four pounds and fifty-six pence". /// /// # Panics /// Panics on a currency without listed unit names, or an out-of-range amount. pub fn amount_in_words(amount: &Money) -> String { let units = match UNITS.iter().find(|u| u.0 == amount.currency) { Some(found) => found, None => panic!("no words for currency \"{}\"", amount.currency), }; // Validate the whole amount first, so an out-of-range value fails with the // same message it would as a plain number. integer_to_words(amount.minor); let magnitude = amount.minor.abs(); let major = magnitude / 100; let minor = magnitude % 100; let mut parts: Vec = Vec::new(); if major > 0 || minor == 0 { let name = if major == 1 { units.1 } else { units.2 }; parts.push(format!("{} {}", integer_to_words(major), name)); } if minor > 0 { let name = if minor == 1 { units.3 } else { units.4 }; parts.push(format!("{} {}", integer_to_words(minor), name)); } let body = parts.join(" and "); if amount.minor < 0 { format!("minus {}", body) } else { body } } pub fn fune_vector(args: &[Value]) -> Value { Value::Str(amount_in_words(&money_from_value(&args[0]))) }