Functional Weave
Code in TypeScript

legal.court-fee@1.0.2

impl/rust.rs

2,740 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::legal_court_fee_data::{ISSUE_FEES, ISSUE_FEES_HISTORY};  ← this capability’s own data, compiled from data/issue-fees.json into the same file by fune build
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

fn is_iso_date(s: &str) -> bool {
    let b = s.as_bytes();
    b.len() == 10
        && b.iter().enumerate().all(|(i, c)| if i == 4 || i == 7 { *c == b'-' } else { c.is_ascii_digit() })
}

/// The fee to issue a money claim, from the bands in force on the issue date.
///
/// # Panics
/// Panics on a non-GBP or non-positive claim, an unknown channel, a bad date,
/// a date before the data, or an online claim over GBP 100,000.
pub fn court_fee(claim: &Money, channel: &str, on_date: &str) -> Money {
    if claim.currency != "GBP" {
        panic!("court fees are set for claims in GBP, received {}", claim.currency);
    }
    if claim.minor <= 0 {
        panic!("claim must be greater than zero");
    }
    if channel != "paper" && channel != "online" {
        panic!("unknown filing channel \"{}\": expected paper or online", channel);
    }
    if !is_iso_date(on_date) {
        panic!("onDate must be an ISO date (YYYY-MM-DD), received \"{}\"", on_date);
    }
    let mut in_force = 0;
    let mut earliest: Option<&str> = None;
    for row in ISSUE_FEES {
        if row.channel != channel {
            continue;
        }
        if earliest.map_or(true, |e| row.valid_from < e) {
            earliest = Some(row.valid_from);
        }
        if on_date < row.valid_from || row.valid_to.map_or(false, |to| on_date > to) {
            continue;
        }
        in_force += 1;
        if claim.minor <= row.over_minor || row.up_to_minor.map_or(false, |up| claim.minor > up) {
            continue;
        }
        if let Some(fee) = row.fee_minor {
            return money(fee, "GBP");
        }
        // gov.uk: "round down the result to the nearest 1 pence".
        return apply_rate(claim, row.basis_points.unwrap(), "down");
    }
    if in_force > 0 {
        panic!(
            "a claim of {} pence cannot be issued {} on {}: online claims are limited to GBP 100,000",
            claim.minor, channel, on_date
        );
    }
    if let Some(e) = earliest {
        if ISSUE_FEES_HISTORY != "full" && on_date < e {
            panic!(
                "no {} court fee on {}: this build was installed with history={}, so it only carries fees from {}. Reinstall with history=full for older claims.",
                channel, on_date, ISSUE_FEES_HISTORY, e
            );
        }
    }
    panic!("no {} court fee in force on {}", channel, on_date);
}

pub fn fune_vector(args: &[Value]) -> Value {
    money_to_value(&court_fee(&money_from_value(&args[0]), args[1].as_str(), args[2].as_str()))
}