ExDav.ICal (ExDav v0.3.5)

Copy Markdown View Source

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

etag(content)

@spec etag(binary()) :: String.t()

overlaps_range?(text, range_start, range_end)

@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_ical_datetime(value)

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.

summarize(text)

@spec summarize(String.t()) :: {String.t() | nil, String.t() | nil}

Returns {uid_or_nil, component_or_nil} for the first inner component (VEVENT or VTODO) found in the iCalendar text.

validate(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 no BEGIN:VCALENDAR envelope
  • :no_component — envelope is empty (no VEVENT/VTODO/VJOURNAL/VFREEBUSY)
  • :missing_dtstart — VEVENT block has no DTSTART

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.