Functional Weave
Code in Python

banking.bacs-processing-date@1.0.0

impl/rust.rs

2,490 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_add_business_days::add_business_days;  ← from dates.add-business-days ^1.0.0 · built alongside by fune
use super::dates_bank_holidays::bank_holidays;  ← from dates.bank-holidays ^1.0.0 · built alongside by fune
use super::dates_day_of_week::is_weekday;  ← from dates.day-of-week ^1.0.0 · built alongside by fune

fn holidays_for(years: &[i64]) -> Vec<String> {
    let mut out = Vec::new();
    for year in years {
        for holiday in bank_holidays("england-and-wales", *year) {
            out.push(holiday.date);
        }
    }
    out
}

fn cycle_with(submission_iso: &str, after_cutoff: bool, holidays: &[String]) -> BacsCycle {
    let processing_day = is_weekday(submission_iso) && !holidays.iter().any(|h| h == submission_iso);
    let input_day = if processing_day && !after_cutoff {
        submission_iso.to_string()
    } else {
        add_business_days(submission_iso, 1, holidays)
    };
    BacsCycle {
        processing_day: add_business_days(&input_day, 1, holidays),
        entry_day: add_business_days(&input_day, 2, holidays),
        input_day,
    }
}

/// The Bacs cycle a file joins. Bacs works on processing days - Monday to
/// Friday except England and Wales bank holidays - and a file joins the cycle
/// whose input day is the day it arrives, if that is a processing day and it
/// arrived before the 22:30 deadline, or the next processing day otherwise.
/// Processing is the next processing day and entry the one after.
///
/// # Panics
/// Panics on a malformed date or a year outside the published holiday data.
pub fn bacs_processing_dates(submission_iso: &str, after_cutoff: bool) -> BacsCycle {
    // is_weekday validates the date before its year is read.
    is_weekday(submission_iso);
    let year: i64 = submission_iso[0..4].parse().unwrap();
    // Only reach for next year's calendar when the cycle really runs into it,
    // so a December submission in the last published year still works as long
    // as its cycle ends inside that year.
    let mut cycle = cycle_with(submission_iso, after_cutoff, &holidays_for(&[year]));
    if cycle.entry_day[0..4] != submission_iso[0..4] {
        cycle = cycle_with(submission_iso, after_cutoff, &holidays_for(&[year, year + 1]));
    }
    cycle
}

pub fn bacs_cycle_to_value(cycle: &BacsCycle) -> Value {
    Value::obj(vec![
        ("inputDay", Value::str(&cycle.input_day)),
        ("processingDay", Value::str(&cycle.processing_day)),
        ("entryDay", Value::str(&cycle.entry_day)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    bacs_cycle_to_value(&bacs_processing_dates(args[0].as_str(), args[1].as_bool()))
}