nylas-types 0.1.1

Type definitions for Nylas API v3
Documentation
//! Free/Busy types for calendar availability.
//!
//! Query multiple calendars for free/busy information.

use serde::{Deserialize, Serialize};

/// Request to query free/busy information.
///
/// # Example
///
/// ```
/// # use nylas_types::FreeBusyRequest;
/// let request = FreeBusyRequest::builder()
///     .emails(vec!["user1@example.com".to_string(), "user2@example.com".to_string()])
///     .start_time(1735689600)
///     .end_time(1735776000)
///     .build();
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FreeBusyRequest {
    /// Email addresses to query.
    pub emails: Vec<String>,

    /// Start time (Unix timestamp).
    pub start_time: i64,

    /// End time (Unix timestamp).
    pub end_time: i64,
}

impl FreeBusyRequest {
    /// Create a new builder for free/busy requests.
    pub fn builder() -> FreeBusyRequestBuilder {
        FreeBusyRequestBuilder::default()
    }
}

/// Builder for FreeBusyRequest.
#[derive(Debug, Default)]
pub struct FreeBusyRequestBuilder {
    emails: Vec<String>,
    start_time: Option<i64>,
    end_time: Option<i64>,
}

impl FreeBusyRequestBuilder {
    /// Set email addresses to query.
    pub fn emails(mut self, emails: Vec<String>) -> Self {
        self.emails = emails;
        self
    }

    /// Add a single email address.
    pub fn add_email(mut self, email: impl Into<String>) -> Self {
        self.emails.push(email.into());
        self
    }

    /// Set start time.
    pub fn start_time(mut self, timestamp: i64) -> Self {
        self.start_time = Some(timestamp);
        self
    }

    /// Set end time.
    pub fn end_time(mut self, timestamp: i64) -> Self {
        self.end_time = Some(timestamp);
        self
    }

    /// Build the free/busy request.
    ///
    /// # Panics
    ///
    /// Panics if emails, start_time, or end_time are not set.
    pub fn build(self) -> FreeBusyRequest {
        FreeBusyRequest {
            emails: self.emails,
            start_time: self.start_time.expect("start_time is required"),
            end_time: self.end_time.expect("end_time is required"),
        }
    }
}

/// Response from free/busy query.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FreeBusyResponse {
    /// Free/busy information by email address.
    pub calendars: Vec<FreeBusyCalendar>,
}

/// Free/busy information for a single calendar.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FreeBusyCalendar {
    /// Email address of the calendar owner.
    pub email: String,

    /// Calendar ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub calendar_id: Option<String>,

    /// Busy time slots.
    pub time_slots: Vec<FreeBusyTimeSlot>,
}

/// A busy time slot in a calendar.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FreeBusyTimeSlot {
    /// Start time (Unix timestamp).
    pub start_time: i64,

    /// End time (Unix timestamp).
    pub end_time: i64,

    /// Status of the time slot.
    pub status: BusyStatus,

    /// Event ID if available.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_id: Option<String>,
}

/// Status of a time slot.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BusyStatus {
    /// Time slot is busy.
    Busy,

    /// Time slot is tentatively booked.
    Tentative,

    /// Time slot is free.
    Free,

    /// Out of office.
    #[serde(rename = "out-of-office")]
    OutOfOffice,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_free_busy_request_builder() {
        let request = FreeBusyRequest::builder()
            .emails(vec!["user1@example.com".to_string()])
            .start_time(1735689600)
            .end_time(1735776000)
            .build();

        assert_eq!(request.emails.len(), 1);
        assert_eq!(request.start_time, 1735689600);
        assert_eq!(request.end_time, 1735776000);
    }

    #[test]
    fn test_free_busy_request_add_email() {
        let request = FreeBusyRequest::builder()
            .add_email("user1@example.com")
            .add_email("user2@example.com")
            .start_time(1735689600)
            .end_time(1735776000)
            .build();

        assert_eq!(request.emails.len(), 2);
    }

    #[test]
    #[should_panic(expected = "start_time is required")]
    fn test_free_busy_request_missing_start_time() {
        FreeBusyRequest::builder()
            .emails(vec!["user@example.com".to_string()])
            .end_time(1735776000)
            .build();
    }

    #[test]
    #[should_panic(expected = "end_time is required")]
    fn test_free_busy_request_missing_end_time() {
        FreeBusyRequest::builder()
            .emails(vec!["user@example.com".to_string()])
            .start_time(1735689600)
            .build();
    }

    #[test]
    fn test_free_busy_request_serialization() {
        let request = FreeBusyRequest::builder()
            .emails(vec!["user@example.com".to_string()])
            .start_time(1735689600)
            .end_time(1735776000)
            .build();

        let json = serde_json::to_string(&request).unwrap();
        let deserialized: FreeBusyRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(request, deserialized);
    }

    #[test]
    fn test_busy_status_serialization() {
        let status = BusyStatus::Busy;
        let json = serde_json::to_string(&status).unwrap();
        assert_eq!(json, "\"busy\"");

        let status = BusyStatus::OutOfOffice;
        let json = serde_json::to_string(&status).unwrap();
        assert_eq!(json, "\"out-of-office\"");
    }

    #[test]
    fn test_free_busy_response_serialization() {
        let response = FreeBusyResponse {
            calendars: vec![FreeBusyCalendar {
                email: "user@example.com".to_string(),
                calendar_id: Some("cal_123".to_string()),
                time_slots: vec![FreeBusyTimeSlot {
                    start_time: 1735689600,
                    end_time: 1735693200,
                    status: BusyStatus::Busy,
                    event_id: Some("event_456".to_string()),
                }],
            }],
        };

        let json = serde_json::to_string(&response).unwrap();
        let deserialized: FreeBusyResponse = serde_json::from_str(&json).unwrap();

        assert_eq!(response, deserialized);
    }
}