Functional Weave
Code in Python

property.rental-yield@1.0.1

impl/rust.rs

2,324 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

// Above this, amount * 10000 is no longer exact as a JavaScript number; the
// same limit applies here so every language refuses the same inputs.
const MAX_MINOR: i64 = 90_000_000_000;

/// Gross and net rental yield in basis points. Each is rounded once, half-up,
/// from the exact ratio, never from a rounded percentage.
///
/// # Panics
/// Panics on mismatched currencies, a value that is not positive, a negative
/// rent or cost, or an amount beyond the exact range.
pub fn rental_yield(annual_rent: &Money, annual_costs: &Money, property_value: &Money) -> RentalYield {
    let currency = property_value.currency.as_str();
    for other in [annual_rent, annual_costs] {
        if other.currency != currency {
            panic!("currency mismatch: {} and {}", other.currency, currency);
        }
    }
    if property_value.minor <= 0 {
        panic!("propertyValue must be greater than zero, received {}", property_value.minor);
    }
    if annual_rent.minor < 0 {
        panic!("annualRent must not be negative, received {}", annual_rent.minor);
    }
    if annual_costs.minor < 0 {
        panic!("annualCosts must not be negative, received {}", annual_costs.minor);
    }
    for m in [annual_rent.minor, annual_costs.minor, property_value.minor] {
        if m > MAX_MINOR {
            panic!("amounts must be at most {} minor units", MAX_MINOR);
        }
    }
    let net = annual_rent.minor - annual_costs.minor;
    RentalYield {
        gross_basis_points: round_div(annual_rent.minor * 10000, property_value.minor, "half-up"),
        net_basis_points: round_div(net * 10000, property_value.minor, "half-up"),
        net_income: money(net, currency),
    }
}

pub fn rental_yield_to_value(result: &RentalYield) -> Value {
    Value::obj(vec![
        ("grossBasisPoints", Value::Int(result.gross_basis_points)),
        ("netBasisPoints", Value::Int(result.net_basis_points)),
        ("netIncome", money_to_value(&result.net_income)),
    ])
}

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