nylas-types 0.1.1

Type definitions for Nylas API v3
Documentation
//! Calendar types for the Nylas API v3.

use serde::{Deserialize, Serialize};

use crate::{CalendarId, GrantId, ResourceId};

/// A calendar object from the Nylas API.
///
/// Calendars represent a user's calendars from various providers (Google, Microsoft, etc.).
///
/// # Example
///
/// ```
/// # use nylas_types::{Calendar, CalendarId, GrantId};
/// let calendar = Calendar {
///     id: CalendarId::new("cal_123"),
///     grant_id: GrantId::new("grant_123"),
///     name: "Work Calendar".to_string(),
///     description: Some("My work calendar".to_string()),
///     location: None,
///     timezone: "America/New_York".to_string(),
///     is_primary: Some(true),
///     read_only: false,
///     is_owned_by_user: Some(true),
///     hex_color: Some("#039be5".to_string()),
///     hex_foreground_color: Some("#ffffff".to_string()),
///     object: "calendar".to_string(),
///     metadata: None,
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Calendar {
    /// Unique identifier for the calendar.
    pub id: CalendarId,

    /// Grant ID associated with this calendar.
    pub grant_id: GrantId,

    /// The calendar's name.
    pub name: String,

    /// Description of the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    /// Location associated with the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,

    /// Timezone in IANA format (e.g., "America/New_York", "UTC").
    pub timezone: String,

    /// Whether this is the primary calendar (Google and EWS only).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_primary: Option<bool>,

    /// Whether the calendar is read-only.
    #[serde(default)]
    pub read_only: bool,

    /// Whether the calendar is owned by the user.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_owned_by_user: Option<bool>,

    /// Hexadecimal color code for the calendar (Gmail and Microsoft Graph).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hex_color: Option<String>,

    /// Hexadecimal foreground color code (Gmail and Microsoft Graph).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hex_foreground_color: Option<String>,

    /// Object type identifier (always "calendar").
    #[serde(default = "default_object_type")]
    pub object: String,

    /// Optional metadata for the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<serde_json::Value>,
}

fn default_object_type() -> String {
    "calendar".to_string()
}

/// Request body for creating a calendar.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreateCalendarRequest {
    /// The calendar's name.
    pub name: String,

    /// Description of the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    /// Location associated with the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,

    /// Timezone in IANA format.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timezone: Option<String>,

    /// Optional metadata for the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<serde_json::Value>,
}

impl CreateCalendarRequest {
    /// Create a new calendar request with a name.
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            description: None,
            location: None,
            timezone: None,
            metadata: None,
        }
    }

    /// Set the description.
    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }

    /// Set the location.
    pub fn location(mut self, location: impl Into<String>) -> Self {
        self.location = Some(location.into());
        self
    }

    /// Set the timezone.
    pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
        self.timezone = Some(timezone.into());
        self
    }

    /// Set metadata.
    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
        self.metadata = Some(metadata);
        self
    }
}

/// Request body for updating a calendar.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct UpdateCalendarRequest {
    /// The calendar's name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,

    /// Description of the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    /// Location associated with the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,

    /// Timezone in IANA format.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timezone: Option<String>,

    /// Optional metadata for the calendar.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<serde_json::Value>,
}

impl UpdateCalendarRequest {
    /// Create a new empty update request.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the name.
    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    /// Set the description.
    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }

    /// Set the location.
    pub fn location(mut self, location: impl Into<String>) -> Self {
        self.location = Some(location.into());
        self
    }

    /// Set the timezone.
    pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
        self.timezone = Some(timezone.into());
        self
    }

    /// Set metadata.
    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
        self.metadata = Some(metadata);
        self
    }
}

