90 lines
4.4 KiB
Markdown
90 lines
4.4 KiB
Markdown
# Architecture
|
|
|
|
Nocal is split into four layers. Dependencies point inward, keeping remote sync
|
|
and terminal rendering replaceable.
|
|
|
|
```text
|
|
src/main.cpp
|
|
├── tui/ ANSI rendering, input decoding, terminal lifecycle
|
|
├── storage/ iCalendar file adapter (later: SQLite cache)
|
|
└── domain/ Event, Calendar, civil dates, month layout and queries
|
|
|
|
future sync workers
|
|
├── caldav/ Nextcloud and generic CalDAV
|
|
├── google/ Google Calendar REST/OAuth
|
|
└── graph/ Microsoft Graph/OAuth
|
|
└──────────> domain/ through a SyncProvider interface
|
|
```
|
|
|
|
## Domain
|
|
|
|
The domain layer uses C++20 `<chrono>` civil dates and time points. It knows
|
|
nothing about escape sequences, files, OAuth, or HTTP. Month layout always
|
|
produces 42 cells, starting on Monday, which keeps redraw geometry stable.
|
|
|
|
Events retain a stable UID, title, half-open start/end interval, all-day flag,
|
|
optional descriptive fields, and calendar identity. Queries define overlap as
|
|
`event.start < range.end && event.end > range.start`; this matters for events
|
|
crossing midnight.
|
|
|
|
## Storage
|
|
|
|
Version 0.1 uses a user-owned iCalendar file, defaulting to
|
|
`$XDG_DATA_HOME/nocal/calendar.ics` or `~/.local/share/nocal/calendar.ics`.
|
|
The adapter unfolds content lines before parsing and escapes text on output.
|
|
On POSIX systems, writes are serialized with an advisory lock, staged in a
|
|
same-directory private temporary file, flushed, and atomically renamed over the
|
|
destination. A load retains an immutable snapshot of the exact source bytes.
|
|
Before saving, the writer compares that snapshot with the destination while it
|
|
holds the same lock used for replacement, then checks again after staging and
|
|
immediately before commit. This catches same-size and same-timestamp changes
|
|
and fully serializes cooperating Nocal writers. The lock is advisory: an
|
|
unrelated program can ignore it, so no portable filesystem API can provide a
|
|
true compare-and-swap against every possible external writer.
|
|
|
|
Before an existing destination is replaced, its exact bytes are atomically
|
|
written to the adjacent `.bak` file. Explicit backup restoration uses the same
|
|
lock, source-revision check, private staging, and atomic replacement path; it
|
|
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.
|
|
|
|
The current writer deliberately refuses to mutate an existing file when the
|
|
loader encounters information it cannot round-trip, including recurrence,
|
|
alarms, attendees, time-zone identifiers, unknown properties, or malformed
|
|
components. Browsing remains available. This conservative boundary is more
|
|
important than partial editing because a successful-looking edit must not
|
|
erase unrelated calendar data.
|
|
|
|
Provider synchronization will not write directly into this UI file. It will use
|
|
a transaction-capable local cache and preserve remote ETags/sync tokens in a
|
|
provider metadata table.
|
|
|
|
The planned cache schema has `calendars`, `events`, `event_instances`, and
|
|
`sync_state`. Raw provider payloads are retained alongside normalized fields so
|
|
an older Nocal cannot destroy fields it does not understand.
|
|
|
|
## Terminal backend
|
|
|
|
The initial backend depends only on POSIX `termios`, `poll`, and ANSI/ECMA-48.
|
|
It uses the alternate screen, hides the cursor while rendering, restores all
|
|
terminal state through RAII, and repaints from a complete frame buffer. A resize
|
|
signal only sets a flag; terminal size and rendering are handled safely in the
|
|
main loop.
|
|
|
|
Colors are semantic ANSI slots and default background, intentionally delegating
|
|
actual RGB values to the terminal theme. This is both simpler and more native
|
|
than shipping an application theme that fights the desktop palette.
|
|
|
|
## Sync roadmap
|
|
|
|
All providers implement the same conceptual operations: authenticate, list
|
|
calendars, perform an incremental pull, push a local mutation, and resolve a
|
|
conflict. CalDAV uses sync tokens/ETags, Google uses page/sync tokens, and
|
|
Microsoft uses Graph delta links. Secrets belong in the Freedesktop Secret
|
|
Service, never in the calendar database or command line.
|
|
|
|
Conflict resolution is deterministic: unchanged side wins; otherwise retain
|
|
both versions and mark a conflict for the user. Network work happens outside the
|
|
render loop and publishes immutable snapshots to it.
|