@@ -3,12 +3,14 @@ use crate::Env;
33use lettre:: address:: Envelope ;
44use lettre:: message:: header:: ContentType ;
55use lettre:: message:: Mailbox ;
6- use lettre:: transport:: file:: FileTransport ;
6+ use lettre:: transport:: file:: AsyncFileTransport ;
77use lettre:: transport:: smtp:: authentication:: { Credentials , Mechanism } ;
8- use lettre:: transport:: smtp:: SmtpTransport ;
9- use lettre:: transport:: stub:: StubTransport ;
10- use lettre:: { Address , Message , Transport } ;
8+ use lettre:: transport:: smtp:: AsyncSmtpTransport ;
9+ use lettre:: transport:: stub:: AsyncStubTransport ;
10+ use lettre:: { Address , AsyncTransport , Message , Tokio1Executor } ;
1111use rand:: distributions:: { Alphanumeric , DistString } ;
12+ use std:: sync:: Arc ;
13+ use tokio:: runtime:: Handle ;
1214
1315pub trait Email {
1416 fn subject ( & self ) -> String ;
@@ -36,7 +38,7 @@ impl Emails {
3638
3739 let backend = match ( login, password, server) {
3840 ( Ok ( login) , Ok ( password) , Ok ( server) ) => {
39- let transport = SmtpTransport :: relay ( & server)
41+ let transport = AsyncSmtpTransport :: < Tokio1Executor > :: relay ( & server)
4042 . unwrap ( )
4143 . credentials ( Credentials :: new ( login, password) )
4244 . authentication ( vec ! [ Mechanism :: Plain ] )
@@ -45,8 +47,8 @@ impl Emails {
4547 EmailBackend :: Smtp ( Box :: new ( transport) )
4648 }
4749 _ => {
48- let transport = FileTransport :: new ( "/tmp" ) ;
49- EmailBackend :: FileSystem ( transport)
50+ let transport = AsyncFileTransport :: new ( "/tmp" ) ;
51+ EmailBackend :: FileSystem ( Arc :: new ( transport) )
5052 }
5153 } ;
5254
@@ -67,23 +69,23 @@ impl Emails {
6769 /// to later assert the mails were sent.
6870 pub fn new_in_memory ( ) -> Self {
6971 Self {
70- backend : EmailBackend :: Memory ( StubTransport :: new_ok ( ) ) ,
72+ backend : EmailBackend :: Memory ( AsyncStubTransport :: new_ok ( ) ) ,
7173 domain : "crates.io" . into ( ) ,
7274 from : DEFAULT_FROM . parse ( ) . unwrap ( ) ,
7375 }
7476 }
7577
7678 /// This is supposed to be used only during tests, to retrieve the messages stored in the
7779 /// "memory" backend. It's not cfg'd away because our integration tests need to access this.
78- pub fn mails_in_memory ( & self ) -> Option < Vec < ( Envelope , String ) > > {
80+ pub async fn mails_in_memory ( & self ) -> Option < Vec < ( Envelope , String ) > > {
7981 if let EmailBackend :: Memory ( transport) = & self . backend {
80- Some ( transport. messages ( ) )
82+ Some ( transport. messages ( ) . await )
8183 } else {
8284 None
8385 }
8486 }
8587
86- pub fn send < E : Email > ( & self , recipient : & str , email : E ) -> Result < ( ) , EmailError > {
88+ fn build_message < E : Email > ( & self , recipient : & str , email : E ) -> Result < Message , EmailError > {
8789 // The message ID is normally generated by the SMTP server, but if we let it generate the
8890 // ID there will be no way for the crates.io application to know the ID of the message it
8991 // just sent, as it's not included in the SMTP response.
@@ -102,15 +104,32 @@ impl Emails {
102104 let subject = email. subject ( ) ;
103105 let body = email. body ( ) ;
104106
105- let email = Message :: builder ( )
107+ let message = Message :: builder ( )
106108 . message_id ( Some ( message_id. clone ( ) ) )
107109 . to ( recipient. parse ( ) ?)
108110 . from ( from)
109111 . subject ( subject)
110112 . header ( ContentType :: TEXT_PLAIN )
111113 . body ( body) ?;
112114
113- self . backend . send ( email) . map_err ( EmailError :: TransportError )
115+ Ok ( message)
116+ }
117+
118+ pub fn send < E : Email > ( & self , recipient : & str , email : E ) -> Result < ( ) , EmailError > {
119+ let email = self . build_message ( recipient, email) ?;
120+
121+ Handle :: current ( )
122+ . block_on ( self . backend . send ( email) )
123+ . map_err ( EmailError :: TransportError )
124+ }
125+
126+ pub async fn async_send < E : Email > ( & self , recipient : & str , email : E ) -> Result < ( ) , EmailError > {
127+ let email = self . build_message ( recipient, email) ?;
128+
129+ self . backend
130+ . send ( email)
131+ . await
132+ . map_err ( EmailError :: TransportError )
114133 }
115134}
116135
@@ -129,19 +148,19 @@ enum EmailBackend {
129148 /// Backend used in production to send mails using SMTP.
130149 ///
131150 /// This is using `Box` to avoid a large size difference between variants.
132- Smtp ( Box < SmtpTransport > ) ,
151+ Smtp ( Box < AsyncSmtpTransport < Tokio1Executor > > ) ,
133152 /// Backend used locally during development, will store the emails in the provided directory.
134- FileSystem ( FileTransport ) ,
153+ FileSystem ( Arc < AsyncFileTransport < Tokio1Executor > > ) ,
135154 /// Backend used during tests, will keep messages in memory to allow tests to retrieve them.
136- Memory ( StubTransport ) ,
155+ Memory ( AsyncStubTransport ) ,
137156}
138157
139158impl EmailBackend {
140- fn send ( & self , message : Message ) -> anyhow:: Result < ( ) > {
159+ async fn send ( & self , message : Message ) -> anyhow:: Result < ( ) > {
141160 match self {
142- EmailBackend :: Smtp ( transport) => transport. send ( & message) . map ( |_| ( ) ) ?,
143- EmailBackend :: FileSystem ( transport) => transport. send ( & message) . map ( |_| ( ) ) ?,
144- EmailBackend :: Memory ( transport) => transport. send ( & message) . map ( |_| ( ) ) ?,
161+ EmailBackend :: Smtp ( transport) => transport. send ( message) . await . map ( |_| ( ) ) ?,
162+ EmailBackend :: FileSystem ( transport) => transport. send ( message) . await . map ( |_| ( ) ) ?,
163+ EmailBackend :: Memory ( transport) => transport. send ( message) . await . map ( |_| ( ) ) ?,
145164 }
146165
147166 Ok ( ( ) )
@@ -171,20 +190,19 @@ mod tests {
171190 }
172191 }
173192
174- #[ test]
175- fn sending_to_invalid_email_fails ( ) {
193+ #[ tokio :: test]
194+ async fn sending_to_invalid_email_fails ( ) {
176195 let emails = Emails :: new_in_memory ( ) ;
177196
178- assert_err ! ( emails. send(
179- "String.Format(\" {0}.{1}@live.com\" , FirstName, LastName)" ,
180- TestEmail
181- ) ) ;
197+ let address = "String.Format(\" {0}.{1}@live.com\" , FirstName, LastName)" ;
198+ assert_err ! ( emails. async_send( address, TestEmail ) . await ) ;
182199 }
183200
184- #[ test]
185- fn sending_to_valid_email_succeeds ( ) {
201+ #[ tokio :: test]
202+ async fn sending_to_valid_email_succeeds ( ) {
186203 let emails = Emails :: new_in_memory ( ) ;
187204
188- assert_ok ! ( emails
. send
( "[email protected] " , TestEmail ) ) ; 205+ let address =
"[email protected] " ; 206+ assert_ok ! ( emails. async_send( address, TestEmail ) . await ) ;
189207 }
190208}
0 commit comments