use super::funejson::Value; use super::dates_add_days::add_days; use super::dates_day_of_week::day_of_week; use super::subscriptions_next_billing_date::next_billing_date; use super::todo_item::{recurrence_from_value, Recurrence}; /// The first date of the series strictly after both `due` and `today`. /// /// Every occurrence is the anchor plus whole intervals, so month ends stay /// month ends; finishing late skips the occurrences already missed. /// /// # Panics /// Panics on an unknown frequency, an interval below 1 (or above 1 for /// weekdays), an impossible date, or an anchor after the due date. pub fn next_occurrence(recurrence: &Recurrence, due: &str, today: &str) -> String { let frequency = recurrence.frequency.as_str(); let interval = recurrence.interval; let step = match frequency { "daily" => "day", "weekly" => "week", "monthly" => "month", "yearly" => "year", "weekdays" => "", other => panic!( "unknown repeat frequency \"{}\": expected daily, weekdays, weekly, monthly or yearly", other ), }; if interval < 1 { panic!("repeat interval must be 1 or more, received {}", interval); } if frequency == "weekdays" && interval != 1 { panic!("weekday repeats cannot skip weeks: interval must be 1, received {}", interval); } add_days(&recurrence.anchor, 0); add_days(due, 0); add_days(today, 0); if recurrence.anchor.as_str() > due { panic!("repeat anchor {} is after the due date {}", recurrence.anchor, due); } let after = if due > today { due } else { today }; if frequency == "weekdays" { let mut next = add_days(after, 1); while day_of_week(&next) > 5 { next = add_days(&next, 1); } return next; } next_billing_date(&recurrence.anchor, step, interval, after) } pub fn fune_vector(args: &[Value]) -> Value { if let Value::Float(f) = args[0].get("interval") { panic!("repeat interval must be 1 or more, received {}", f); } let rule = recurrence_from_value(&args[0]).expect("a recurrence"); Value::Str(next_occurrence(&rule, args[1].as_str(), args[2].as_str())) }