Functional Weave
Code in Rust

fleet.cost-per-mile@1.0.0

impl/rust.rs

2,840 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_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::money_sum::sum_money;  ← from money.sum ^1.0.0 · built alongside by fune

fn tenths(amount: &Money, miles: i64) -> i64 {
    round_div(amount.minor * 10, miles, "half-up")
}

fn display(value: i64) -> String {
    let sign = if value < 0 { "-" } else { "" };
    let magnitude = value.unsigned_abs();
    format!("{}{}.{}", sign, magnitude / 10, magnitude % 10)
}

/// Whole-life cost per mile, in tenths of a penny, the unit fleet reports use.
///
/// Each part is rounded on its own for the breakdown, and the total is rounded
/// from the total cost: adding up the rounded parts drifts by a tenth or two,
/// which is why the total is not their sum.
///
/// # Panics
/// Panics if miles is not above zero or the costs mix currencies.
pub fn cost_per_mile(costs: &WholeLifeCosts, miles: i64) -> CostPerMile {
    if miles <= 0 {
        panic!("miles must be a whole number above zero, received {}", miles);
    }
    let total_cost = sum_money(
        &[
            costs.depreciation.clone(),
            costs.fuel.clone(),
            costs.maintenance.clone(),
            costs.insurance.clone(),
            costs.tax.clone(),
        ],
        &costs.depreciation.currency,
    );
    let total_tenths = tenths(&total_cost, miles);
    CostPerMile {
        depreciation_tenths: tenths(&costs.depreciation, miles),
        fuel_tenths: tenths(&costs.fuel, miles),
        maintenance_tenths: tenths(&costs.maintenance, miles),
        insurance_tenths: tenths(&costs.insurance, miles),
        tax_tenths: tenths(&costs.tax, miles),
        total_tenths,
        per_mile: display(total_tenths),
        total_cost,
    }
}

pub fn cost_per_mile_to_value(c: &CostPerMile) -> Value {
    Value::obj(vec![
        ("totalCost", money_to_value(&c.total_cost)),
        ("depreciationTenths", Value::Int(c.depreciation_tenths)),
        ("fuelTenths", Value::Int(c.fuel_tenths)),
        ("maintenanceTenths", Value::Int(c.maintenance_tenths)),
        ("insuranceTenths", Value::Int(c.insurance_tenths)),
        ("taxTenths", Value::Int(c.tax_tenths)),
        ("totalTenths", Value::Int(c.total_tenths)),
        ("perMile", Value::str(&c.per_mile)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    if let Value::Float(f) = &args[1] {
        panic!("miles must be a whole number above zero, received {}", f);
    }
    let c = &args[0];
    let costs = WholeLifeCosts {
        depreciation: money_from_value(c.get("depreciation")),
        fuel: money_from_value(c.get("fuel")),
        maintenance: money_from_value(c.get("maintenance")),
        insurance: money_from_value(c.get("insurance")),
        tax: money_from_value(c.get("tax")),
    };
    cost_per_mile_to_value(&cost_per_mile(&costs, args[1].as_i64()))
}