Skip to main content

jmap_calendars_types/
calendar.rs

1//! Calendar object, CalendarRights, and IncludeInAvailability.
2//!
3//! Normative reference: draft-ietf-jmap-calendars-33 §4.
4
5use std::collections::BTreeMap;
6
7use jmap_types::Id;
8use serde::{Deserialize, Serialize};
9
10/// Controls which events in a Calendar contribute to availability calculations
11/// (draft-ietf-jmap-calendars-33 §4).
12///
13/// The spec defines exactly three values with no extension mechanism; modelled
14/// as a closed enum (no `Other` variant).  Wire values are lowercase.
15#[non_exhaustive]
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub enum IncludeInAvailability {
19    /// All events in this calendar are considered for availability.
20    #[serde(rename = "all")]
21    All,
22    /// Only events where the user is a confirmed or tentative participant are
23    /// considered.
24    #[serde(rename = "attending")]
25    Attending,
26    /// No events in this calendar are used for availability (but may be
27    /// included via another calendar).
28    #[serde(rename = "none")]
29    None,
30}
31
32/// Access control rights the authenticated user holds for a Calendar
33/// (draft-ietf-jmap-calendars-33 §4, after the `myRights` field definition).
34///
35/// `Default` produces all-false (no access), which is the most restrictive
36/// valid value and a safe starting point when constructing rights in tests
37/// or server code.
38///
39/// ## Invariant (spec §4)
40///
41/// If `may_write_all` is `true`, then `may_write_own`, `may_update_private`,
42/// and `may_rsvp` MUST also be `true`.  This invariant is enforced by the
43/// handler/backend layer, not this type.
44#[non_exhaustive]
45#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct CalendarRights {
48    /// User may read free/busy information for this calendar as part of a
49    /// `Principal/getAvailability` call.
50    pub may_read_free_busy: bool,
51
52    /// User may fetch the events in this calendar.
53    pub may_read_items: bool,
54
55    /// User may create, modify, or destroy all events in this calendar, or
56    /// move events to/from this calendar.  If true, `may_write_own`,
57    /// `may_update_private`, and `may_rsvp` MUST also be true.
58    pub may_write_all: bool,
59
60    /// User may create, modify, or destroy an event if they are the owner or
61    /// the event has no owner.
62    pub may_write_own: bool,
63
64    /// User may modify per-user properties on all events in this calendar,
65    /// even without general write access.
66    pub may_update_private: bool,
67
68    /// User may modify participant status and related scheduling fields for
69    /// their own ParticipantIdentity objects.
70    #[serde(rename = "mayRSVP")]
71    pub may_rsvp: bool,
72
73    /// User may modify the `shareWith` property of this calendar.
74    pub may_share: bool,
75
76    /// User may delete the calendar itself.
77    pub may_delete: bool,
78
79    /// User may modify the shared metadata of this calendar and of the
80    /// events in it (draft-ietf-jmap-metadata-04 §4.1).
81    /// `None` when the server omits it: the right is present only when the
82    /// metadata capability lists this type or a type it contains (§4.1).
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub may_set_metadata: Option<bool>,
85
86    /// Catch-all for vendor / site / private extension fields not covered
87    /// by the typed fields above. Preserves unknown fields across
88    /// deserialize/serialize round-trip per workspace extras-preservation
89    /// policy (see workspace AGENTS.md).
90    #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
91    pub extra: serde_json::Map<String, serde_json::Value>,
92}
93
94/// A JMAP Calendar object (draft-ietf-jmap-calendars-33 §4).
95///
96/// A Calendar is a named collection of CalendarEvents.  All events are
97/// associated with at least one calendar.  The `id` is immutable and
98/// server-set.
99///
100/// ## Nullable vs. absent fields
101///
102/// Per RFC 8620 §5.1, a field may be absent from a response when the
103/// `properties` argument was used to request a subset.  Fields that the
104/// spec defines as nullable (e.g. `description`, `color`, `share_with`)
105/// use `Option<T>` **without** `skip_serializing_if` so that `null` round-
106/// trips correctly.  Fields that are simply optional in the request context
107/// use `Option<T>` **with** `skip_serializing_if = "Option::is_none"`.
108#[non_exhaustive]
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct Calendar {
112    /// Server-assigned immutable identifier.
113    ///
114    /// `Option<Id>` with `skip_serializing_if` so /set create payloads
115    /// constructed by a typed caller do NOT emit a client-supplied `id`
116    /// on the wire. Per RFC 8620 §5.3, the `id` property MUST NOT be set
117    /// in the create object — the server assigns it. Matches the
118    /// in-crate sibling `CalendarEvent.id` (event.rs:43-44).
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub id: Option<Id>,
121
122    /// User-visible name for this calendar (1–255 UTF-8 octets).
123    pub name: String,
124
125    /// Optional longer-form description; `null` if not set.
126    pub description: Option<String>,
127
128    /// CSS color name or `#rrggbb`; `null` if not set.
129    pub color: Option<String>,
130
131    /// Client UI sort position; lower values sort first (0 ≤ n < 2^31).
132    #[serde(default)]
133    pub sort_order: u32,
134
135    /// Whether the user has subscribed to this calendar.
136    pub is_subscribed: bool,
137
138    /// Whether to display this calendar's events (ignored if not subscribed).
139    #[serde(default = "default_true")]
140    pub is_visible: bool,
141
142    /// Server-set; true for at most one calendar per account.
143    pub is_default: bool,
144
145    /// Which events in this calendar contribute to availability calculations.
146    pub include_in_availability: IncludeInAvailability,
147
148    /// Default alerts for timed events when `useDefaultAlerts` is true.
149    /// `Id[Alert]` (draft-ietf-jmap-calendars-33 §4): keys are alert ids;
150    /// values are Alert objects (RFC 8984 §4.5.2).
151    /// Complex sub-objects passed through as opaque JSON.
152    pub default_alerts_with_time: Option<BTreeMap<Id, serde_json::Value>>,
153
154    /// Default alerts for all-day events when `useDefaultAlerts` is true.
155    /// `Id[Alert]` (draft-ietf-jmap-calendars-33 §4): keys are alert ids;
156    /// values are Alert objects (RFC 8984 §4.5.2).
157    pub default_alerts_without_time: Option<BTreeMap<Id, serde_json::Value>>,
158
159    /// IANA Time Zone Database id; `null` to inherit from the account Principal.
160    ///
161    /// This crate does not check the value against the IANA Time Zone
162    /// Database; validating the zone name is the backend's job.
163    pub time_zone: Option<String>,
164
165    /// Map of Principal id → rights; `null` if not shared.
166    /// May only be modified by a user with `may_share` right.
167    pub share_with: Option<BTreeMap<Id, CalendarRights>>,
168
169    /// Access rights the authenticated user holds for this calendar (server-set).
170    pub my_rights: CalendarRights,
171
172    /// Shared metadata, keyed by namespace identifier
173    /// (draft-ietf-jmap-metadata-04 §2). Each value is a JSON object (§2.1).
174    /// `None` when absent, which -04 §2 treats as `{}`; in an update a
175    /// `null` value "is equivalent to {}" (-04 §3.2) and also decodes as
176    /// `None`.
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub metadata: Option<serde_json::Map<String, serde_json::Value>>,
179
180    /// The authenticated user's own metadata, keyed by namespace
181    /// identifier (draft-ietf-jmap-metadata-04 §2). Same shape and `None`
182    /// handling as `metadata`.
183    #[serde(default, skip_serializing_if = "Option::is_none")]
184    pub private_metadata: Option<serde_json::Map<String, serde_json::Value>>,
185
186    /// Catch-all for vendor / site / private extension fields not covered
187    /// by the typed fields above. Preserves unknown fields across
188    /// deserialize/serialize round-trip per workspace extras-preservation
189    /// policy (see workspace AGENTS.md).
190    #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
191    pub extra: serde_json::Map<String, serde_json::Value>,
192}
193
194/// Serde default for `Calendar::is_visible` ("default: true").
195fn default_true() -> bool {
196    true
197}