nylas-types 0.1.1

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

use serde::{Deserialize, Serialize};

/// An attachment object from the Nylas API.
///
/// Attachments are files attached to messages or drafts.
///
/// # Example
///
/// ```
/// # use nylas_types::Attachment;
/// let attachment = Attachment {
///     id: "attachment_123".to_string(),
///     grant_id: Some("grant_123".to_string()),
///     filename: Some("document.pdf".to_string()),
///     content_type: Some("application/pdf".to_string()),
///     size: Some(12345),
///     content_id: None,
///     is_inline: Some(false),
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Attachment {
    /// Unique identifier for the attachment.
    pub id: String,

    /// Grant ID associated with this attachment.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub grant_id: Option<String>,

    /// Filename of the attachment.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub filename: Option<String>,

    /// MIME type of the attachment.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_type: Option<String>,

    /// Size of the attachment in bytes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<u64>,

    /// Content ID for inline attachments (used in HTML emails).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_id: Option<String>,

    /// Whether the attachment is displayed inline.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_inline: Option<bool>,
}

impl Attachment {
    /// Create a new attachment builder.
    pub fn builder() -> AttachmentBuilder {
        AttachmentBuilder::default()
    }
}

/// Builder for creating attachments.
#[derive(Debug, Clone, Default)]
pub struct AttachmentBuilder {
    id: Option<String>,
    grant_id: Option<String>,
    filename: Option<String>,
    content_type: Option<String>,
    size: Option<u64>,
    content_id: Option<String>,
    is_inline: Option<bool>,
}

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

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

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

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

    /// Set the size in bytes.
    pub fn size(mut self, size: u64) -> Self {
        self.size = Some(size);
        self
    }

    /// Set the content ID for inline attachments.
    pub fn content_id(mut self, content_id: impl Into<String>) -> Self {
        self.content_id = Some(content_id.into());
        self
    }

    /// Set whether the attachment is inline.
    pub fn is_inline(mut self, is_inline: bool) -> Self {
        self.is_inline = Some(is_inline);
        self
    }

    /// Build the attachment.
    ///
    /// # Panics
    ///
    /// Panics if the ID is not set.
    pub fn build(self) -> Attachment {
        Attachment {
            id: self.id.expect("Attachment ID is required"),
            grant_id: self.grant_id,
            filename: self.filename,
            content_type: self.content_type,
            size: self.size,
            content_id: self.content_id,
            is_inline: self.is_inline,
        }
    }
}

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

    #[test]
    fn test_attachment_creation() {
        let attachment = Attachment {
            id: "att_123".to_string(),
            grant_id: Some("grant_123".to_string()),
            filename: Some("document.pdf".to_string()),
            content_type: Some("application/pdf".to_string()),
            size: Some(12345),
            content_id: None,
            is_inline: Some(false),
        };

        assert_eq!(attachment.id, "att_123");
        assert_eq!(attachment.filename, Some("document.pdf".to_string()));
        assert_eq!(attachment.size, Some(12345));
    }

    #[test]
    fn test_attachment_builder() {
        let attachment = Attachment::builder()
            .id("att_123")
            .filename("test.pdf")
            .content_type("application/pdf")
            .size(1024)
            .is_inline(false)
            .build();

        assert_eq!(attachment.id, "att_123");
        assert_eq!(attachment.filename, Some("test.pdf".to_string()));
        assert_eq!(attachment.content_type, Some("application/pdf".to_string()));
        assert_eq!(attachment.size, Some(1024));
        assert_eq!(attachment.is_inline, Some(false));
    }

    #[test]
    fn test_attachment_inline() {
        let attachment = Attachment::builder()
            .id("att_inline")
            .filename("image.png")
            .content_type("image/png")
            .content_id("img001")
            .is_inline(true)
            .build();

        assert_eq!(attachment.content_id, Some("img001".to_string()));
        assert_eq!(attachment.is_inline, Some(true));
    }

    #[test]
    fn test_attachment_serialization() {
        let attachment = Attachment {
            id: "att_123".to_string(),
            grant_id: Some("grant_123".to_string()),
            filename: Some("test.pdf".to_string()),
            content_type: Some("application/pdf".to_string()),
            size: Some(5000),
            content_id: None,
            is_inline: Some(false),
        };

        let json = serde_json::to_string(&attachment).unwrap();
        assert!(json.contains("att_123"));
        assert!(json.contains("test.pdf"));
        assert!(json.contains("5000"));

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

    #[test]
    fn test_attachment_minimal() {
        let attachment = Attachment::builder().id("att_minimal").build();

        assert_eq!(attachment.id, "att_minimal");
        assert_eq!(attachment.filename, None);
        assert_eq!(attachment.size, None);
    }

    #[test]
    #[should_panic(expected = "Attachment ID is required")]
    fn test_attachment_builder_without_id() {
        Attachment::builder().filename("test.pdf").build();
    }
}