/// Organizational resource (conference room, equipment, etc.)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CalendarResource {
    /// Unique identifier for the resource
    pub id: ResourceId,

    /// Resource email address
    pub email: String,

    /// Resource name
    pub name: String,

    /// Resource type
    #[serde(rename = "type")]
    pub resource_type: ResourceType,

    /// Building name
    #[serde(skip_serializing_if = "Option::is_none")]
    pub building: Option<String>,

    /// Floor number
    #[serde(skip_serializing_if = "Option::is_none")]
    pub floor: Option<String>,

    /// Capacity (number of people)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub capacity: Option<i32>,

    /// Features available (projector, whiteboard, etc.)
    #[serde(default)]
    pub features: Vec<String>,

    /// Resource description
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

/// Type of organizational resource.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ResourceType {
    /// Conference room
    Room,
    /// Equipment (projector, etc.)
    Equipment,
    /// Shared workspace
    Workspace,
    /// Other resource type
    Other,
}

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

    #[test]
    fn test_calendar_creation() {
        let calendar = Calendar {
            id: CalendarId::new("cal_123"),
            grant_id: GrantId::new("grant_123"),
            name: "My Calendar".to_string(),
            description: Some("Test calendar".to_string()),
            location: None,
            timezone: "UTC".to_string(),
            is_primary: Some(true),
            read_only: false,
            is_owned_by_user: Some(true),
            hex_color: Some("#039be5".to_string()),
            hex_foreground_color: Some("#ffffff".to_string()),
            object: "calendar".to_string(),
            metadata: None,
        };

        assert_eq!(calendar.name, "My Calendar");
        assert_eq!(calendar.timezone, "UTC");
        assert_eq!(calendar.is_primary, Some(true));
    }

    #[test]
    fn test_calendar_serialization() {
        let calendar = Calendar {
            id: CalendarId::new("cal_123"),
            grant_id: GrantId::new("grant_123"),
            name: "Test".to_string(),
            description: None,
            location: None,
            timezone: "America/New_York".to_string(),
            is_primary: Some(false),
            read_only: false,
            is_owned_by_user: Some(true),
            hex_color: None,
            hex_foreground_color: None,
            object: "calendar".to_string(),
            metadata: None,
        };

        let json = serde_json::to_string(&calendar).unwrap();
        assert!(json.contains("cal_123"));
        assert!(json.contains("Test"));
        assert!(json.contains("America/New_York"));

        let deserialized: Calendar = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized, calendar);
    }

    #[test]
    fn test_create_calendar_request_builder() {
        let request = CreateCalendarRequest::new("Work Calendar")
            .description("My work events")
            .timezone("America/Los_Angeles")
            .location("Office");

        assert_eq!(request.name, "Work Calendar");
        assert_eq!(request.description, Some("My work events".to_string()));
        assert_eq!(request.timezone, Some("America/Los_Angeles".to_string()));
        assert_eq!(request.location, Some("Office".to_string()));
    }

    #[test]
    fn test_update_calendar_request_builder() {
        let request = UpdateCalendarRequest::new()
            .name("Updated Calendar")
            .timezone("UTC");

        assert_eq!(request.name, Some("Updated Calendar".to_string()));
        assert_eq!(request.timezone, Some("UTC".to_string()));
        assert_eq!(request.description, None);
    }

    #[test]
    fn test_create_calendar_request_serialization() {
        let request = CreateCalendarRequest::new("Test Calendar").description("Test description");

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("Test Calendar"));
        assert!(json.contains("Test description"));

        let deserialized: CreateCalendarRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized, request);
    }

    #[test]
    fn test_resource_id_creation() {
        let id = ResourceId::new("resource_123");
        assert_eq!(id.as_str(), "resource_123");
    }

    #[test]
    fn test_resource_type_serialization() {
        let room = ResourceType::Room;
        let json = serde_json::to_string(&room).unwrap();
        assert_eq!(json, "\"room\"");
    }

    #[test]
    fn test_resource_deserialization() {
        let json = r#"{
            "id": "res_123",
            "email": "room1@company.com",
            "name": "Conference Room 1",
            "type": "room",
            "capacity": 10,
            "features": ["projector", "whiteboard"]
        }"#;

        let resource: CalendarResource = serde_json::from_str(json).unwrap();
        assert_eq!(resource.name, "Conference Room 1");
        assert_eq!(resource.capacity, Some(10));
    }
}