Functional Weave
Code in Rust

finance.invoice.line-total@1.0.0

impl/rust.rs

1,620 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::money_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::money_apply_rate::apply_rate;  ← from money.apply-rate ^1.0.0 · built alongside by fune

/// Net total for one invoice line.
///
/// The discount is rounded once, here, before any tax is applied. Rounding the
/// discount and the tax in the other order produces answers that differ by a
/// penny on roughly one line in five.
///
/// # Panics
/// Panics if the discount is outside 0..=10000 basis points.
pub fn line_total(unit_price: &Money, quantity: i64, discount_basis_points: i64) -> Money {
    if !(0..=10000).contains(&discount_basis_points) {
        panic!(
            "discount_basis_points must be between 0 and 10000, received {}",
            discount_basis_points
        );
    }

    let gross = money(unit_price.minor * quantity, &unit_price.currency);
    let discount = apply_rate(&gross, discount_basis_points, "half-up");
    money(gross.minor - discount.minor, &gross.currency)
}

pub fn fune_vector(args: &[Value]) -> Value {
    // Refuse what the typed signature cannot hold, with the wording TypeScript
    // and Python use, rather than let the conversion below quietly change it.
    if let Value::Float(f) = args[1] {
        if f.fract() != 0.0 {
            panic!("quantity must be an integer, received {}", f);
        }
    }
    if let Value::Float(f) = args[2] {
        if f.fract() != 0.0 {
            panic!("discountBasisPoints must be between 0 and 10000, received {}", f);
        }
    }
    money_to_value(&line_total(
        &money_from_value(&args[0]),
        args[1].as_i64(),
        args[2].as_i64(),
    ))
}