libitofin 0.52.1

A ground-up Rust port of QuantLib: quantitative-finance primitives for pricing, risk, and numerical methods.
Documentation
use crate::errors::QlResult;
use crate::require;
use crate::time::{businessdayconvention::BusinessDayConvention, calendar::Calendar, date::Date};

pub(crate) fn valid_cpi_date(date: Date) -> QlResult<()> {
    require!(
        (367..=109574).contains(&date.serial_number()),
        "invalid CPI date"
    );
    Ok(())
}
pub(crate) fn checked_cpi_day(date: Date, n: i32) -> QlResult<Date> {
    let serial = date.serial_number().checked_add(n);
    require!(
        serial.is_some_and(|s| (367..=109574).contains(&s)),
        "CPI date outside supported range"
    );
    match serial {
        Some(s) => Ok(Date::from_serial(s)),
        None => crate::fail!("CPI date overflow"),
    }
}
pub(crate) fn checked_cpi_adjust(
    calendar: &Calendar,
    date: Date,
    convention: BusinessDayConvention,
) -> QlResult<Date> {
    use BusinessDayConvention::*;
    valid_cpi_date(date)?;
    let seek = |direction| -> QlResult<Date> {
        let mut current = date;
        while calendar.is_holiday(current) {
            current = checked_cpi_day(current, direction)?;
        }
        Ok(current)
    };
    match convention {
        Unadjusted => Ok(date),
        Following => seek(1),
        Preceding => seek(-1),
        ModifiedFollowing | HalfMonthModifiedFollowing => {
            let next = seek(1)?;
            if next.month() != date.month()
                || (convention == HalfMonthModifiedFollowing
                    && date.day_of_month() <= 15
                    && next.day_of_month() > 15)
            {
                seek(-1)
            } else {
                Ok(next)
            }
        }
        ModifiedPreceding => {
            let prior = seek(-1)?;
            if prior.month() != date.month() {
                seek(1)
            } else {
                Ok(prior)
            }
        }
        Nearest => {
            let mut after = date;
            let mut before = date;
            while calendar.is_holiday(after) && calendar.is_holiday(before) {
                after = checked_cpi_day(after, 1)?;
                before = checked_cpi_day(before, -1)?;
            }
            if calendar.is_holiday(after) {
                Ok(before)
            } else {
                Ok(after)
            }
        }
    }
}