use super::funejson::Value; use super::dates_add_days::add_days; use super::dates_day_of_week::day_of_week; use super::todo_item::Recurrence; use super::todo_item_validate_draft::recurrence_to_value; use super::todo_parse_date_phrase::parse_date_phrase; use super::todo_parse_quick_add::parse_quick_add; /// A Repeat box: an "every ..." phrase as todo.parse-quick-add reads it, then /// optionally a start date. Anything else is None. The series starts on the /// typed date, else on `due`, else where quick-add would start it; "every /// " and "every weekday" move the start forward to a day they allow. /// /// # Panics /// On a malformed or impossible `today` or `due` (dates.add-days's message). pub fn parse_repeat(text: &str, due: Option<&str>, today: &str) -> Option { add_days(today, 0); if let Some(date) = due { add_days(date, 0); } let words: Vec<&str> = text .split(|c: char| matches!(c, ' ' | '\t' | '\n' | '\r' | '\u{0B}' | '\u{0C}')) .filter(|w| !w.is_empty()) .collect(); if words.len() < 2 || words[0].to_ascii_lowercase() != "every" { return None; } // The repeat is 2 words ("every week") or 3 ("every 2 weeks"); no 2-word // form starts a 3-word one, so the first that parses as a bare repeat wins. let mut found: Option<(Recurrence, usize)> = None; for n in [2usize, 3] { if n > words.len() { break; } let draft = parse_quick_add(&words[..n].join(" "), today); if let Some(rule) = draft.recurrence { if draft.title.is_empty() { found = Some((rule, n)); break; } } } let (repeat, length) = found?; let mut start: Option = due.map(|d| d.to_string()); if words.len() > length { start = Some(parse_date_phrase(&words[length..].join(" "), today)?); } let mut start = match start { None => return Some(repeat), Some(date) => date, }; if repeat.frequency == "weekdays" { let day = day_of_week(&start); if day >= 6 { start = add_days(&start, 8 - day); } } else if repeat.frequency == "weekly" && length == 2 && parse_date_phrase(words[1], today).is_some() { // "every monday": quick-add anchored it on the next Monday, so that // anchor's weekday is the one the series keeps. let shift = (day_of_week(&repeat.anchor) - day_of_week(&start) + 7) % 7; start = add_days(&start, shift); } Some(Recurrence { anchor: start, ..repeat }) } pub fn fune_vector(args: &[Value]) -> Value { let due = if args[1].is_null() { None } else { Some(args[1].as_str()) }; recurrence_to_value(&parse_repeat(args[0].as_str(), due, args[2].as_str())) }