Functional Weave
Code in Rust

telecoms.data-allowance@1.0.0

impl/rust.rs

2,908 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::{assert_same_currency, money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune

/// Usage against a bundle: remaining allowance, overage in whole blocks priced
/// and limited to an optional cap, and the share used, rounded down so a
/// bundle with anything left never reads 100%.
///
/// # Panics
/// Panics on negative quantities or prices, a block below 1, or a cap in
/// another currency.
pub fn data_allowance(allowance: i64, used: i64, overage_block: i64, price_per_block: &Money, overage_cap: Option<&Money>) -> DataAllowance {
    if allowance < 0 || used < 0 {
        panic!(
            "allowance and usage must be whole numbers of 0 or more, received {} and {}",
            allowance, used
        );
    }
    if overage_block < 1 {
        panic!("overage block must be 1 or more, received {}", overage_block);
    }
    if price_per_block.minor < 0 {
        panic!("price per block must not be negative, received {}", price_per_block.minor);
    }
    if let Some(cap) = overage_cap {
        assert_same_currency(price_per_block, cap);
        if cap.minor < 0 {
            panic!("overage cap must not be negative, received {}", cap.minor);
        }
    }
    let overage = (used - allowance).max(0);
    let overage_blocks = round_div(overage, overage_block, "up");
    let uncapped = overage_blocks * price_per_block.minor;
    let cap_minor = overage_cap.map(|c| c.minor);
    let capped = matches!(cap_minor, Some(c) if uncapped > c);
    DataAllowance {
        used,
        remaining: (allowance - used).max(0),
        overage,
        overage_blocks,
        overage_charge: money(
            if capped { cap_minor.unwrap() } else { uncapped },
            &price_per_block.currency,
        ),
        capped,
        percent_used_basis_points: if allowance == 0 {
            None
        } else {
            Some(round_div(used * 10000, allowance, "down"))
        },
    }
}

pub fn data_allowance_to_value(d: &DataAllowance) -> Value {
    Value::obj(vec![
        ("used", Value::Int(d.used)),
        ("remaining", Value::Int(d.remaining)),
        ("overage", Value::Int(d.overage)),
        ("overageBlocks", Value::Int(d.overage_blocks)),
        ("overageCharge", money_to_value(&d.overage_charge)),
        ("capped", Value::Bool(d.capped)),
        (
            "percentUsedBasisPoints",
            match d.percent_used_basis_points {
                Some(n) => Value::Int(n),
                None => Value::Null,
            },
        ),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let cap = if args[4].is_null() { None } else { Some(money_from_value(&args[4])) };
    data_allowance_to_value(&data_allowance(
        args[0].as_i64(),
        args[1].as_i64(),
        args[2].as_i64(),
        &money_from_value(&args[3]),
        cap.as_ref(),
    ))
}