1use serde::{Deserialize, Serialize};
4
5use crate::{CalendarId, EmailAddress, EventId, GrantId};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50pub struct Event {
51 pub id: EventId,
53
54 pub grant_id: GrantId,
56
57 pub calendar_id: CalendarId,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub title: Option<String>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub description: Option<String>,
67
68 pub when: When,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub location: Option<String>,
74
75 #[serde(default = "default_busy")]
77 pub busy: bool,
78
79 #[serde(default)]
81 pub status: EventStatus,
82
83 #[serde(default)]
85 pub participants: Vec<Participant>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub organizer: Option<EmailAddress>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub creator: Option<EmailAddress>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub conferencing: Option<Conferencing>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub recurrence: Option<Recurrence>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub reminders: Option<Reminders>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub capacity: Option<i32>,
110
111 #[serde(default)]
113 pub hide_participants: bool,
114
115 #[serde(default)]
117 pub read_only: bool,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub html_link: Option<String>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub ical_uid: Option<String>,
126
127 #[serde(default)]
129 pub resources: Vec<Resource>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub visibility: Option<EventVisibility>,
134
135 #[serde(default = "default_event_object_type")]
137 pub object: String,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub metadata: Option<serde_json::Value>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub created_at: Option<i64>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub updated_at: Option<i64>,
150}
151
152fn default_busy() -> bool {
153 true
154}
155
156fn default_event_object_type() -> String {
157 "event".to_string()
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(tag = "object", rename_all = "lowercase")]
165pub enum When {
166 Timespan {
168 start_time: i64,
170
171 end_time: i64,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 start_timezone: Option<String>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 end_timezone: Option<String>,
181 },
182
183 Datespan {
185 start_date: String,
187
188 end_date: String,
190 },
191
192 Date {
194 date: String,
196 },
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
201#[serde(rename_all = "lowercase")]
202pub enum EventStatus {
203 #[default]
205 Confirmed,
206
207 Tentative,
209
210 Cancelled,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
216#[serde(rename_all = "lowercase")]
217pub enum EventVisibility {
218 Default,
220
221 Public,
223
224 Private,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
230pub struct Participant {
231 pub email: String,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub name: Option<String>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub status: Option<ParticipantStatus>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub comment: Option<String>,
245
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub phone_number: Option<String>,
249}
250
251impl Participant {
252 pub fn new(email: impl Into<String>) -> Self {
254 Self {
255 email: email.into(),
256 name: None,
257 status: None,
258 comment: None,
259 phone_number: None,
260 }
261 }
262
263 pub fn name(mut self, name: impl Into<String>) -> Self {
265 self.name = Some(name.into());
266 self
267 }
268
269 pub fn status(mut self, status: ParticipantStatus) -> Self {
271 self.status = Some(status);
272 self
273 }
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "lowercase")]
279pub enum ParticipantStatus {
280 Yes,
282
283 No,
285
286 Maybe,
288
289 Noreply,
291}
292
293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
295pub struct Conferencing {
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub provider: Option<String>,
299
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub details: Option<ConferencingDetails>,
303}
304
305#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
307pub struct ConferencingDetails {
308 pub url: String,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub meeting_code: Option<String>,
314
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub password: Option<String>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
321 pub pin: Option<String>,
322
323 #[serde(default)]
325 pub phone: Vec<String>,
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
330pub struct Recurrence {
331 #[serde(rename = "rrule")]
333 pub rrule: Vec<String>,
334
335 #[serde(default)]
337 pub exdate: Vec<String>,
338}
339
340#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
342pub struct Reminders {
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub use_default: Option<bool>,
346
347 #[serde(default)]
349 pub overrides: Vec<ReminderOverride>,
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
354pub struct ReminderOverride {
355 pub reminder_minutes: i32,
357
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub reminder_method: Option<String>,
361}
362
363#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
365pub struct Resource {
366 pub email: String,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub name: Option<String>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub object: Option<String>,
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn test_event_creation() {
384 let event = Event {
385 id: EventId::new("event_123"),
386 grant_id: GrantId::new("grant_123"),
387 calendar_id: CalendarId::new("cal_123"),
388 title: Some("Test Event".to_string()),
389 description: None,
390 when: When::Timespan {
391 start_time: 1234567890,
392 end_time: 1234571490,
393 start_timezone: Some("UTC".to_string()),
394 end_timezone: Some("UTC".to_string()),
395 },
396 location: None,
397 busy: true,
398 status: EventStatus::Confirmed,
399 participants: vec![],
400 organizer: None,
401 creator: None,
402 conferencing: None,
403 recurrence: None,
404 reminders: None,
405 capacity: None,
406 hide_participants: false,
407 read_only: false,
408 html_link: None,
409 ical_uid: None,
410 resources: vec![],
411 visibility: None,
412 object: "event".to_string(),
413 metadata: None,
414 created_at: None,
415 updated_at: None,
416 };
417
418 assert_eq!(event.title, Some("Test Event".to_string()));
419 assert!(event.busy);
420 assert_eq!(event.status, EventStatus::Confirmed);
421 }
422
423 #[test]
424 fn test_when_timespan_serialization() {
425 let when = When::Timespan {
426 start_time: 1234567890,
427 end_time: 1234571490,
428 start_timezone: Some("America/New_York".to_string()),
429 end_timezone: Some("America/New_York".to_string()),
430 };
431
432 let json = serde_json::to_string(&when).unwrap();
433 assert!(json.contains("timespan"));
434 assert!(json.contains("1234567890"));
435 assert!(json.contains("America/New_York"));
436
437 let deserialized: When = serde_json::from_str(&json).unwrap();
438 assert_eq!(deserialized, when);
439 }
440
441 #[test]
442 fn test_when_datespan_serialization() {
443 let when = When::Datespan {
444 start_date: "2024-01-01".to_string(),
445 end_date: "2024-01-03".to_string(),
446 };
447
448 let json = serde_json::to_string(&when).unwrap();
449 assert!(json.contains("datespan"));
450 assert!(json.contains("2024-01-01"));
451 assert!(json.contains("2024-01-03"));
452
453 let deserialized: When = serde_json::from_str(&json).unwrap();
454 assert_eq!(deserialized, when);
455 }
456
457 #[test]
458 fn test_when_date_serialization() {
459 let when = When::Date {
460 date: "2024-01-15".to_string(),
461 };
462
463 let json = serde_json::to_string(&when).unwrap();
464 assert!(json.contains("date"));
465 assert!(json.contains("2024-01-15"));
466
467 let deserialized: When = serde_json::from_str(&json).unwrap();
468 assert_eq!(deserialized, when);
469 }
470
471 #[test]
472 fn test_participant_builder() {
473 let participant = Participant::new("user@example.com")
474 .name("John Doe")
475 .status(ParticipantStatus::Yes);
476
477 assert_eq!(participant.email, "user@example.com");
478 assert_eq!(participant.name, Some("John Doe".to_string()));
479 assert_eq!(participant.status, Some(ParticipantStatus::Yes));
480 }
481
482 #[test]
483 fn test_participant_status_serialization() {
484 let status = ParticipantStatus::Yes;
485 let json = serde_json::to_string(&status).unwrap();
486 assert_eq!(json, "\"yes\"");
487
488 let status = ParticipantStatus::Maybe;
489 let json = serde_json::to_string(&status).unwrap();
490 assert_eq!(json, "\"maybe\"");
491 }
492
493 #[test]
494 fn test_event_status_serialization() {
495 let status = EventStatus::Confirmed;
496 let json = serde_json::to_string(&status).unwrap();
497 assert_eq!(json, "\"confirmed\"");
498
499 let status = EventStatus::Cancelled;
500 let json = serde_json::to_string(&status).unwrap();
501 assert_eq!(json, "\"cancelled\"");
502 }
503
504 #[test]
505 fn test_conferencing_serialization() {
506 let conferencing = Conferencing {
507 provider: Some("Google Meet".to_string()),
508 details: Some(ConferencingDetails {
509 url: "https://meet.google.com/abc-def-ghi".to_string(),
510 meeting_code: Some("abc-def-ghi".to_string()),
511 password: None,
512 pin: None,
513 phone: vec![],
514 }),
515 };
516
517 let json = serde_json::to_string(&conferencing).unwrap();
518 assert!(json.contains("Google Meet"));
519 assert!(json.contains("meet.google.com"));
520
521 let deserialized: Conferencing = serde_json::from_str(&json).unwrap();
522 assert_eq!(deserialized, conferencing);
523 }
524}