nylas_types/
calendar.rs

1//! Calendar types for the Nylas API v3.
2
3use serde::{Deserialize, Serialize};
4
5use crate::{CalendarId, GrantId};
6
7/// A calendar object from the Nylas API.
8///
9/// Calendars represent a user's calendars from various providers (Google, Microsoft, etc.).
10///
11/// # Example
12///
13/// ```
14/// # use nylas_types::{Calendar, CalendarId, GrantId};
15/// let calendar = Calendar {
16///     id: CalendarId::new("cal_123"),
17///     grant_id: GrantId::new("grant_123"),
18///     name: "Work Calendar".to_string(),
19///     description: Some("My work calendar".to_string()),
20///     location: None,
21///     timezone: "America/New_York".to_string(),
22///     is_primary: Some(true),
23///     read_only: false,
24///     is_owned_by_user: Some(true),
25///     hex_color: Some("#039be5".to_string()),
26///     hex_foreground_color: Some("#ffffff".to_string()),
27///     object: "calendar".to_string(),
28///     metadata: None,
29/// };
30/// ```
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct Calendar {
33    /// Unique identifier for the calendar.
34    pub id: CalendarId,
35
36    /// Grant ID associated with this calendar.
37    pub grant_id: GrantId,
38
39    /// The calendar's name.
40    pub name: String,
41
42    /// Description of the calendar.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub description: Option<String>,
45
46    /// Location associated with the calendar.
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub location: Option<String>,
49
50    /// Timezone in IANA format (e.g., "America/New_York", "UTC").
51    pub timezone: String,
52
53    /// Whether this is the primary calendar (Google and EWS only).
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub is_primary: Option<bool>,
56
57    /// Whether the calendar is read-only.
58    #[serde(default)]
59    pub read_only: bool,
60
61    /// Whether the calendar is owned by the user.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub is_owned_by_user: Option<bool>,
64
65    /// Hexadecimal color code for the calendar (Gmail and Microsoft Graph).
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub hex_color: Option<String>,
68
69    /// Hexadecimal foreground color code (Gmail and Microsoft Graph).
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub hex_foreground_color: Option<String>,
72
73    /// Object type identifier (always "calendar").
74    #[serde(default = "default_object_type")]
75    pub object: String,
76
77    /// Optional metadata for the calendar.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub metadata: Option<serde_json::Value>,
80}
81
82fn default_object_type() -> String {
83    "calendar".to_string()
84}
85
86/// Request body for creating a calendar.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub struct CreateCalendarRequest {
89    /// The calendar's name.
90    pub name: String,
91
92    /// Description of the calendar.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub description: Option<String>,
95
96    /// Location associated with the calendar.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub location: Option<String>,
99
100    /// Timezone in IANA format.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub timezone: Option<String>,
103
104    /// Optional metadata for the calendar.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub metadata: Option<serde_json::Value>,
107}
108
109impl CreateCalendarRequest {
110    /// Create a new calendar request with a name.
111    pub fn new(name: impl Into<String>) -> Self {
112        Self {
113            name: name.into(),
114            description: None,
115            location: None,
116            timezone: None,
117            metadata: None,
118        }
119    }
120
121    /// Set the description.
122    pub fn description(mut self, description: impl Into<String>) -> Self {
123        self.description = Some(description.into());
124        self
125    }
126
127    /// Set the location.
128    pub fn location(mut self, location: impl Into<String>) -> Self {
129        self.location = Some(location.into());
130        self
131    }
132
133    /// Set the timezone.
134    pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
135        self.timezone = Some(timezone.into());
136        self
137    }
138
139    /// Set metadata.
140    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
141        self.metadata = Some(metadata);
142        self
143    }
144}
145
146/// Request body for updating a calendar.
147#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
148pub struct UpdateCalendarRequest {
149    /// The calendar's name.
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub name: Option<String>,
152
153    /// Description of the calendar.
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub description: Option<String>,
156
157    /// Location associated with the calendar.
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub location: Option<String>,
160
161    /// Timezone in IANA format.
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub timezone: Option<String>,
164
165    /// Optional metadata for the calendar.
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub metadata: Option<serde_json::Value>,
168}
169
170impl UpdateCalendarRequest {
171    /// Create a new empty update request.
172    pub fn new() -> Self {
173        Self::default()
174    }
175
176    /// Set the name.
177    pub fn name(mut self, name: impl Into<String>) -> Self {
178        self.name = Some(name.into());
179        self
180    }
181
182    /// Set the description.
183    pub fn description(mut self, description: impl Into<String>) -> Self {
184        self.description = Some(description.into());
185        self
186    }
187
188    /// Set the location.
189    pub fn location(mut self, location: impl Into<String>) -> Self {
190        self.location = Some(location.into());
191        self
192    }
193
194    /// Set the timezone.
195    pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
196        self.timezone = Some(timezone.into());
197        self
198    }
199
200    /// Set metadata.
201    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
202        self.metadata = Some(metadata);
203        self
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_calendar_creation() {
213        let calendar = Calendar {
214            id: CalendarId::new("cal_123"),
215            grant_id: GrantId::new("grant_123"),
216            name: "My Calendar".to_string(),
217            description: Some("Test calendar".to_string()),
218            location: None,
219            timezone: "UTC".to_string(),
220            is_primary: Some(true),
221            read_only: false,
222            is_owned_by_user: Some(true),
223            hex_color: Some("#039be5".to_string()),
224            hex_foreground_color: Some("#ffffff".to_string()),
225            object: "calendar".to_string(),
226            metadata: None,
227        };
228
229        assert_eq!(calendar.name, "My Calendar");
230        assert_eq!(calendar.timezone, "UTC");
231        assert_eq!(calendar.is_primary, Some(true));
232    }
233
234    #[test]
235    fn test_calendar_serialization() {
236        let calendar = Calendar {
237            id: CalendarId::new("cal_123"),
238            grant_id: GrantId::new("grant_123"),
239            name: "Test".to_string(),
240            description: None,
241            location: None,
242            timezone: "America/New_York".to_string(),
243            is_primary: Some(false),
244            read_only: false,
245            is_owned_by_user: Some(true),
246            hex_color: None,
247            hex_foreground_color: None,
248            object: "calendar".to_string(),
249            metadata: None,
250        };
251
252        let json = serde_json::to_string(&calendar).unwrap();
253        assert!(json.contains("cal_123"));
254        assert!(json.contains("Test"));
255        assert!(json.contains("America/New_York"));
256
257        let deserialized: Calendar = serde_json::from_str(&json).unwrap();
258        assert_eq!(deserialized, calendar);
259    }
260
261    #[test]
262    fn test_create_calendar_request_builder() {
263        let request = CreateCalendarRequest::new("Work Calendar")
264            .description("My work events")
265            .timezone("America/Los_Angeles")
266            .location("Office");
267
268        assert_eq!(request.name, "Work Calendar");
269        assert_eq!(request.description, Some("My work events".to_string()));
270        assert_eq!(request.timezone, Some("America/Los_Angeles".to_string()));
271        assert_eq!(request.location, Some("Office".to_string()));
272    }
273
274    #[test]
275    fn test_update_calendar_request_builder() {
276        let request = UpdateCalendarRequest::new()
277            .name("Updated Calendar")
278            .timezone("UTC");
279
280        assert_eq!(request.name, Some("Updated Calendar".to_string()));
281        assert_eq!(request.timezone, Some("UTC".to_string()));
282        assert_eq!(request.description, None);
283    }
284
285    #[test]
286    fn test_create_calendar_request_serialization() {
287        let request = CreateCalendarRequest::new("Test Calendar").description("Test description");
288
289        let json = serde_json::to_string(&request).unwrap();
290        assert!(json.contains("Test Calendar"));
291        assert!(json.contains("Test description"));
292
293        let deserialized: CreateCalendarRequest = serde_json::from_str(&json).unwrap();
294        assert_eq!(deserialized, request);
295    }
296}