Functional Weave
Code in Rust

charity.fundraising-ratio@1.0.0

impl/rust.rs

2,743 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune
use super::money_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::money_currency_digits::currency_digits;  ← from money.currency-digits ^1.0.0 · built alongside by fune

fn check(name: &str, currency: &str, amount: &Money) {
    if amount.currency != currency {
        panic!("currency mismatch: {} and {}", amount.currency, currency);
    }
    if amount.minor < 0 {
        panic!("{} must not be negative, received {}", name, amount.minor);
    }
}

/// Cost to raise £1 and the share of spending that went on the charity's
/// purposes. Each ratio is divided once from the integer amounts and rounded
/// half up.
///
/// # Panics
/// Panics on negative amounts, mixed currencies, zero funds raised or total
/// expenditure, or charitable spend above total expenditure.
pub fn fundraising_ratios(
    fundraising_costs: &Money,
    funds_raised: &Money,
    charitable_spend: &Money,
    total_expenditure: &Money,
) -> FundraisingRatios {
    let currency = fundraising_costs.currency.as_str();
    check("fundraisingCosts", currency, fundraising_costs);
    check("fundsRaised", currency, funds_raised);
    check("charitableSpend", currency, charitable_spend);
    check("totalExpenditure", currency, total_expenditure);
    if funds_raised.minor == 0 {
        panic!("fundsRaised must be positive to work out a cost to raise");
    }
    if total_expenditure.minor == 0 {
        panic!("totalExpenditure must be positive to work out a charitable spend ratio");
    }
    if charitable_spend.minor > total_expenditure.minor {
        panic!(
            "charitableSpend of {} is more than totalExpenditure of {}",
            charitable_spend.minor, total_expenditure.minor
        );
    }
    let unit = 10i64.pow(currency_digits(currency) as u32);
    FundraisingRatios {
        cost_to_raise_one: money(round_div(fundraising_costs.minor * unit, funds_raised.minor, "half-up"), currency),
        cost_ratio_basis_points: round_div(fundraising_costs.minor * 10000, funds_raised.minor, "half-up"),
        charitable_spend_basis_points: round_div(charitable_spend.minor * 10000, total_expenditure.minor, "half-up"),
    }
}

pub fn fundraising_ratios_to_value(r: &FundraisingRatios) -> Value {
    Value::obj(vec![
        ("costToRaiseOne", money_to_value(&r.cost_to_raise_one)),
        ("costRatioBasisPoints", Value::Int(r.cost_ratio_basis_points)),
        ("charitableSpendBasisPoints", Value::Int(r.charitable_spend_basis_points)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    fundraising_ratios_to_value(&fundraising_ratios(
        &money_from_value(&args[0]),
        &money_from_value(&args[1]),
        &money_from_value(&args[2]),
        &money_from_value(&args[3]),
    ))
}