unosend 1.0.0

Official Rust SDK for Unosend - Email API for developers
Documentation
use crate::client::Unosend;
use crate::error::UnosendError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Email sending API.
pub struct EmailsApi {
    client: Unosend,
}

impl EmailsApi {
    pub(crate) fn new(client: Unosend) -> Self {
        Self { client }
    }

    /// Send an email.
    pub async fn send(&self, request: SendEmailRequest) -> Result<SendEmailResponse, UnosendError> {
        self.client.post("/emails", &request).await
    }

    /// Get an email by ID.
    pub async fn get(&self, email_id: &str) -> Result<EmailDetails, UnosendError> {
        self.client.get(&format!("/emails/{}", email_id)).await
    }
}

/// Request to send an email.
#[derive(Debug, Clone, Serialize, Default)]
pub struct SendEmailRequest {
    /// Sender email address.
    pub from: String,

    /// Recipient email address(es).
    #[serde(flatten)]
    pub to: EmailRecipient,

    /// Email subject line.
    pub subject: String,

    /// HTML content.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub html: Option<String>,

    /// Plain text content.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,

    /// CC recipients.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cc: Option<Vec<String>>,

    /// BCC recipients.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bcc: Option<Vec<String>>,

    /// Reply-to address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reply_to: Option<String>,

    /// Email priority: "high", "normal", or "low".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub priority: Option<String>,

    /// Template ID for using pre-built templates.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub template_id: Option<String>,

    /// Template variables for dynamic content.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub template_data: Option<HashMap<String, serde_json::Value>>,

    /// Schedule email for future delivery (ISO 8601 format).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheduled_for: Option<String>,

    /// File attachments.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attachments: Option<Vec<Attachment>>,

    /// Custom email headers.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<HashMap<String, String>>,

    /// Custom tags for tracking.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<HashMap<String, String>>,
}

/// Email recipient (single or multiple).
#[derive(Debug, Clone, Serialize, Default)]
#[serde(untagged)]
pub enum EmailRecipient {
    #[default]
    None,
    Single {
        to: String,
    },
    Multiple {
        to: Vec<String>,
    },
}

impl From<&str> for EmailRecipient {
    fn from(s: &str) -> Self {
        EmailRecipient::Single { to: s.to_string() }
    }
}

impl From<String> for EmailRecipient {
    fn from(s: String) -> Self {
        EmailRecipient::Single { to: s }
    }
}

impl From<Vec<String>> for EmailRecipient {
    fn from(v: Vec<String>) -> Self {
        EmailRecipient::Multiple { to: v }
    }
}

/// Email attachment.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
    /// Filename.
    pub filename: String,

    /// Base64-encoded content.
    pub content: String,

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

/// Response from sending an email.
#[derive(Debug, Clone, Deserialize)]
pub struct SendEmailResponse {
    pub id: String,
    pub from: String,
    pub to: Vec<String>,
    pub subject: String,
    pub status: String,
    pub created_at: String,
}

/// Detailed email information.
#[derive(Debug, Clone, Deserialize)]
pub struct EmailDetails {
    pub id: String,
    pub from: String,
    pub to: Vec<String>,
    pub subject: String,
    pub status: String,
    pub created_at: String,
    pub html: Option<String>,
    pub text: Option<String>,
    pub sent_at: Option<String>,
    pub delivered_at: Option<String>,
}