use crate::attachment::Attachment;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Recipient {
pub address: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
impl Recipient {
pub fn new(address: impl Into<String>) -> Self {
Self {
address: address.into(),
name: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Sender {
pub address: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to: Option<String>,
}
impl Sender {
pub fn new(address: impl Into<String>) -> Self {
Self {
address: address.into(),
name: None,
reply_to: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_reply_to(mut self, reply_to: impl Into<String>) -> Self {
self.reply_to = Some(reply_to.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EmailOptions {
pub email_id: String,
pub recipient: Recipient,
#[serde(rename = "email_data", skip_serializing_if = "Option::is_none")]
pub data: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sender: Option<Sender>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<Vec<Recipient>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcc: Option<Vec<Recipient>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<Attachment>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub esp_account: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
}
impl EmailOptions {
pub fn new(email_id: impl Into<String>, recipient: Recipient) -> Self {
Self {
email_id: email_id.into(),
recipient,
data: None,
sender: None,
cc: None,
bcc: None,
files: None,
esp_account: None,
version_name: None,
headers: None,
tags: None,
locale: None,
}
}
pub fn with_data(mut self, data: HashMap<String, serde_json::Value>) -> Self {
self.data = Some(data);
self
}
pub fn with_sender(mut self, sender: Sender) -> Self {
self.sender = Some(sender);
self
}
pub fn with_cc(mut self, cc: Vec<Recipient>) -> Self {
self.cc = Some(cc);
self
}
pub fn with_bcc(mut self, bcc: Vec<Recipient>) -> Self {
self.bcc = Some(bcc);
self
}
pub fn with_files(mut self, files: Vec<Attachment>) -> Self {
self.files = Some(files);
self
}
pub fn with_esp_account(mut self, esp_account: impl Into<String>) -> Self {
self.esp_account = Some(esp_account.into());
self
}
pub fn with_version_name(mut self, version_name: impl Into<String>) -> Self {
self.version_name = Some(version_name.into());
self
}
pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
self.headers = Some(headers);
self
}
pub fn with_tags(mut self, tags: Vec<String>) -> Self {
self.tags = Some(tags);
self
}
pub fn with_locale(mut self, locale: impl Into<String>) -> Self {
self.locale = Some(locale.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TemplateOptions {
pub name: String,
pub subject: String,
pub html: String,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub preheader: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub amp_html: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DripCampaignOptions {
pub recipient_address: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email_data: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CustomerOptions {
pub email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RenderOptions {
pub template: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub version_id: Option<String>,
pub template_data: HashMap<String, serde_json::Value>,
pub strict: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_recipient() {
let recipient = Recipient::new("test@example.com");
assert_eq!(recipient.address, "test@example.com");
assert_eq!(recipient.name, None);
let recipient = Recipient::new("test@example.com").with_name("Test User");
assert_eq!(recipient.address, "test@example.com");
assert_eq!(recipient.name, Some("Test User".to_string()));
}
#[test]
fn test_sender() {
let sender = Sender::new("sender@example.com");
assert_eq!(sender.address, "sender@example.com");
assert_eq!(sender.name, None);
assert_eq!(sender.reply_to, None);
let sender = Sender::new("sender@example.com").with_name("Sender Name");
assert_eq!(sender.address, "sender@example.com");
assert_eq!(sender.name, Some("Sender Name".to_string()));
assert_eq!(sender.reply_to, None);
let sender = Sender::new("sender@example.com").with_reply_to("reply@example.com");
assert_eq!(sender.address, "sender@example.com");
assert_eq!(sender.name, None);
assert_eq!(sender.reply_to, Some("reply@example.com".to_string()));
let sender = Sender::new("sender@example.com")
.with_name("Sender Name")
.with_reply_to("reply@example.com");
assert_eq!(sender.address, "sender@example.com");
assert_eq!(sender.name, Some("Sender Name".to_string()));
assert_eq!(sender.reply_to, Some("reply@example.com".to_string()));
}
#[test]
fn test_email_options() {
let recipient = Recipient::new("recipient@example.com");
let options = EmailOptions::new("template-123", recipient.clone());
assert_eq!(options.email_id, "template-123");
assert_eq!(options.recipient.address, "recipient@example.com");
assert_eq!(options.data, None);
assert_eq!(options.sender, None);
assert_eq!(options.cc, None);
assert_eq!(options.bcc, None);
assert_eq!(options.files, None);
assert_eq!(options.esp_account, None);
assert_eq!(options.version_name, None);
assert_eq!(options.headers, None);
assert_eq!(options.tags, None);
assert_eq!(options.locale, None);
let mut email_data = HashMap::new();
email_data.insert("name".to_string(), json!("John Doe"));
email_data.insert("order_id".to_string(), json!(12345));
let options = EmailOptions::new("template-123", recipient.clone()).with_data(email_data);
assert_eq!(options.data.as_ref().unwrap()["name"], "John Doe");
assert_eq!(options.data.as_ref().unwrap()["order_id"], 12345);
let sender = Sender::new("sender@example.com").with_name("Sender Name");
let options = EmailOptions::new("template-123", recipient.clone()).with_sender(sender);
assert_eq!(
options.sender.as_ref().unwrap().address,
"sender@example.com"
);
assert_eq!(
options.sender.as_ref().unwrap().name,
Some("Sender Name".to_string())
);
let cc1 = Recipient::new("cc1@example.com");
let cc2 = Recipient::new("cc2@example.com").with_name("CC Recipient");
let bcc = Recipient::new("bcc@example.com");
let options = EmailOptions::new("template-123", recipient.clone())
.with_cc(vec![cc1, cc2])
.with_bcc(vec![bcc]);
assert_eq!(options.cc.as_ref().unwrap().len(), 2);
assert_eq!(options.cc.as_ref().unwrap()[0].address, "cc1@example.com");
assert_eq!(options.cc.as_ref().unwrap()[1].address, "cc2@example.com");
assert_eq!(
options.cc.as_ref().unwrap()[1].name,
Some("CC Recipient".to_string())
);
assert_eq!(options.bcc.as_ref().unwrap().len(), 1);
assert_eq!(options.bcc.as_ref().unwrap()[0].address, "bcc@example.com");
let custom_headers = HashMap::from([("Header-X".into(), "Some value".into())]);
let options = EmailOptions::new("template-123", recipient)
.with_esp_account("esp-123")
.with_files(vec![Attachment::from_bytes(
b"File contents",
"cooldoc.pdf",
)])
.with_headers(custom_headers)
.with_version_name("version-name")
.with_locale("en-US")
.with_tags(vec!["tag1".to_string(), "tag2".to_string()]);
assert_eq!(options.esp_account, Some("esp-123".to_string()));
assert_eq!(options.version_name, Some("version-name".to_string()));
assert_eq!(options.locale, Some("en-US".to_string()));
assert_eq!(options.tags.as_ref().unwrap()[0], "tag1");
assert_eq!(options.tags.as_ref().unwrap()[1], "tag2");
}
#[test]
fn test_drip_campaign_options() {
let options = DripCampaignOptions {
recipient_address: "recipient@example.com".to_string(),
email_data: None,
tags: None,
locale: None,
};
assert_eq!(options.recipient_address, "recipient@example.com");
assert_eq!(options.email_data, None);
assert_eq!(options.tags, None);
assert_eq!(options.locale, None);
let mut email_data = HashMap::new();
email_data.insert("foo".to_string(), json!("bar"));
let options = DripCampaignOptions {
recipient_address: "recipient@example.com".to_string(),
email_data: Some(email_data),
tags: Some(vec!["tag1".to_string(), "tag2".to_string()]),
locale: Some("fr-CA".to_string()),
};
assert_eq!(options.recipient_address, "recipient@example.com");
assert_eq!(options.email_data.as_ref().unwrap()["foo"], "bar");
assert_eq!(options.tags.as_ref().unwrap()[0], "tag1");
assert_eq!(options.tags.as_ref().unwrap()[1], "tag2");
assert_eq!(options.locale, Some("fr-CA".to_string()));
}
#[test]
fn test_template_options() {
let options = TemplateOptions {
name: "Template Name".to_string(),
subject: "Email Subject".to_string(),
html: "<html>Content</html>".to_string(),
text: "Plain text content".to_string(),
preheader: Some("Preheader text".to_string()),
amp_html: None,
};
assert_eq!(options.name, "Template Name");
assert_eq!(options.subject, "Email Subject");
assert_eq!(options.html, "<html>Content</html>");
assert_eq!(options.text, "Plain text content");
assert_eq!(options.preheader, Some("Preheader text".to_string()));
assert_eq!(options.amp_html, None);
}
#[test]
fn test_render_options() {
let mut template_data = HashMap::new();
template_data.insert("name".to_string(), json!("John"));
template_data.insert("items".to_string(), json!(["item1", "item2"]));
let options = RenderOptions {
template: "template-id".to_string(),
version_id: Some("version-id".to_string()),
template_data,
strict: true,
locale: Some("en-US".to_string()),
};
assert_eq!(options.template, "template-id");
assert_eq!(options.version_id, Some("version-id".to_string()));
assert_eq!(options.template_data["name"], "John");
assert_eq!(options.template_data["items"], json!(["item1", "item2"]));
assert!(options.strict);
assert_eq!(options.locale, Some("en-US".to_string()));
}
#[test]
fn test_customer_options() {
let options = CustomerOptions {
email: "customer@example.com".to_string(),
data: None,
locale: None,
};
assert_eq!(options.email, "customer@example.com");
assert_eq!(options.data, None);
assert_eq!(options.locale, None);
let mut data = HashMap::new();
data.insert("first_name".to_string(), json!("John"));
data.insert("last_name".to_string(), json!("Doe"));
data.insert("age".to_string(), json!(30));
let options = CustomerOptions {
email: "customer@example.com".to_string(),
data: Some(data),
locale: Some("en-US".to_string()),
};
assert_eq!(options.email, "customer@example.com");
assert_eq!(options.data.as_ref().unwrap()["first_name"], "John");
assert_eq!(options.data.as_ref().unwrap()["last_name"], "Doe");
assert_eq!(options.data.as_ref().unwrap()["age"], 30);
assert_eq!(options.locale, Some("en-US".to_string()));
}
}