feat(recurring): implement RECURRENCE-ID override handling

- Add `overrides` field to `rem` type to hold single-event REMs from
  non-cancelled overrides
- Add `is_cancelled`, `build_override_rem`, and `collect_overrides` to
  process RECURRENCE-ID override events
- Replace `warn_unhandled_recurring` with `collect_overrides` in the
  collector pipeline
- Fix `separate_master_and_recurrence` partition logic (swapped
  `Left`/`Right`)
- Render override REMs appended to the master REM in `string_of_rem`
This commit is contained in:
2026-05-17 20:04:18 +02:00
parent 03c652260a
commit c246ec6775
3 changed files with 66 additions and 18 deletions

View File

@@ -32,6 +32,7 @@ type rem = {
(** List of events that are part of the same recurring series: these are only the overrides, not the master event
*)
exdate : Icalendar.date_or_datetime list; (** List of excluded dates for recurring events *)
overrides : rem list; (** Single-event REMs generated from non-cancelled RECURRENCE-ID overrides *)
}
(** A complete REM command *)
@@ -49,6 +50,7 @@ let empty =
daily = None;
recurring = [];
exdate = [];
overrides = [];
}
(* ── buffer primitives ────────────────────────────────────────── *)
@@ -221,12 +223,16 @@ let render_yearly rem month day =
(* ── dispatcher ───────────────────────────────────────────────── *)
let string_of_rem rem =
match rem.daily with
| Some d -> render_daily rem d
| None -> (
match rem.weekly with
| Some w -> render_weekly rem w
| None -> (
match rem.yearly with
| Some (month, day) -> render_yearly rem month day
| None -> render_single rem))
let main =
match rem.daily with
| Some d -> render_daily rem d
| None -> (
match rem.weekly with
| Some w -> render_weekly rem w
| None -> (
match rem.yearly with
| Some (month, day) -> render_yearly rem month day
| None -> render_single rem))
in
let overrides = List.map render_single rem.overrides in
String.concat "" (main :: overrides)