Skip to main content

nylas_types/
availability.rs

1//! Availability and free-busy types for the Nylas API v3.
2
3use serde::{Deserialize, Serialize};
4
5/// A time slot representing available or free-busy time for participants.
6///
7/// # Example
8///
9/// ```
10/// # use nylas_types::TimeSlot;
11/// let slot = TimeSlot {
12///     emails: vec!["user@example.com".to_string()],
13///     start_time: 1234567890,
14///     end_time: 1234571490,
15/// };
16/// ```
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct TimeSlot {
19    /// List of email addresses for which this time slot is available.
20    pub emails: Vec<String>,
21
22    /// Unix timestamp indicating the start of the time slot.
23    pub start_time: i64,
24
25    /// Unix timestamp indicating the end of the time slot.
26    pub end_time: i64,
27}
28
29impl TimeSlot {
30    /// Create a new time slot.
31    pub fn new(emails: Vec<String>, start_time: i64, end_time: i64) -> Self {
32        Self {
33            emails,
34            start_time,
35            end_time,
36        }
37    }
38}
39
40/// Response from the availability endpoint containing available time slots.
41///
42/// # Example
43///
44/// ```
45/// # use nylas_types::{AvailabilityResponse, TimeSlot};
46/// let response = AvailabilityResponse {
47///     time_slots: vec![
48///         TimeSlot::new(
49///             vec!["user@example.com".to_string()],
50///             1234567890,
51///             1234571490,
52///         ),
53///     ],
54/// };
55/// ```
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct AvailabilityResponse {
58    /// List of available time slots for the queried participants.
59    pub time_slots: Vec<TimeSlot>,
60}
61
62impl AvailabilityResponse {
63    /// Create a new availability response.
64    pub fn new(time_slots: Vec<TimeSlot>) -> Self {
65        Self { time_slots }
66    }
67}
68
69/// Request for querying availability/free-busy information.
70///
71/// # Example
72///
73/// ```
74/// # use nylas_types::AvailabilityRequest;
75/// let request = AvailabilityRequest::builder()
76///     .email("user@example.com")
77///     .duration_minutes(30)
78///     .start_time(1234567890)
79///     .end_time(1234654290)
80///     .build();
81/// ```
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83pub struct AvailabilityRequest {
84    /// List of email addresses to check availability for.
85    pub emails: Vec<String>,
86
87    /// Duration of requested availability slots in minutes.
88    pub duration_minutes: u32,
89
90    /// Optional interval between availability slots in minutes.
91    /// If not specified, defaults to the duration.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub interval_minutes: Option<u32>,
94
95    /// Unix timestamp indicating the start of the time range to check.
96    pub start_time: i64,
97
98    /// Unix timestamp indicating the end of the time range to check.
99    pub end_time: i64,
100}
101
102impl AvailabilityRequest {
103    /// Create a builder for constructing an AvailabilityRequest.
104    pub fn builder() -> AvailabilityRequestBuilder {
105        AvailabilityRequestBuilder {
106            emails: Vec::new(),
107            duration_minutes: None,
108            interval_minutes: None,
109            start_time: None,
110            end_time: None,
111        }
112    }
113
114    /// Create a new availability request with required fields.
115    pub fn new(emails: Vec<String>, duration_minutes: u32, start_time: i64, end_time: i64) -> Self {
116        Self {
117            emails,
118            duration_minutes,
119            interval_minutes: None,
120            start_time,
121            end_time,
122        }
123    }
124}
125
126/// Builder for constructing AvailabilityRequest.
127#[derive(Debug, Clone)]
128pub struct AvailabilityRequestBuilder {
129    emails: Vec<String>,
130    duration_minutes: Option<u32>,
131    interval_minutes: Option<u32>,
132    start_time: Option<i64>,
133    end_time: Option<i64>,
134}
135
136impl AvailabilityRequestBuilder {
137    /// Add a single email address to the request.
138    pub fn email(mut self, email: impl Into<String>) -> Self {
139        self.emails.push(email.into());
140        self
141    }
142
143    /// Add multiple email addresses to the request.
144    pub fn emails(mut self, emails: Vec<String>) -> Self {
145        self.emails.extend(emails);
146        self
147    }
148
149    /// Set the duration of requested availability slots in minutes.
150    pub fn duration_minutes(mut self, duration: u32) -> Self {
151        self.duration_minutes = Some(duration);
152        self
153    }
154
155    /// Set the interval between availability slots in minutes.
156    pub fn interval_minutes(mut self, interval: u32) -> Self {
157        self.interval_minutes = Some(interval);
158        self
159    }
160
161    /// Set the start time for the availability query.
162    pub fn start_time(mut self, start_time: i64) -> Self {
163        self.start_time = Some(start_time);
164        self
165    }
166
167    /// Set the end time for the availability query.
168    pub fn end_time(mut self, end_time: i64) -> Self {
169        self.end_time = Some(end_time);
170        self
171    }
172
173    /// Build the AvailabilityRequest.
174    ///
175    /// # Panics
176    ///
177    /// Panics if required fields (emails, duration_minutes, start_time, end_time) are not set.
178    pub fn build(self) -> AvailabilityRequest {
179        AvailabilityRequest {
180            emails: if self.emails.is_empty() {
181                panic!("At least one email address is required")
182            } else {
183                self.emails
184            },
185            duration_minutes: self.duration_minutes.expect("duration_minutes is required"),
186            interval_minutes: self.interval_minutes,
187            start_time: self.start_time.expect("start_time is required"),
188            end_time: self.end_time.expect("end_time is required"),
189        }
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_time_slot_creation() {
199        let slot = TimeSlot::new(vec!["user@example.com".to_string()], 1234567890, 1234571490);
200
201        assert_eq!(slot.emails.len(), 1);
202        assert_eq!(slot.emails[0], "user@example.com");
203        assert_eq!(slot.start_time, 1234567890);
204        assert_eq!(slot.end_time, 1234571490);
205    }
206
207    #[test]
208    fn test_time_slot_serialization() {
209        let slot = TimeSlot::new(vec!["user@example.com".to_string()], 1234567890, 1234571490);
210
211        let json = serde_json::to_string(&slot).unwrap();
212        assert!(json.contains("user@example.com"));
213        assert!(json.contains("1234567890"));
214        assert!(json.contains("1234571490"));
215
216        let deserialized: TimeSlot = serde_json::from_str(&json).unwrap();
217        assert_eq!(deserialized, slot);
218    }
219
220    #[test]
221    fn test_availability_response_creation() {
222        let slots = vec![TimeSlot::new(
223            vec!["user@example.com".to_string()],
224            1234567890,
225            1234571490,
226        )];
227
228        let response = AvailabilityResponse::new(slots.clone());
229        assert_eq!(response.time_slots.len(), 1);
230        assert_eq!(response.time_slots[0], slots[0]);
231    }
232
233    #[test]
234    fn test_availability_response_serialization() {
235        let response = AvailabilityResponse::new(vec![TimeSlot::new(
236            vec!["user@example.com".to_string()],
237            1234567890,
238            1234571490,
239        )]);
240
241        let json = serde_json::to_string(&response).unwrap();
242        assert!(json.contains("user@example.com"));
243
244        let deserialized: AvailabilityResponse = serde_json::from_str(&json).unwrap();
245        assert_eq!(deserialized, response);
246    }
247
248    #[test]
249    fn test_availability_request_new() {
250        let request = AvailabilityRequest::new(
251            vec!["user@example.com".to_string()],
252            30,
253            1234567890,
254            1234654290,
255        );
256
257        assert_eq!(request.emails.len(), 1);
258        assert_eq!(request.emails[0], "user@example.com");
259        assert_eq!(request.duration_minutes, 30);
260        assert_eq!(request.start_time, 1234567890);
261        assert_eq!(request.end_time, 1234654290);
262        assert_eq!(request.interval_minutes, None);
263    }
264
265    #[test]
266    fn test_availability_request_builder_single_email() {
267        let request = AvailabilityRequest::builder()
268            .email("user@example.com")
269            .duration_minutes(30)
270            .start_time(1234567890)
271            .end_time(1234654290)
272            .build();
273
274        assert_eq!(request.emails.len(), 1);
275        assert_eq!(request.emails[0], "user@example.com");
276        assert_eq!(request.duration_minutes, 30);
277    }
278
279    #[test]
280    fn test_availability_request_builder_multiple_emails() {
281        let request = AvailabilityRequest::builder()
282            .email("user1@example.com")
283            .email("user2@example.com")
284            .duration_minutes(45)
285            .interval_minutes(15)
286            .start_time(1234567890)
287            .end_time(1234654290)
288            .build();
289
290        assert_eq!(request.emails.len(), 2);
291        assert_eq!(request.duration_minutes, 45);
292        assert_eq!(request.interval_minutes, Some(15));
293    }
294
295    #[test]
296    fn test_availability_request_builder_with_emails_vec() {
297        let emails = vec![
298            "user1@example.com".to_string(),
299            "user2@example.com".to_string(),
300        ];
301
302        let request = AvailabilityRequest::builder()
303            .emails(emails)
304            .duration_minutes(60)
305            .start_time(1234567890)
306            .end_time(1234654290)
307            .build();
308
309        assert_eq!(request.emails.len(), 2);
310        assert_eq!(request.duration_minutes, 60);
311    }
312
313    #[test]
314    #[should_panic(expected = "At least one email address is required")]
315    fn test_availability_request_builder_no_emails() {
316        AvailabilityRequest::builder()
317            .duration_minutes(30)
318            .start_time(1234567890)
319            .end_time(1234654290)
320            .build();
321    }
322
323    #[test]
324    #[should_panic(expected = "duration_minutes is required")]
325    fn test_availability_request_builder_no_duration() {
326        AvailabilityRequest::builder()
327            .email("user@example.com")
328            .start_time(1234567890)
329            .end_time(1234654290)
330            .build();
331    }
332
333    #[test]
334    #[should_panic(expected = "start_time is required")]
335    fn test_availability_request_builder_no_start_time() {
336        AvailabilityRequest::builder()
337            .email("user@example.com")
338            .duration_minutes(30)
339            .end_time(1234654290)
340            .build();
341    }
342
343    #[test]
344    fn test_availability_request_serialization() {
345        let request = AvailabilityRequest::builder()
346            .email("user@example.com")
347            .duration_minutes(30)
348            .interval_minutes(15)
349            .start_time(1234567890)
350            .end_time(1234654290)
351            .build();
352
353        let json = serde_json::to_string(&request).unwrap();
354        assert!(json.contains("user@example.com"));
355        assert!(json.contains("30"));
356        assert!(json.contains("15"));
357
358        let deserialized: AvailabilityRequest = serde_json::from_str(&json).unwrap();
359        assert_eq!(deserialized, request);
360    }
361
362    #[test]
363    fn test_availability_request_serialization_no_interval() {
364        let request = AvailabilityRequest::new(
365            vec!["user@example.com".to_string()],
366            30,
367            1234567890,
368            1234654290,
369        );
370
371        let json = serde_json::to_string(&request).unwrap();
372        // interval_minutes should not be serialized when None
373        assert!(!json.contains("interval_minutes"));
374
375        let deserialized: AvailabilityRequest = serde_json::from_str(&json).unwrap();
376        assert_eq!(deserialized, request);
377    }
378}