Functional Weave
Code in Rust

dates.bank-holidays@1.0.0

impl/rust.rs

2,595 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::dates_bank_holidays_data::BANK_HOLIDAYS;  ← this capability’s own data, compiled from data/bank-holidays.json into the same file by fune build

/// The bank holidays for a UK region and year, in date order, as gov.uk
/// publishes them. A year the data does not cover is an error rather than an
/// empty list, because an empty list reads as "no holidays" and a deadline
/// calculation would then count Christmas Day as a working day.
///
/// # Panics
/// Panics on an unknown region or a year outside the published calendar.
pub fn bank_holidays(region: &str, year: i64) -> Vec<BankHoliday> {
    let mut first: Option<i64> = None;
    let mut last: Option<i64> = None;
    let mut found: Vec<BankHoliday> = Vec::new();
    let prefix = format!("{:04}-", year);
    for row in BANK_HOLIDAYS {
        if row.region != region {
            continue;
        }
        let row_year: i64 = row.date[0..4].parse().unwrap();
        if first.map_or(true, |f| row_year < f) {
            first = Some(row_year);
        }
        if last.map_or(true, |l| row_year > l) {
            last = Some(row_year);
        }
        if row.date.starts_with(&prefix) {
            found.push(BankHoliday {
                date: row.date.to_string(),
                title: row.title.to_string(),
                substitute: row.substitute,
            });
        }
    }
    let (first, last) = match (first, last) {
        (Some(f), Some(l)) => (f, l),
        _ => panic!(
            "unknown region \"{}\": expected england-and-wales, scotland or northern-ireland",
            region
        ),
    };
    if year < first || year > last {
        panic!(
            "no bank holiday data for {} in {}: the published calendar covers {} to {}",
            region, year, first, last
        );
    }
    // ISO dates sort correctly as strings.
    found.sort_by(|a, b| a.date.cmp(&b.date));
    found
}

pub fn bank_holiday_to_value(holiday: &BankHoliday) -> Value {
    Value::obj(vec![
        ("date", Value::str(&holiday.date)),
        ("title", Value::str(&holiday.title)),
        ("substitute", Value::Bool(holiday.substitute)),
    ])
}

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!("year must be an integer, received {}", f);
        }
    }
    Value::Arr(
        bank_holidays(args[0].as_str(), args[1].as_i64())
            .iter()
            .map(bank_holiday_to_value)
            .collect(),
    )
}