generated from eblume/project-template
Phase 1: v1 prototype #1
1 changed files with 25 additions and 0 deletions
feat(cli): parse "every Nth" recurrence → monthly by day-of-month
All checks were successful
Build / validate (pull_request) Successful in 2m47s
All checks were successful
Build / validate (pull_request) Successful in 2m47s
Todoist uses "every 5th" for monthly-on-the-5th; map it to FREQ=MONTHLY;BYMONTHDAY=N (1..=31). Surfaced by the Todoist import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit
2d4e4ae4d7
|
|
@ -177,6 +177,10 @@ pub fn parse_recurrence(spec: &str) -> Result<String> {
|
|||
if let Some((m, d)) = parse_month_day(body) {
|
||||
return Ok(format!("FREQ=YEARLY;BYMONTH={m};BYMONTHDAY={d}"));
|
||||
}
|
||||
// "every <Nth>" → monthly on that day of the month ("every 5th").
|
||||
if let Some(d) = parse_monthday_ordinal(body) {
|
||||
return Ok(format!("FREQ=MONTHLY;BYMONTHDAY={d}"));
|
||||
}
|
||||
// "every <unit>" / "every N <unit>s".
|
||||
let mut it = body.split_whitespace();
|
||||
let first = it.next().unwrap_or("");
|
||||
|
|
@ -224,6 +228,19 @@ fn byday(wd: Weekday) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a day-of-month ordinal like "5th", "1st", "22nd", "3rd" → 1..=31.
|
||||
fn parse_monthday_ordinal(s: &str) -> Option<u32> {
|
||||
let digits = s.trim_end_matches(|c: char| c.is_ascii_alphabetic());
|
||||
let suffix = &s[digits.len()..];
|
||||
if !matches!(suffix, "st" | "nd" | "rd" | "th") {
|
||||
return None;
|
||||
}
|
||||
match digits.parse::<u32>() {
|
||||
Ok(d @ 1..=31) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse "april 15" / "apr 15" (and the reversed "15 april") → (month, day).
|
||||
fn parse_month_day(s: &str) -> Option<(u32, u32)> {
|
||||
let toks: Vec<&str> = s.split_whitespace().collect();
|
||||
|
|
@ -367,6 +384,14 @@ mod tests {
|
|||
parse_recurrence("every April 15").unwrap(),
|
||||
"FREQ=YEARLY;BYMONTH=4;BYMONTHDAY=15"
|
||||
);
|
||||
assert_eq!(
|
||||
parse_recurrence("every 5th").unwrap(),
|
||||
"FREQ=MONTHLY;BYMONTHDAY=5"
|
||||
);
|
||||
assert_eq!(
|
||||
parse_recurrence("every 22nd").unwrap(),
|
||||
"FREQ=MONTHLY;BYMONTHDAY=22"
|
||||
);
|
||||
assert!(parse_recurrence("every blue moon").is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue