use super::funejson::Value; use super::math_rational::{multiply_rational, rational, rational_from_value, rational_to_integer}; use super::money_amount::{money, money_from_value, money_to_value, Money}; use super::money_currency_digits::currency_digits; /// Convert `amount` into the rate's quote currency. /// /// Exact up to one final rounding into the quote currency's minor unit; the /// rate is a fraction in major units and each currency's decimal places are /// looked up rather than assumed to be two. /// /// # Panics /// Panics if the amount is not in the rate's base currency, the rate is not /// positive, a currency has no ISO 4217 minor unit, or the exact value /// overflows `math.rational`'s range. pub fn convert_money(amount: &Money, rate: &ExchangeRate, mode: &str) -> Money { if amount.currency != rate.base { panic!( "exchange rate converts {} to {}, not {}", rate.base, rate.quote, amount.currency ); } let r = rational(rate.rate.numerator, rate.rate.denominator); if r.numerator <= 0 { panic!("exchange rate must be positive"); } let from_digits = currency_digits(&rate.base) as u32; let to_digits = currency_digits(&rate.quote) as u32; // minor units of quote per minor unit of base: rate x 10^to_digits / 10^from_digits let scale = rational(10_i64.pow(to_digits), 10_i64.pow(from_digits)); let per_minor = multiply_rational(&r, &scale); let exact = multiply_rational(&rational(amount.minor, 1), &per_minor); money(rational_to_integer(&exact, mode), &rate.quote) } pub fn exchange_rate_from_value(v: &Value) -> ExchangeRate { ExchangeRate { base: v.get("base").as_str().to_string(), quote: v.get("quote").as_str().to_string(), rate: rational_from_value(v.get("rate")), } } pub fn fune_vector(args: &[Value]) -> Value { money_to_value(&convert_money( &money_from_value(&args[0]), &exchange_rate_from_value(&args[1]), args[2].as_str(), )) }