plunk-rs 0.1.0

Async Rust client for the Plunk transactional email API
Documentation
use crate::email::{Email, EmailAddress, EmailContent};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::collections::BTreeMap;

#[derive(Clone, Debug, Serialize)]
pub(crate) struct WireEmail {
    to: WireRecipients,
    #[serde(skip_serializing_if = "Option::is_none")]
    subject: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    body: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    template: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    from: Option<WireEmailAddress>,
    #[serde(skip_serializing_if = "Map::is_empty", default)]
    data: Map<String, Value>,
    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
    headers: BTreeMap<String, String>,
    #[serde(rename = "reply", skip_serializing_if = "Option::is_none")]
    reply: Option<String>,
}

impl From<&Email> for WireEmail {
    fn from(value: &Email) -> Self {
        let (subject, body, template) = match &value.content {
            EmailContent::Html { subject, body } => {
                (Some(subject.clone()), Some(body.clone()), None)
            }
            EmailContent::Template { template_id } => (None, None, Some(template_id.clone())),
        };

        Self {
            to: WireRecipients::from(value.recipients()),
            subject,
            body,
            template,
            from: value.from.as_ref().map(WireEmailAddress::from),
            data: value.data.clone(),
            headers: value.headers.clone(),
            reply: value.reply_to.as_ref().map(|a| a.email.clone()),
        }
    }
}

#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
enum WireRecipients {
    One(WireEmailAddress),
    Many(Vec<WireEmailAddress>),
}

impl From<&[EmailAddress]> for WireRecipients {
    fn from(value: &[EmailAddress]) -> Self {
        match value {
            [only] => Self::One(WireEmailAddress::from(only)),
            many => Self::Many(many.iter().map(WireEmailAddress::from).collect()),
        }
    }
}

#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
enum WireEmailAddress {
    Plain(String),
    Named { name: String, email: String },
}

impl From<&EmailAddress> for WireEmailAddress {
    fn from(value: &EmailAddress) -> Self {
        match &value.name {
            Some(name) => Self::Named {
                name: name.clone(),
                email: value.email.clone(),
            },
            None => Self::Plain(value.email.clone()),
        }
    }
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct WireSendResponse {
    pub(crate) success: bool,
    pub(crate) data: WireSendResponseData,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct WireSendResponseData {
    pub(crate) emails: Vec<WireDeliveredEmail>,
    pub(crate) timestamp: String,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct WireDeliveredEmail {
    pub(crate) contact: WireContact,
    pub(crate) email: String,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct WireContact {
    pub(crate) id: String,
    pub(crate) email: String,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct WireApiError {
    pub(crate) code: i64,
    pub(crate) error: String,
    pub(crate) message: String,
}