jmap-calendars-types 0.4.2

JMAP Calendars extension data types (draft-ietf-jmap-calendars-*)
Documentation

jmap-calendars-types

Serde-annotated Rust types for JMAP Calendars (draft-ietf-jmap-calendars-33) and JSCalendar (RFC 8984).

Types only — no method handlers, no async, no network I/O. Sits between jmap-types (RFC 8620 primitives) and jmap-calendars-server / jmap-calendars-client.

What it is

Type Module Spec
Calendar calendar draft §4
CalendarRights calendar draft §4
IncludeInAvailability calendar draft §4
CalendarEvent event draft §5, RFC 8984 §2.1, §4, §5.1
CalendarEventFilterCondition event draft §5.11.1
CalendarEventComparator event draft §5.11.2
RecurrenceRule jmap-jscalendar-types RFC 8984 §4.3.3
NDay jmap-jscalendar-types RFC 8984 §4.3.3
Location jmap-jscalendar-types RFC 8984 §4.2.5
VirtualLocation jmap-jscalendar-types RFC 8984 §4.2.6
Link jmap-jscalendar-types RFC 8984 §1.4.11
Participant jmap-jscalendar-types RFC 8984 §4.4.6
Alert jmap-jscalendar-types RFC 8984 §4.5.2
AlertTrigger jmap-jscalendar-types RFC 8984 §4.5.2
OffsetTrigger jmap-jscalendar-types RFC 8984 §4.5.2
AbsoluteTrigger jmap-jscalendar-types RFC 8984 §4.5.2
Relation jmap-jscalendar-types RFC 8984 §1.4.10
LocalDateTime jmap-jscalendar-types RFC 8984 §1.4.5
Duration jmap-jscalendar-types RFC 8984 §1.4.6
SignedDuration jmap-jscalendar-types RFC 8984 §1.4.7
CalendarEventNotification notification draft §7
Person notification draft §7
NotificationType notification draft §7
NotificationFilterCondition notification draft §7.4.1
CalendarAlert notification draft §7
ParticipantIdentity participant_identity draft §3
BusyPeriod availability draft §2.2
CalendarsCapability crate root draft §1.5
CalendarsAccountCapability crate root draft §1.5
CalendarsParseCapability crate root draft §5.13
PrincipalCalendarsCapability crate root draft §1.5
PrincipalsAvailabilityCapability crate root draft §2.2
PrincipalsAvailabilityAccountCapability crate root draft §2.2

The JSCalendar sub-object types (rows above with module = jmap-jscalendar-types) live in the jmap-jscalendar-types crate and are re-exported at this crate's root (jmap_calendars_types::Location). The same sub-types are consumed by jmap-tasks-types.

Every public item has exactly one path in this crate: the crate root. The Module column names the source file only; the modules are private.

What it's for

draft-ietf-jmap-calendars-33 data types, consumed by jmap-calendars-server (method handlers + the CalendarsBackend trait) and jmap-calendars-client (typed method bindings). Re-exports the jmap-jscalendar-types sub-objects at the crate root so callers can opt into typed RFC 8984 JSCalendar sub-objects without taking an extra direct dependency. Sibling to jmap-mail-types, jmap-contacts-types, and jmap-tasks-types in the workspace's extension-types family.

Property enum types re-exported at the crate root: CalendarProperty, CalendarEventProperty, CalendarEventNotificationProperty, ParticipantIdentityProperty.

Capability URI string constants: JMAP_CALENDARS_URI, JMAP_CALENDARS_PARSE_URI, JMAP_PRINCIPALS_AVAILABILITY_URI.

Filter extensibility

Filter and comparator types in this crate — CalendarEventFilterCondition, CalendarEventComparator, NotificationFilterCondition, and the generic Filter<T> / Operator re-exported from jmap-types — are intentionally not extensible via vendor "extras" fields. A filter clause the server does not understand silently breaks query correctness: the client gets the wrong set of records back with no error signal. So these types deliberately have no extra catch-all field.

Vendors who need to filter on custom fields have two options:

  • IETF-track (recommended). Use the JMAP Object Metadata extension (draft-ietf-jmap-metadata, capability URI urn:ietf:params:jmap:metadata), (draft-ietf-jmap-metadata-04), which adds namespace-keyed metadata / privateMetadata properties to each listed data type, with capability-declared schema (dataTypes → namespaces / supportsVendorNamespaces / maxDepth, §1.2.1) and metadataExists / metadataTextContains / metadataTextEquals conditions on the type's own /query filter (§3.6). This is the workspace's recommended path for vendor data that needs to be queryable. Helpers live in jmap-metadata-types, jmap-metadata-server, and jmap-metadata-client.
  • Pre-IETF escape. If you cannot wait for the metadata draft, escape the filter tree to serde_json::Value or fork the FilterCondition types. See PLAN.md in this crate for the hybrid sloppy-value pattern this crate already uses for JSCalendar-shaped fields.

