postmark-client 0.2.0

Postmark API client using reqwest
Documentation
use serde::{Deserialize, Serialize, Serializer};
use std::collections::HashMap;

/// as_base64 allows for binary data to be encoded as a base64 string
fn as_base64<T, S>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
where
    T: AsRef<[u8]>,
    S: Serializer,
{
    serializer.serialize_str(&base64::encode(data.as_ref()))
}

#[derive(Serialize, Debug)]
pub enum TrackLinks {
    None,
    HtmlTAndText,
    HtmlOnly,
    TextOnly,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct EmailHeader {
    pub name: String,
    pub value: String,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Attachment {
    pub name: String,
    #[serde(serialize_with = "as_base64")]
    pub content: Vec<u8>,
    pub content_type: String,
}

#[derive(Serialize, Debug)]
#[serde(untagged)]
pub enum EmailBody {
    #[serde(rename = "TextBody")]
    Text(String),
    #[serde(rename = "HtmlBody")]
    Html(String),
    HtmlAndText {
        #[serde(rename = "TextBody")]
        text: String,
        #[serde(rename = "HtmlBody")]
        html: String,
    },
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Email {
    pub from: String,
    pub to: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bcc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tag: Option<String>,
    #[serde(flatten)]
    pub body: EmailBody,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reply_to: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<Vec<EmailHeader>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub track_opens: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub track_links: Option<TrackLinks>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<HashMap<String, String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attachments: Option<Vec<Attachment>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message_stream: Option<String>,
}

impl Email {
    pub fn new(from: String, to: String, body: EmailBody) -> Email {
        Email {
            from,
            to,
            cc: None,
            bcc: None,
            subject: None,
            tag: None,
            body,
            reply_to: None,
            headers: None,
            track_opens: None,
            track_links: None,
            metadata: None,
            attachments: None,
            message_stream: None,
        }
    }
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct EmailResponse {
    pub to: String,
    pub submitted_at: String,
    pub message_id: String,
    pub error_code: i32,
    pub message: String,
}

#[derive(Serialize, Debug)]
pub enum TemplateToUse {
    #[serde(rename = "TemplateId")]
    Id(String),
    #[serde(rename = "TemplateAlias")]
    Alias(String),
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TemplatedEmail<M>
where
    M: Serialize,
{
    #[serde(flatten)]
    pub template: TemplateToUse,
    pub template_model: M,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub inline_css: Option<bool>,
    pub from: String,
    pub to: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bcc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tag: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reply_to: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<Vec<EmailHeader>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub track_opens: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub track_links: Option<TrackLinks>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<HashMap<String, String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attachments: Option<Vec<Attachment>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message_stream: Option<String>,
}

impl<M> TemplatedEmail<M>
where
    M: Serialize,
{
    pub fn new(from: String, to: String, template: TemplateToUse, model: M) -> TemplatedEmail<M> {
        TemplatedEmail {
            template,
            template_model: model,
            inline_css: None,
            from,
            to,
            cc: None,
            bcc: None,
            tag: None,
            reply_to: None,
            headers: None,
            track_opens: None,
            track_links: None,
            metadata: None,
            attachments: None,
            message_stream: None,
        }
    }
}

/// Represents an API error from Postmark.
///
/// <https://postmarkapp.com/developer/api/overview#error-codes>
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct APIError {
    pub error_code: String,
    pub message: String,
}