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

@@ -6,8 +6,9 @@ inherits its colors from your terminal theme.
This repository currently contains the first local-calendar vertical slice:
date/domain logic, guarded atomic iCalendar persistence, add/edit/delete forms,
a responsive ANSI terminal UI with month and agenda views, tests, and Linux
launch integration. See [the product specification](docs/PRODUCT.md),
a responsive ANSI terminal UI with month and agenda views, explicit safe
calendar import/export, tests, and Linux launch integration. See
[the product specification](docs/PRODUCT.md),
[architecture](docs/ARCHITECTURE.md), and [roadmap](docs/ROADMAP.md).
## Controls
@@ -46,6 +47,28 @@ Run the tests with `meson test -C build --print-errorlogs`. Nocal reads
positional argument. `--demo` adds a few unsaved sample appointments and
`--print` emits a non-interactive frame.
Calendar interchange is explicit, non-interactive, and network-free:
```sh
nocal --import SOURCE.ics --calendar TARGET.ics
nocal --export DESTINATION.ics --calendar SOURCE.ics
```
Import atomically merges source events into the target. An event whose UID and
all fields exactly match an existing event is skipped. The complete operation
is rejected before the target is written if either file is unsafe to
round-trip or contains empty or duplicate UIDs or invalid intervals, or if an
imported UID collides with a different target event. A successful merge that
adds events retains the target's exact previous bytes as `TARGET.ics.bak`; an
import containing only exact duplicates does not rewrite the target or its
backup.
Export validates the source and atomically creates a canonical calendar at the
destination. It refuses to overwrite any existing destination and rejects a
source that fails event validation or that Nocal cannot safely round-trip.
`--import` and `--export` are mutually exclusive and cannot be combined with
`--demo`, `--print`, or `--restore-backup`.
Nocal expands and safely round-trips a focused iCalendar recurrence subset:
daily, weekly, monthly, and yearly rules with interval, count or until, common
weekly/monthly/yearly selectors, `EXDATE`, floating times, UTC, and system IANA