This policy is part of the workspace extras-preservation policy documented in the workspace AGENTS.md; the filter-algebra exclusion decision is bd JMAP-lbdy.

Spec coverage

draft-ietf-jmap-calendars-33

Section Topic
§1.5 CalendarsCapability, CalendarsAccountCapability
§2.2 Principal/getAvailability — BusyPeriod
§3 ParticipantIdentity
§4 Calendar, CalendarRights, IncludeInAvailability
§5 CalendarEvent JMAP properties (id, calendarIds, isDraft, utcStart, utcEnd, useDefaultAlerts, blobId, etc.)
§5.4 Per-user properties (keywords, color, freeBusyStatus, useDefaultAlerts, alerts)
§5.7 iCalendar wire property (ICalComponent, draft-ietf-calext-jscalendar-icalendar-28 §5.1.1)
§5.11.1 CalendarEventFilterCondition
§5.11.2 CalendarEventComparator
§5.13 CalendarsParseCapability
§7 CalendarEventNotification, Person, NotificationType
§7.4.1 NotificationFilterCondition

RFC 8984 (JSCalendar)

Section Topic
§1.4.5 LocalDateTime newtype
§1.4.6 Duration newtype
§1.4.7 SignedDuration newtype
§1.4.10 Relation
§1.4.11 Link
§4.1 Metadata properties (uid, relatedTo, prodId, created, updated, sequence)
§4.2 What/where properties (title, description, locations, virtualLocations, links, keywords, color)
§4.3 Recurrence properties (recurrenceId, recurrenceRules, recurrenceOverrides, excluded)
§4.3.3 RecurrenceRule, NDay
§4.4 Scheduling/sharing (priority, freeBusyStatus, privacy, participants, replyTo)
§4.4.6 Participant
§4.5 Alert properties (alerts)
§4.5.2 Alert, AlertTrigger, OffsetTrigger, AbsoluteTrigger
§4.6 Multilingual (localizations)
§4.7 Time zone (timeZone, timeZones)
§4.7.2 Custom time zone definitions (opaque serde_json::Value passthrough)
§5.1 Event-specific (start, duration, status)

How to use

Deserialize a CalendarEvent

use jmap_calendars_types::CalendarEvent;

let json = r#"{
    "id": "ev1",
    "uid": "abc-123@example.com",
    "title": "Team meeting",
    "start": "2024-06-15T10:00:00",
    "duration": "PT1H",
    "timeZone": "America/New_York",
    "calendarIds": { "cal1": true },
    "updated": "2024-06-01T00:00:00Z"
}"#;

let event: CalendarEvent = serde_json::from_str(json).unwrap();
assert_eq!(event.title.as_deref(), Some("Team meeting"));

Deserialize a Calendar

use jmap_calendars_types::Calendar;

let json = r#"{
    "id": "cal1",
    "name": "Work",
    "color": "#4285f4",
    "isSubscribed": true,
    "isVisible": true,
    "isDefault": false,
    "includeInAvailability": "all",
    "myRights": {
        "mayReadFreeBusy": true,
        "mayReadItems": true,
        "mayWriteAll": true,
        "mayWriteOwn": true,
        "mayUpdatePrivate": true,
        "mayRSVP": true,
        "mayShare": false,
        "mayDelete": false
    },
    "sortOrder": 0
}"#;

let cal: Calendar = serde_json::from_str(json).unwrap();
assert_eq!(cal.name, "Work");

Deserialize a BusyPeriod

use jmap_calendars_types::BusyPeriod;

let json = r#"{
    "utcStart": "2024-06-15T09:00:00Z",
    "utcEnd": "2024-06-15T10:00:00Z",
    "busyStatus": "busy"
}"#;

let period: BusyPeriod = serde_json::from_str(json).unwrap();
assert_eq!(period.utc_start, "2024-06-15T09:00:00Z");

How it works

rename_all = "camelCase"

Every struct carries #[serde(rename_all = "camelCase")]. Rust field names use snake_case; the wire format is camelCase per JSCalendar and JMAP conventions. The mapping is automatic: calendar_ids → "calendarIds", recurrence_rules → "recurrenceRules", etc.

