1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
use reqwest;
use std::collections::HashMap;
use serde::{Deserialize};
use reqwest::Error as ReqError;

const MAILGUN_API: &str = "https://api.mailgun.net/v3";
const MESSAGES_ENDPOINT: &str = "messages";

#[derive(Default)]
pub struct Mailgun {
  pub api_key: String,
  pub domain: String,
  pub message: Message,
}

pub type SendResult<T> = Result<T, ReqError>;

#[derive(Deserialize, Debug, PartialEq)]
pub struct SendResponse {
  pub message: String,
  pub id: String,
}

impl Mailgun{
  pub fn send(self, sender: &EmailAddress) ->  SendResult<SendResponse> {
    let client = reqwest::Client::new();
    let mut params = self.message.to_params();
    params.insert("from".to_string(), sender.to_string());
    let url = format!("{}/{}/{}", MAILGUN_API, self.domain, MESSAGES_ENDPOINT);

    let mut res = client.post(&url)
      .basic_auth("api", Some(self.api_key.clone()))
      .form(&params)
      .send()?
      .error_for_status()?;

    let parsed: SendResponse = res.json()?;
    Ok(parsed)
  }
}

#[derive(Default)]
pub struct Message {
  pub to: Vec<EmailAddress>,
  pub cc: Vec<EmailAddress>,
  pub bcc: Vec<EmailAddress>,
  pub subject: String,
  pub text: String,
  pub html: String,
}

impl Message {
  fn to_params(self) -> HashMap<String, String> {
    let mut params = HashMap::new();

    Message::add_recipients("to", self.to, &mut params);
    Message::add_recipients("cc", self.cc, &mut params);
    Message::add_recipients("bcc", self.bcc, &mut params);

    params.insert(String::from("subject"), self.subject);

    params.insert(String::from("text"), self.text);
    params.insert(String::from("html"), self.html);

    params
  }

  fn add_recipients(field: &str, addresses: Vec<EmailAddress>, params: &mut HashMap<String, String>) {
    if !addresses.is_empty() {
      let joined = addresses.iter()
        .map(EmailAddress::to_string)
        .collect::<Vec<String>>()
        .join(",");
      params.insert(field.to_owned(), joined);
    }
  }
}

pub struct EmailAddress {
  name: Option<String>,
  address: String,
}

impl EmailAddress {
  pub fn address(address: &str) -> Self {
    EmailAddress { name: None, address: address.to_string() }
  }

  pub fn name_address(name: &str, address: &str ) -> Self {
    EmailAddress { name: Some(name.to_string()), address: address.to_string() }
  }

  pub fn email(&self) -> &str {
    &self.address
  }

  pub fn to_string(&self) -> String {
    match self.name {
      Some(ref name) => format!("{} <{}>", name, self.address),
      None => self.address.clone()
    }
  }
}