use crate::support::tokens;
pub fn interval(schedule: &str) -> Option<i64> {
match schedule {
"6h" => Some(6 * 3600),
"daily" => Some(24 * 3600),
"weekly" => Some(7 * 24 * 3600),
_ => None,
}
}
pub fn describe(schedule: &str) -> &'static str {
match schedule {
"6h" => "every six hours",
"daily" => "once a day",
"weekly" => "once a week",
_ => "never — backups are taken by hand",
}
}
pub fn next_due(schedule: &str, last: Option<&str>, now: &str) -> Option<String> {
let interval = interval(schedule)?;
match last {
None => Some(now.to_string()),
Some(at) => Some(tokens::format_utc(tokens::parse_utc(at) + interval)),
}
}
pub fn is_due(schedule: &str, last: Option<&str>, now: &str) -> bool {
match next_due(schedule, last, now) {
None => false,
Some(due) => tokens::parse_utc(now) >= tokens::parse_utc(&due),
}
}
pub fn beyond_retention(newest_first: &[i64], keep: usize) -> Vec<i64> {
if keep == 0 {
return Vec::new();
}
newest_first.iter().skip(keep).copied().collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_schedule_that_has_never_run_is_due_now() {
assert!(is_due("daily", None, "2026-09-03 10:00:00"));
assert!(!is_due("disabled", None, "2026-09-03 10:00:00"));
assert!(!is_due("", None, "2026-09-03 10:00:00"));
}
#[test]
fn the_next_run_is_one_interval_after_the_last() {
let now = "2026-09-03 06:00:00";
assert_eq!(next_due("6h", Some(now), now).as_deref(), Some("2026-09-03 12:00:00"));
assert_eq!(next_due("weekly", Some(now), now).as_deref(), Some("2026-09-10 06:00:00"));
assert_eq!(next_due("disabled", Some(now), now), None);
assert_eq!(next_due("daily", None, now).as_deref(), Some(now));
}
#[test]
fn a_run_is_owed_only_once_the_interval_has_passed() {
let last = Some("2026-09-03 06:00:00");
assert!(!is_due("6h", last, "2026-09-03 11:59:59"));
assert!(is_due("6h", last, "2026-09-03 12:00:00"));
assert!(is_due("6h", last, "2026-09-04 09:00:00"));
}
#[test]
fn retention_of_zero_deletes_nothing() {
let ids = [9, 8, 7, 6, 5, 4, 3, 2, 1];
assert_eq!(beyond_retention(&ids, 0), Vec::<i64>::new());
assert_eq!(beyond_retention(&ids, 7), vec![2, 1]);
assert_eq!(beyond_retention(&ids, 20), Vec::<i64>::new());
assert_eq!(beyond_retention(&[], 7), Vec::<i64>::new());
}
}