feat: add safe calendar import and export

Add explicit noninteractive CLI transfer modes with full event validation, idempotent duplicate handling, collision refusal, atomic import backups, and no-overwrite export creation.

Cover pure merge semantics and end-to-end rollback paths for unsupported content, invalid identities, conflicts, option combinations, and existing destinations.
This commit is contained in:
2026-07-18 09:32:54 +01:00
parent 8b6cf2e877
commit b524e6e33d
11 changed files with 867 additions and 8 deletions

View File

@@ -82,6 +82,25 @@ does not consume the backup. The TUI mutates a copyable in-memory model and
rolls it back if persistence fails. Its bounded undo/redo history contains only
mutations that crossed the persistence boundary successfully.
Explicit CLI import and export reuse the storage parser, safety classification,
validation, and atomic persistence boundary; these workflows do not depend on
the TUI and perform no network access. Import loads and validates both source
and target before staging a target replacement. UIDs in both files must be
non-empty and unique, every interval must be valid, and every component in both
files must be safe to round-trip. Matching UID plus identical fields is an
idempotent duplicate; matching UID with different fields is a collision that
aborts the whole operation. A committed merge writes the exact old target to
`.bak` before atomic replacement. If every source event is already present
exactly, import performs no write and does not alter the backup.
Export accepts only a valid, safely round-trippable source, serializes a
canonical calendar through private staging, and atomically creates a destination
that must not already exist. Destination refusal is part of the
commit boundary, so export never replaces an existing path. Import, export,
demo, non-interactive frame printing, and backup restoration are distinct
top-level application modes; import and export cannot be selected together or
combined with any of the other modes.
The current writer deliberately refuses to mutate an existing file when the
loader encounters information it cannot round-trip, including recurrence
overrides, `RDATE`, custom `VTIMEZONE` definitions, alarms, attendees, unknown