Tiny iCalendar helpers — just enough to extract the UID and primary component (VEVENT vs VTODO) from an iCalendar object and to compute an ETag from its serialized form.
Summary
Functions
Returns true if the iCalendar object overlaps the given time range
[range_start, range_end) (both DateTime.t/0).
Parse a CalDAV time-range start/end attribute (basic ISO 8601 form
used by CalDAV: YYYYMMDDTHHMMSSZ) or an iCalendar property value into
a DateTime. Returns nil on failure.
Returns {uid_or_nil, component_or_nil} for the first inner component
(VEVENT or VTODO) found in the iCalendar text.
Lightweight validation of an iCalendar PUT body.
Functions
@spec overlaps_range?(String.t(), DateTime.t(), DateTime.t()) :: boolean()
Returns true if the iCalendar object overlaps the given time range
[range_start, range_end) (both DateTime.t/0).
Uses a deliberately permissive interpretation: we extract the first
DTSTART and DTEND/DUE and treat them as UTC instants regardless
of TZID. Recurring events (RRULE) are not expanded — we test only
the master instance, which is good enough for the minimal server.
Parse a CalDAV time-range start/end attribute (basic ISO 8601 form
used by CalDAV: YYYYMMDDTHHMMSSZ) or an iCalendar property value into
a DateTime. Returns nil on failure.
Returns {uid_or_nil, component_or_nil} for the first inner component
(VEVENT or VTODO) found in the iCalendar text.
@spec validate(String.t()) :: :ok | {:error, :not_calendar | :no_component | :missing_dtstart}
Lightweight validation of an iCalendar PUT body.
Catches the obvious cases that would otherwise leave us storing
garbage on disk: missing VCALENDAR envelope, no inner component,
or a VEVENT without the required DTSTART (RFC 5545 §3.6.1).
Returns :ok or {:error, reason} with one of:
:not_calendar— body has noBEGIN:VCALENDARenvelope:no_component— envelope is empty (no VEVENT/VTODO/VJOURNAL/VFREEBUSY):missing_dtstart— VEVENT block has noDTSTART
We don't deep-parse: we only look for required line-prefixes after unfolding. This trades some strictness for simplicity; clients that send malformed bodies still get rejected, but we don't try to be a full RFC 5545 validator.