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
104
105
106
107
108
use hyper::client::{Client, Response};
use hyper::header::Headers;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;
use serde::Serialize;
use serde_json;
use std::env;
use std::io::Read;
use regex::Regex;

/// X-Custom header macros for hyper
header! { (XSmtp2goApi, "X-Smtp2go-Api") => [String] }
header! { (XSmtp2goApiVersion, "X-Smtp2go-Api-Version") => [String] }
header! { (XSmtp2goApiKey, "X-Smtp2go-Api-Key") => [String] }

pub trait Smtp2goApiRequest: Serialize {}

#[derive(Debug, Deserialize)]
pub struct Smtp2goApiResponse {
    request_id: String,
    data: serde_json::Value,
}

#[derive(Debug)]
pub enum Smtp2goApiError {
    MissingAPIKey(String),
    IncorrectAPIKeyFormat(String),
    MissingRequiredField(String),
    RequestError(String),
    EndpointError(String),
    InvalidJSON(String),
}

pub type Smtp2goApiResult = Result<Smtp2goApiResponse, Smtp2goApiError>;

impl Smtp2goApiResponse {
    pub fn new<T: Into<String>>(request_id: T, data: serde_json::Value) -> Smtp2goApiResponse {
        Smtp2goApiResponse {
            request_id: request_id.into(),
            data: data,
        }
    }
}

fn parse_response(mut response: Response) -> Smtp2goApiResult {

    // read all the response data into a string
    let mut text: String = String::new();
    response.read_to_string(&mut text).unwrap();

    // deserialize the response text into the response struct
    let response: Smtp2goApiResponse = match serde_json::from_str(&text) {
        Ok(val) => val,
        Err(_) => {
            return Err(Smtp2goApiError::InvalidJSON("Unable to parse response JSON".to_string()))
        }
    };

    Ok(response)
}

pub fn api_request<T: Into<String>, U: Smtp2goApiRequest>(endpoint: T, request: &mut U) -> Smtp2goApiResult {

    // grab the api_root and api_key environment variables
    let api_root: String = match env::var("SMTP2GO_API_ROOT") {
        Ok(api_root) => api_root,
        Err(_) => "https://api.smtp2go.com/v3".to_string(),
    };

    // grab the api_root and api_key environment variables
    let api_key: String = match env::var("SMTP2GO_API_KEY") {
        Ok(api_key) => api_key,
        Err(_) => return Err(Smtp2goApiError::MissingAPIKey("Unable to find an api_key, please set the SMTP2GO_API_KEY environment variable".to_string())),
    };

    // check if the key is correctly formatted prior to the http request
    let re = Regex::new(r"^api-[a-zA-Z0-9]{32}$").unwrap();
    if !re.is_match(&api_key){
        return Err(Smtp2goApiError::IncorrectAPIKeyFormat(format!("The value of SMTP2GO_API_KEY '{}' does not match the api key format of ^api-[a-zA-Z0-9]{{32}}$, please correct it", api_key)))
    }

    // create the header payload to
    let mut headers = Headers::new();
    headers.set(XSmtp2goApi(String::from("smtp2go-rust")).to_owned());
    headers.set(XSmtp2goApiVersion(String::from(::VERSION)).to_owned());
    headers.set(XSmtp2goApiKey(String::from(api_key)).to_owned());

    let ssl = NativeTlsClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);
    let client = Client::with_connector(connector);

    // serialise the request into a json string
    let rjson: String =
        match serde_json::to_string(&request) {
            Ok(val) => val,
            Err(_) => return Err(Smtp2goApiError::InvalidJSON("Unable to serialise request into valid JSON".to_string())),
        };

    // create the hyper client
    let url: String = format!("{}/{}", api_root, endpoint.into());
    match client.post(&url)
        .body(&rjson)
        .headers(headers)
        .send() {
	        Ok(res) => parse_response(res),
	        Err(err) => { Err(Smtp2goApiError::RequestError(format!("Somthing went wrong with the request: {}", err))) }
    }
}