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
use std::convert::TryInto;
use crate::{Client, POSTMARK_API_URL};
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, Response};
use thiserror::Error;
use typed_builder::TypedBuilder;
#[derive(TypedBuilder)]
pub struct PostmarkClient {
#[builder(default, setter(into, strip_option))]
pub token: Option<String>,
#[builder(default=POSTMARK_API_URL.into(), setter(into))]
pub base_url: String,
}
impl std::fmt::Debug for PostmarkClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
token: ref _token,
base_url: ref _base_url,
} = *self;
let mut builder = f.debug_struct("PostmarkClient");
builder.field("token", &_token.as_ref().map(|_| "***"));
builder.field("base_url", &(*_base_url));
builder.finish()
}
}
impl Default for PostmarkClient {
fn default() -> Self {
Self {
base_url: POSTMARK_API_URL.into(),
token: None,
}
}
}
#[derive(Error, Debug)]
pub enum PostmarkClientError {
#[error("error setting auth header: {}", source)]
AuthError {
#[from]
source: http::header::InvalidHeaderValue,
},
#[error("communication with postmark: {}", source)]
Communication {
#[from]
source: reqwest::Error,
},
#[error("`http` error: {}", source)]
Http {
#[from]
source: http::Error,
},
}
#[async_trait]
impl Client for PostmarkClient {
type Error = PostmarkClientError;
async fn execute(&self, req: Request<Bytes>) -> Result<Response<Bytes>, Self::Error> {
let client = reqwest::Client::builder().build()?;
let mut req = req;
if let Some(tok) = &self.token {
req.headers_mut()
.append("X-Postmark-Server-Token", tok.try_into()?);
}
let reqwest_req = req.try_into()?;
let reqwest_rsp = client.execute(reqwest_req).await?;
let mut rsp = Response::builder()
.status(reqwest_rsp.status())
.version(reqwest_rsp.version());
let headers = rsp.headers_mut().unwrap();
for (k, v) in reqwest_rsp.headers() {
headers.insert(k, v.clone());
}
Ok(rsp.body(reqwest_rsp.bytes().await?)?)
}
}