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
|
use std::{path::PathBuf, str::FromStr};
use email_address::EmailAddress;
use super::{
EmailMessenger, EmailMessengerError, EmailType, EMAIL_TEMPLATE_DEFAULT_LOGIN,
EMAIL_TEMPLATE_DEFAULT_SIGNUP,
};
pub(crate) struct LocalEmailStubber {
pub(crate) email_template_login: Option<String>,
pub(crate) email_template_signup: Option<String>,
}
#[async_trait::async_trait]
impl EmailMessenger for LocalEmailStubber {
// TODO: this module really shouldn't be called client, it should be called services... the client is sqlx/mailgun/sns wrapper or whatever...
async fn send_email(
&self,
email_address: &str,
validation_id: &str,
secret_code: &str,
t: EmailType,
) -> Result<(), EmailMessengerError> {
let login_template = self
.email_template_login
.clone()
.unwrap_or(EMAIL_TEMPLATE_DEFAULT_LOGIN.to_string());
let signup_template = self
.email_template_signup
.clone()
.unwrap_or(EMAIL_TEMPLATE_DEFAULT_SIGNUP.to_string());
let replace_template = |s: &str| {
s.replace(
"%secd_link%",
&format!("{}?code={}", validation_id, secret_code),
)
.replace("%secd_email_address%", email_address)
.replace("%secd_code%", secret_code)
};
if !EmailAddress::is_valid(email_address) {
return Err(EmailMessengerError::InvalidEmailAddress);
}
let body = match t {
EmailType::Login => replace_template(&login_template),
EmailType::Signup => replace_template(&signup_template),
};
// TODO: write to the system mailbox instead?
std::fs::write(
PathBuf::from_str(&format!("/tmp/{}.localmail", validation_id))
.map_err(|_| EmailMessengerError::Unknown)?,
body,
)
.map_err(|_| EmailMessengerError::FailedToSendEmail)?;
Ok(())
}
}
|