#[serde(rename = "@type")] for JSCalendar type discriminators

JSCalendar embeds an @type field in sub-objects to identify their kind (e.g. "RecurrenceRule", "Participant", "Alert"). The @ character is not valid in a Rust identifier, so each such field is declared as:

#[serde(rename = "@type")]
pub at_type: String,

AlertTrigger uses an internally-tagged serde enum (#[serde(tag = "@type")]) so that OffsetTrigger, AbsoluteTrigger, and the forward-compatibility Unknown(serde_json::Value) variant all dispatch on the same @type field.

#[non_exhaustive] policy

All public structs and enums are #[non_exhaustive]. New fields may be added as the draft evolves without breaking downstream crates. Construct structs using ..Default::default() or the builder pattern your crate provides.

serde_json::Value for open-ended extension points

Several CalendarEvent fields use Option<serde_json::Value> rather than concrete typed structs:

Field Why Value
timeZones Custom VTIMEZONE definitions (RFC 8984 §4.7.2) are a complex nested format requiring a full timezone parser. Almost no caller needs to construct these.
locations, virtualLocations, links, participants, alerts, recurrenceRules, excludedRecurrenceRules, keywords, categories, replyTo, relatedTo JSCalendar defines these as open-ended maps/arrays where values are rich nested objects. Concrete types for each are re-exported at the crate root; callers that need typed access deserialize the Value themselves using those types.

The fields recurrenceOverrides and localizations use a typed envelope — Option<BTreeMap<String, jmap_types::PatchObject>> — because they ARE RFC 8620 §5.3 PatchObjects at the JMAP layer. The outer envelope is typed; the inner PatchObject leaves remain serde_json::Value to preserve per-leaf JSCalendar shape flexibility. Wire format is byte-identical to a plain JSON object map via PatchObject's #[serde(transparent)].

This is the correct representation, not laziness — do not change these to typed structs without reading RFC 8984 §3.3 on extensibility.

iCalendar wire name

The icalendar field on CalendarEvent carries the iCalendar property (draft-ietf-jmap-calendars-33 §5.7), an ICalComponent object holding iCalendar data that converted to the event. It is only returned when explicitly requested. The field carries an explicit rename rather than relying on rename_all = "camelCase":

#[serde(rename = "iCalendar", skip_serializing_if = "Option::is_none")]
pub icalendar: Option<serde_json::Value>,

Gotchas

  • timeZones, links, participants, alerts, locations, virtualLocations use Option<serde_json::Value> because JSCalendar defines these as open-ended maps where values are complex nested objects. Callers needing typed access must deserialize the Value themselves using the concrete types re-exported at the crate root.

  • recurrenceOverrides, localizations use Option<BTreeMap<String, jmap_types::PatchObject>>: the outer envelope is typed (RFC 8620 §5.3 PatchObject) while inner leaves remain Value for JSCalendar shape flexibility. Construct via PatchObject::from_map(...) or Map::into().

  • iCalendar: Option<Value> carries the ICalComponent object (jCal arrays, RFC 7265) with no validation; the crate does not parse it.

  • draft-26 iCalComponent. draft-ietf-jmap-calendars-26 used an iCalComponent string. That key no longer deserializes into the typed field; it falls into the extras catch-all and round-trips under its own spelling. Pinned in calendar_event_draft26_icalcomponent_lands_in_extras.

  • Draft expired. draft-ietf-jmap-calendars-33 is an expired Internet-Draft, not a published RFC. Some ambiguous field semantics use best-judgment interpretation based on the JSCalendar RFC 8984 normative base. The type definitions will need review when (if) the draft is published.

  • LocalDateTime and Duration are unvalidated newtype wrappers around String. Format correctness (RFC 8984 §1.4.5, §1.4.6) is the backend's responsibility; this crate does not parse the internal format.

Crate family

jmap-types (RFC 8620 wire primitives)
    └── jmap-calendars-types  ← this crate
            ├── jmap-calendars-server (method handlers)
            └── jmap-calendars-client (client extension trait)

Path dependencies between crates use path = "../crate-jmap-*" and will remain that way until the family is published to crates.io.

References

  • draft-ietf-jmap-calendars-33 — JMAP Calendars binding (Calendar, CalendarEvent additions, CalendarEventNotification, ParticipantIdentity, capability objects, error codes)
  • RFC 8984 — JSCalendar Event format (all JSCalendar Event properties, RecurrenceRule, Alert, Participant, Location, etc.)
  • RFC 8620 — JMAP Core (request format, PatchObject, SetError, ResultReference)