Skip to main content

postmark/
client.rs

1use reqwest::{
2    Method,
3    header::{self, HeaderMap, HeaderValue},
4};
5use serde::{Deserialize, Serialize, de::DeserializeOwned};
6
7pub struct Client {
8    pub http_client: reqwest::Client,
9    pub api_base_url: &'static str,
10    pub account_api_token: Option<String>,
11}
12
13#[derive(Clone, Debug, Deserialize, Serialize)]
14#[serde(rename_all = "PascalCase")]
15pub struct ApiError {
16    pub error_code: i64,
17    pub message: String,
18}
19
20pub struct SendRequestInput<B: Serialize> {
21    pub method: Method,
22    pub url: String,
23    pub body: B,
24    pub server_token: Option<String>,
25}
26
27impl Client {
28    pub fn new(account_api_token: Option<String>) -> Client {
29        let http_client = reqwest::Client::new();
30
31        return Client {
32            http_client,
33            api_base_url: "https://api.postmarkapp.com",
34            account_api_token,
35        };
36    }
37
38    pub(crate) async fn send_request<B: Serialize, R: DeserializeOwned>(
39        &self,
40        input: SendRequestInput<B>,
41    ) -> Result<R, ApiError> {
42        // it's okay to unwrap here because we are sure that the headers won't contain any invalid
43        // characters
44        let mut headers: HeaderMap<HeaderValue> = HeaderMap::new();
45        headers.insert(header::ACCEPT, HeaderValue::from_str("application/json").unwrap());
46        headers.insert(header::CONTENT_TYPE, HeaderValue::from_str("application/json").unwrap());
47
48        if let Some(server_api_token) = input.server_token {
49            headers.insert("X-Postmark-Server-Token", HeaderValue::from_str(&server_api_token).unwrap());
50        } else if let Some(account_api_token) = &self.account_api_token {
51            headers.insert(
52                "X-Postmark-Account-Token",
53                HeaderValue::from_str(account_api_token.as_str()).unwrap(),
54            );
55        }
56
57        let url = format!("{}{}", &self.api_base_url, input.url);
58        let res = self
59            .http_client
60            .request(input.method, url)
61            .headers(headers)
62            .json(&input.body)
63            .send()
64            .await
65            .map_err(|err| ApiError {
66                error_code: 0,
67                message: format!("postmark: error sending request: {err}"),
68            })?;
69
70        if res.status().as_u16() > 399 {
71            let err: ApiError = res.json().await.map_err(|err| ApiError {
72                error_code: 0,
73                message: format!("postmark: error parsing error response: {err}"),
74            })?;
75            return Err(err);
76        }
77
78        let res: R = res.json().await.map_err(|err| ApiError {
79            error_code: 0,
80            message: format!("postmark: error parsing response: {err}"),
81        })?;
82
83        return Ok(res);
84    }
85}