diff --git a/CHANGELOG.md b/CHANGELOG.md index 3877fa4..a8326ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ Several breaking changes were made between 0.9 and 0.10, but changes should be s * Add `tokio` 0.2 and 1.0 support * Add `rustls` support -* Add `async-std` support +* Add `async-std` support. NOTE: native-tls isn't supported when using async-std for the smtp transport. * Allow enabling multiple SMTP authentication mechanisms * Allow providing a custom message id * Allow sending raw emails diff --git a/Cargo.toml b/Cargo.toml index d14cfb9..78e8aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,12 @@ webpki-roots = { version = "0.21", optional = true } # async futures-io = { version = "0.3.7", optional = true } futures-util = { version = "0.3.7", default-features = false, features = ["io"], optional = true } +async-trait = { version = "0.1", optional = true } ## async-std -async-attributes = { version = "1.1", optional = true } async-std = { version = "1.8", optional = true, features = ["unstable"] } -async-trait = { version = "0.1", optional = true } +#async-native-tls = { version = "0.3.3", optional = true } +async-rustls = { version = "0.2", optional = true } ## tokio tokio02_crate = { package = "tokio", version = "0.2.7", features = ["fs", "process", "tcp", "dns", "io-util"], optional = true } @@ -70,6 +71,7 @@ glob = "0.3" walkdir = "2" tokio02_crate = { package = "tokio", version = "0.2.7", features = ["macros", "rt-threaded"] } tokio1_crate = { package = "tokio", version = "1", features = ["macros", "rt-multi-thread"] } +async-std = { version = "1.8", features = ["attributes"] } serde_json = "1" maud = "0.22.1" @@ -90,7 +92,9 @@ smtp-transport = ["base64", "nom"] rustls-tls = ["webpki", "webpki-roots", "rustls"] # async -async-std1 = ["async-std", "async-trait", "async-attributes"] +async-std1 = ["async-std", "async-trait", "futures-io", "futures-util"] +#async-std1-native-tls = ["async-std1", "native-tls", "async-native-tls"] +async-std1-rustls-tls = ["async-std1", "rustls-tls", "async-rustls"] tokio02 = ["tokio02_crate", "async-trait", "futures-io", "futures-util"] tokio02-native-tls = ["tokio02", "native-tls", "tokio02_native_tls_crate"] tokio02-rustls-tls = ["tokio02", "rustls-tls", "tokio02_rustls"] @@ -142,3 +146,10 @@ required-features = ["smtp-transport", "tokio1", "tokio1-native-tls", "builder"] name = "tokio1_smtp_starttls" required-features = ["smtp-transport", "tokio1", "tokio1-native-tls", "builder"] +[[example]] +name = "asyncstd1_smtp_tls" +required-features = ["smtp-transport", "async-std1", "async-std1-rustls-tls", "builder"] + +[[example]] +name = "asyncstd1_smtp_starttls" +required-features = ["smtp-transport", "async-std1", "async-std1-rustls-tls", "builder"] diff --git a/README.md b/README.md index a474592..0459971 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Lettre provides the following features: * Unicode support (for email content and addresses) * Secure delivery with SMTP using encryption and authentication * Easy email builders -* Async support (incomplete) +* Async support Lettre does not provide (for now): diff --git a/examples/README.md b/examples/README.md index c4c5e97..eac4e88 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,7 +14,7 @@ This folder contains examples showing how to use lettre in your own projects. - [smtp_starttls.rs] - Send an email over SMTP with STARTTLS and authenticating with username and password. - [smtp_selfsigned.rs] - Send an email over SMTP encrypted with TLS using a self-signed certificate and authenticating with username and password. - The [smtp_tls.rs] and [smtp_starttls.rs] examples also feature `async`hronous implementations powered by [Tokio](https://tokio.rs/). - These files are prefixed with `tokio02_` or `tokio1_`. + These files are prefixed with `tokio02_`, `tokio1_` or `asyncstd1_`. [basic_html.rs]: ./basic_html.rs [maud_html.rs]: ./maud_html.rs diff --git a/examples/asyncstd1_smtp_starttls.rs b/examples/asyncstd1_smtp_starttls.rs new file mode 100644 index 0000000..678422a --- /dev/null +++ b/examples/asyncstd1_smtp_starttls.rs @@ -0,0 +1,31 @@ +use lettre::{ + transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncStd1Connector, + AsyncStd1Transport, Message, +}; + +#[async_std::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let email = Message::builder() + .from("NoBody ".parse().unwrap()) + .reply_to("Yuin ".parse().unwrap()) + .to("Hei ".parse().unwrap()) + .subject("Happy new async year") + .body(String::from("Be happy with async!")) + .unwrap(); + + let creds = Credentials::new("smtp_username".to_string(), "smtp_password".to_string()); + + // Open a remote connection to gmail using STARTTLS + let mailer = AsyncSmtpTransport::::starttls_relay("smtp.gmail.com") + .unwrap() + .credentials(creds) + .build(); + + // Send the email + match mailer.send(email).await { + Ok(_) => println!("Email sent successfully!"), + Err(e) => panic!("Could not send email: {:?}", e), + } +} diff --git a/examples/asyncstd1_smtp_tls.rs b/examples/asyncstd1_smtp_tls.rs new file mode 100644 index 0000000..6c459f2 --- /dev/null +++ b/examples/asyncstd1_smtp_tls.rs @@ -0,0 +1,31 @@ +use lettre::{ + transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncStd1Connector, + AsyncStd1Transport, Message, +}; + +#[async_std::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let email = Message::builder() + .from("NoBody ".parse().unwrap()) + .reply_to("Yuin ".parse().unwrap()) + .to("Hei ".parse().unwrap()) + .subject("Happy new async year") + .body(String::from("Be happy with async!")) + .unwrap(); + + let creds = Credentials::new("smtp_username".to_string(), "smtp_password".to_string()); + + // Open a remote connection to gmail + let mailer = AsyncSmtpTransport::::relay("smtp.gmail.com") + .unwrap() + .credentials(creds) + .build(); + + // Send the email + match mailer.send(email).await { + Ok(_) => println!("Email sent successfully!"), + Err(e) => panic!("Could not send email: {:?}", e), + } +} diff --git a/src/lib.rs b/src/lib.rs index ec9a75f..83eaa36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,9 @@ //! * **tokio1**: Allow to asyncronously send emails using tokio 1.x //! * **tokio1-rustls-tls**: Async TLS support with the `rustls` crate using tokio 1.x //! * **tokio1-native-tls**: Async TLS support with the `native-tls` crate using tokio 1.x -//! * **async-std1**: Allow to asynchronously send emails using async-std 1.x (SMTP isn't supported yet) +//! * **async-std1**: Allow to asynchronously send emails using async-std 1.x +//! * NOTE: native-tls isn't supported with async-std at the moment +//! * **async-std1-rustls-tls**: Async TLS support with the `rustls` crate using async-std 1.x //! * **r2d2**: Connection pool for SMTP transport //! * **tracing**: Logging using the `tracing` crate //! * **serde**: Serialization/Deserialization of entities @@ -67,6 +69,8 @@ pub use crate::transport::sendmail::SendmailTransport; any(feature = "tokio02", feature = "tokio1") ))] pub use crate::transport::smtp::AsyncSmtpTransport; +#[cfg(all(feature = "smtp-transport", feature = "async-std1"))] +pub use crate::transport::smtp::AsyncStd1Connector; #[cfg(feature = "smtp-transport")] pub use crate::transport::smtp::SmtpTransport; #[cfg(all(feature = "smtp-transport", feature = "tokio02"))] diff --git a/src/transport/smtp/async_transport.rs b/src/transport/smtp/async_transport.rs index 8ccba88..6ec2b23 100644 --- a/src/transport/smtp/async_transport.rs +++ b/src/transport/smtp/async_transport.rs @@ -1,10 +1,12 @@ use async_trait::async_trait; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] use super::Tls; use super::{ client::AsyncSmtpConnection, ClientId, Credentials, Error, Mechanism, Response, SmtpInfo, }; +#[cfg(feature = "async-std1")] +use crate::AsyncStd1Transport; use crate::Envelope; #[cfg(feature = "tokio02")] use crate::Tokio02Transport; @@ -54,6 +56,24 @@ impl Tokio1Transport for AsyncSmtpTransport { } } +#[cfg(feature = "async-std1")] +#[async_trait] +impl AsyncStd1Transport for AsyncSmtpTransport { + type Ok = Response; + type Error = Error; + + /// Sends an email + async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result { + let mut conn = self.inner.connection().await?; + + let result = conn.send(envelope, email).await?; + + conn.quit().await?; + + Ok(result) + } +} + impl AsyncSmtpTransport where C: AsyncSmtpConnector, @@ -68,7 +88,9 @@ where feature = "tokio02-native-tls", feature = "tokio02-rustls-tls", feature = "tokio1-native-tls", - feature = "tokio1-rustls-tls" + feature = "tokio1-rustls-tls", + feature = "async-std1-native-tls", + feature = "async-std1-rustls-tls" ))] pub fn relay(relay: &str) -> Result { use super::{TlsParameters, SUBMISSIONS_PORT}; @@ -95,7 +117,9 @@ where feature = "tokio02-native-tls", feature = "tokio02-rustls-tls", feature = "tokio1-native-tls", - feature = "tokio1-rustls-tls" + feature = "tokio1-rustls-tls", + feature = "async-std1-native-tls", + feature = "async-std1-rustls-tls" ))] pub fn starttls_relay(relay: &str) -> Result { use super::{TlsParameters, SUBMISSION_PORT}; @@ -173,7 +197,9 @@ impl AsyncSmtpTransportBuilder { feature = "tokio02-native-tls", feature = "tokio02-rustls-tls", feature = "tokio1-native-tls", - feature = "tokio1-rustls-tls" + feature = "tokio1-rustls-tls", + feature = "async-std1-native-tls", + feature = "async-std1-rustls-tls" ))] pub fn tls(mut self, tls: Tls) -> Self { self.info.tls = tls; @@ -317,6 +343,48 @@ impl AsyncSmtpConnector for Tokio1Connector { } } +#[derive(Debug, Copy, Clone, Default)] +#[cfg(feature = "async-std1")] +#[cfg_attr(docsrs, doc(cfg(feature = "async-std1")))] +pub struct AsyncStd1Connector; + +#[async_trait] +#[cfg(feature = "async-std1")] +impl AsyncSmtpConnector for AsyncStd1Connector { + async fn connect( + hostname: &str, + port: u16, + hello_name: &ClientId, + tls: &Tls, + ) -> Result { + #[allow(clippy::match_single_binding)] + let tls_parameters = match tls { + #[cfg(any(feature = "async-std1-native-tls", feature = "async-std1-rustls-tls"))] + Tls::Wrapper(ref tls_parameters) => Some(tls_parameters.clone()), + _ => None, + }; + #[allow(unused_mut)] + let mut conn = + AsyncSmtpConnection::connect_asyncstd1(hostname, port, hello_name, tls_parameters) + .await?; + + #[cfg(any(feature = "async-std1-native-tls", feature = "async-std1-rustls-tls"))] + match tls { + Tls::Opportunistic(ref tls_parameters) => { + if conn.can_starttls() { + conn.starttls(tls_parameters.clone(), hello_name).await?; + } + } + Tls::Required(ref tls_parameters) => { + conn.starttls(tls_parameters.clone(), hello_name).await?; + } + _ => (), + } + + Ok(conn) + } +} + mod private { use super::*; @@ -327,4 +395,7 @@ mod private { #[cfg(feature = "tokio1")] impl Sealed for Tokio1Connector {} + + #[cfg(feature = "async-std1")] + impl Sealed for AsyncStd1Connector {} } diff --git a/src/transport/smtp/client/async_connection.rs b/src/transport/smtp/client/async_connection.rs index c217cdc..7b8678b 100644 --- a/src/transport/smtp/client/async_connection.rs +++ b/src/transport/smtp/client/async_connection.rs @@ -73,6 +73,20 @@ impl AsyncSmtpConnection { Self::connect_impl(stream, hello_name).await } + /// Connects to the configured server + /// + /// Sends EHLO and parses server information + #[cfg(feature = "async-std1")] + pub async fn connect_asyncstd1( + hostname: &str, + port: u16, + hello_name: &ClientId, + tls_parameters: Option, + ) -> Result { + let stream = AsyncNetworkStream::connect_asyncstd1(hostname, port, tls_parameters).await?; + Self::connect_impl(stream, hello_name).await + } + async fn connect_impl( stream: AsyncNetworkStream, hello_name: &ClientId, diff --git a/src/transport/smtp/client/async_net.rs b/src/transport/smtp/client/async_net.rs index d56249a..fbec4c3 100644 --- a/src/transport/smtp/client/async_net.rs +++ b/src/transport/smtp/client/async_net.rs @@ -1,4 +1,8 @@ -#[cfg(any(feature = "tokio02-rustls-tls", feature = "tokio1-rustls-tls"))] +#[cfg(any( + feature = "tokio02-rustls-tls", + feature = "tokio1-rustls-tls", + feature = "async-std1-rustls-tls" +))] use std::sync::Arc; use std::{ net::SocketAddr, @@ -6,21 +10,29 @@ use std::{ task::{Context, Poll}, }; +use futures_io::{AsyncRead as FuturesAsyncRead, AsyncWrite as FuturesAsyncWrite}; use futures_io::{Error as IoError, ErrorKind, Result as IoResult}; #[cfg(feature = "tokio02")] use tokio02_crate::io::{AsyncRead as _, AsyncWrite as _}; +#[cfg(feature = "tokio1")] +use tokio1_crate::io::{AsyncRead as _, AsyncWrite as _, ReadBuf as Tokio1ReadBuf}; + +#[cfg(feature = "async-std1")] +use async_std::net::TcpStream as AsyncStd1TcpStream; #[cfg(feature = "tokio02")] use tokio02_crate::net::TcpStream as Tokio02TcpStream; #[cfg(feature = "tokio1")] -use tokio1_crate::io::{AsyncRead as _, AsyncWrite as _, ReadBuf as Tokio1ReadBuf}; -#[cfg(feature = "tokio1")] use tokio1_crate::net::TcpStream as Tokio1TcpStream; +#[cfg(feature = "async-std1-native-tls")] +use async_native_tls::TlsStream as AsyncStd1TlsStream; #[cfg(feature = "tokio02-native-tls")] use tokio02_native_tls_crate::TlsStream as Tokio02TlsStream; #[cfg(feature = "tokio1-native-tls")] use tokio1_native_tls_crate::TlsStream as Tokio1TlsStream; +#[cfg(feature = "async-std1-rustls-tls")] +use async_rustls::client::TlsStream as AsyncStd1RustlsTlsStream; #[cfg(feature = "tokio02-rustls-tls")] use tokio02_rustls::client::TlsStream as Tokio02RustlsTlsStream; #[cfg(feature = "tokio1-rustls-tls")] @@ -30,7 +42,9 @@ use tokio1_rustls::client::TlsStream as Tokio1RustlsTlsStream; feature = "tokio02-native-tls", feature = "tokio02-rustls-tls", feature = "tokio1-native-tls", - feature = "tokio1-rustls-tls" + feature = "tokio1-rustls-tls", + feature = "async-std1-native-tls", + feature = "async-std1-rustls-tls" ))] use super::InnerTlsParameters; use super::TlsParameters; @@ -65,6 +79,15 @@ enum InnerAsyncNetworkStream { /// Encrypted Tokio 1.x TCP stream #[cfg(feature = "tokio1-rustls-tls")] Tokio1RustlsTls(Tokio1RustlsTlsStream), + /// Plain Tokio 1.x TCP stream + #[cfg(feature = "async-std1")] + AsyncStd1Tcp(AsyncStd1TcpStream), + /// Encrypted Tokio 1.x TCP stream + #[cfg(feature = "async-std1-native-tls")] + AsyncStd1NativeTls(AsyncStd1TlsStream), + /// Encrypted Tokio 1.x TCP stream + #[cfg(feature = "async-std1-rustls-tls")] + AsyncStd1RustlsTls(AsyncStd1RustlsTlsStream), /// Can't be built None, } @@ -97,6 +120,12 @@ impl AsyncNetworkStream { } #[cfg(feature = "tokio1-rustls-tls")] InnerAsyncNetworkStream::Tokio1RustlsTls(ref s) => s.get_ref().0.peer_addr(), + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(ref s) => s.peer_addr(), + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(ref s) => s.get_ref().peer_addr(), + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(ref s) => s.get_ref().0.peer_addr(), InnerAsyncNetworkStream::None => { debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); Err(IoError::new( @@ -137,6 +166,21 @@ impl AsyncNetworkStream { Ok(stream) } + #[cfg(feature = "async-std1")] + pub async fn connect_asyncstd1( + hostname: &str, + port: u16, + tls_parameters: Option, + ) -> Result { + let tcp_stream = AsyncStd1TcpStream::connect((hostname, port)).await?; + + let mut stream = AsyncNetworkStream::new(InnerAsyncNetworkStream::AsyncStd1Tcp(tcp_stream)); + if let Some(tls_parameters) = tls_parameters { + stream.upgrade_tls(tls_parameters).await?; + } + Ok(stream) + } + pub async fn upgrade_tls(&mut self, tls_parameters: TlsParameters) -> Result<(), Error> { match &self.inner { #[cfg(all( @@ -181,6 +225,27 @@ impl AsyncNetworkStream { self.inner = Self::upgrade_tokio1_tls(tcp_stream, tls_parameters).await?; Ok(()) } + #[cfg(all( + feature = "async-std1", + not(any(feature = "async-std1-native-tls", feature = "async-std1-rustls-tls")) + ))] + InnerAsyncNetworkStream::AsyncStd1Tcp(_) => { + let _ = tls_parameters; + panic!("Trying to upgrade an AsyncNetworkStream without having enabled either the async-std1-native-tls or the async-std1-rustls-tls feature"); + } + + #[cfg(any(feature = "async-std1-native-tls", feature = "async-std1-rustls-tls"))] + InnerAsyncNetworkStream::AsyncStd1Tcp(_) => { + // get owned TcpStream + let tcp_stream = std::mem::replace(&mut self.inner, InnerAsyncNetworkStream::None); + let tcp_stream = match tcp_stream { + InnerAsyncNetworkStream::AsyncStd1Tcp(tcp_stream) => tcp_stream, + _ => unreachable!(), + }; + + self.inner = Self::upgrade_asyncstd1_tls(tcp_stream, tls_parameters).await?; + Ok(()) + } _ => Ok(()), } } @@ -269,6 +334,55 @@ impl AsyncNetworkStream { } } + #[allow(unused_variables)] + #[cfg(any(feature = "async-std1-native-tls", feature = "async-std1-rustls-tls"))] + async fn upgrade_asyncstd1_tls( + tcp_stream: AsyncStd1TcpStream, + mut tls_parameters: TlsParameters, + ) -> Result { + let domain = std::mem::take(&mut tls_parameters.domain); + + match tls_parameters.connector { + #[cfg(feature = "native-tls")] + InnerTlsParameters::NativeTls(connector) => { + panic!("native-tls isn't supported with async-std yet. See https://github.com/lettre/lettre/pull/531#issuecomment-757893531"); + + /* + #[cfg(not(feature = "async-std1-native-tls"))] + panic!("built without the async-std1-native-tls feature"); + + #[cfg(feature = "async-std1-native-tls")] + return { + use async_native_tls::TlsConnector; + + // TODO: fix + let connector: TlsConnector = todo!(); + // let connector = TlsConnector::from(connector); + let stream = connector.connect(&domain, tcp_stream).await?; + Ok(InnerAsyncNetworkStream::AsyncStd1NativeTls(stream)) + }; + */ + } + #[cfg(feature = "rustls-tls")] + InnerTlsParameters::RustlsTls(config) => { + #[cfg(not(feature = "async-std1-rustls-tls"))] + panic!("built without the async-std1-rustls-tls feature"); + + #[cfg(feature = "async-std1-rustls-tls")] + return { + use async_rustls::webpki::DNSNameRef; + use async_rustls::TlsConnector; + + let domain = DNSNameRef::try_from_ascii_str(&domain)?; + + let connector = TlsConnector::from(Arc::new(config)); + let stream = connector.connect(domain, tcp_stream).await?; + Ok(InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream)) + }; + } + } + } + pub fn is_encrypted(&self) -> bool { match self.inner { #[cfg(feature = "tokio02")] @@ -283,12 +397,18 @@ impl AsyncNetworkStream { InnerAsyncNetworkStream::Tokio1NativeTls(_) => true, #[cfg(feature = "tokio1-rustls-tls")] InnerAsyncNetworkStream::Tokio1RustlsTls(_) => true, + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(_) => false, + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(_) => true, + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => true, InnerAsyncNetworkStream::None => false, } } } -impl futures_io::AsyncRead for AsyncNetworkStream { +impl FuturesAsyncRead for AsyncNetworkStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -328,6 +448,16 @@ impl futures_io::AsyncRead for AsyncNetworkStream { Poll::Pending => Poll::Pending, } } + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(ref mut s) => Pin::new(s).poll_read(cx, buf), + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(ref mut s) => { + Pin::new(s).poll_read(cx, buf) + } + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(ref mut s) => { + Pin::new(s).poll_read(cx, buf) + } InnerAsyncNetworkStream::None => { debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); Poll::Ready(Ok(0)) @@ -336,7 +466,7 @@ impl futures_io::AsyncRead for AsyncNetworkStream { } } -impl futures_io::AsyncWrite for AsyncNetworkStream { +impl FuturesAsyncWrite for AsyncNetworkStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -355,6 +485,16 @@ impl futures_io::AsyncWrite for AsyncNetworkStream { InnerAsyncNetworkStream::Tokio1NativeTls(ref mut s) => Pin::new(s).poll_write(cx, buf), #[cfg(feature = "tokio1-rustls-tls")] InnerAsyncNetworkStream::Tokio1RustlsTls(ref mut s) => Pin::new(s).poll_write(cx, buf), + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(ref mut s) => Pin::new(s).poll_write(cx, buf), + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(ref mut s) => { + Pin::new(s).poll_write(cx, buf) + } + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(ref mut s) => { + Pin::new(s).poll_write(cx, buf) + } InnerAsyncNetworkStream::None => { debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); Poll::Ready(Ok(0)) @@ -376,6 +516,12 @@ impl futures_io::AsyncWrite for AsyncNetworkStream { InnerAsyncNetworkStream::Tokio1NativeTls(ref mut s) => Pin::new(s).poll_flush(cx), #[cfg(feature = "tokio1-rustls-tls")] InnerAsyncNetworkStream::Tokio1RustlsTls(ref mut s) => Pin::new(s).poll_flush(cx), + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(ref mut s) => Pin::new(s).poll_flush(cx), + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(ref mut s) => Pin::new(s).poll_flush(cx), + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(ref mut s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::None => { debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); Poll::Ready(Ok(())) @@ -397,6 +543,12 @@ impl futures_io::AsyncWrite for AsyncNetworkStream { InnerAsyncNetworkStream::Tokio1NativeTls(ref mut s) => Pin::new(s).poll_shutdown(cx), #[cfg(feature = "tokio1-rustls-tls")] InnerAsyncNetworkStream::Tokio1RustlsTls(ref mut s) => Pin::new(s).poll_shutdown(cx), + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(ref mut s) => Pin::new(s).poll_close(cx), + #[cfg(feature = "async-std1-native-tls")] + InnerAsyncNetworkStream::AsyncStd1NativeTls(ref mut s) => Pin::new(s).poll_close(cx), + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(ref mut s) => Pin::new(s).poll_close(cx), InnerAsyncNetworkStream::None => { debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); Poll::Ready(Ok(())) diff --git a/src/transport/smtp/client/mod.rs b/src/transport/smtp/client/mod.rs index 8829e0e..03392b3 100644 --- a/src/transport/smtp/client/mod.rs +++ b/src/transport/smtp/client/mod.rs @@ -27,9 +27,9 @@ #[cfg(feature = "serde")] use std::fmt::Debug; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] pub(crate) use self::async_connection::AsyncSmtpConnection; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] pub(crate) use self::async_net::AsyncNetworkStream; use self::net::NetworkStream; #[cfg(any(feature = "native-tls", feature = "rustls-tls"))] @@ -40,9 +40,9 @@ pub use self::{ tls::{Certificate, Tls, TlsParameters, TlsParametersBuilder}, }; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] mod async_connection; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] mod async_net; mod connection; mod mock; diff --git a/src/transport/smtp/client/tls.rs b/src/transport/smtp/client/tls.rs index 0092997..4885427 100644 --- a/src/transport/smtp/client/tls.rs +++ b/src/transport/smtp/client/tls.rs @@ -118,7 +118,13 @@ impl TlsParametersBuilder { /// depending on which one is available #[cfg(any(feature = "native-tls", feature = "rustls-tls"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls-tls"))))] + // TODO: remove below line once native-tls is supported with async-std + #[allow(unreachable_code)] pub fn build(self) -> Result { + // TODO: remove once native-tls is supported with async-std + #[cfg(all(feature = "rustls-tls", feature = "async-std1"))] + return self.build_rustls(); + #[cfg(feature = "native-tls")] return self.build_native(); diff --git a/src/transport/smtp/mod.rs b/src/transport/smtp/mod.rs index b94259b..61e47d5 100644 --- a/src/transport/smtp/mod.rs +++ b/src/transport/smtp/mod.rs @@ -154,11 +154,13 @@ //! ``` //! +#[cfg(feature = "async-std1")] +pub use self::async_transport::AsyncStd1Connector; #[cfg(feature = "tokio02")] pub use self::async_transport::Tokio02Connector; #[cfg(feature = "tokio1")] pub use self::async_transport::Tokio1Connector; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] pub use self::async_transport::{ AsyncSmtpConnector, AsyncSmtpTransport, AsyncSmtpTransportBuilder, }; @@ -181,7 +183,7 @@ use crate::transport::smtp::{ use client::Tls; use std::time::Duration; -#[cfg(any(feature = "tokio02", feature = "tokio1"))] +#[cfg(any(feature = "tokio02", feature = "tokio1", feature = "async-std1"))] mod async_transport; pub mod authentication; pub mod client; diff --git a/tests/transport_file.rs b/tests/transport_file.rs index f0996cb..970d686 100644 --- a/tests/transport_file.rs +++ b/tests/transport_file.rs @@ -91,7 +91,7 @@ mod test { } #[cfg(feature = "async-std1")] - #[async_attributes::test] + #[async_std::test] async fn file_transport_asyncstd1() { use lettre::AsyncStd1Transport; diff --git a/tests/transport_sendmail.rs b/tests/transport_sendmail.rs index fda2a64..d9f2cd1 100644 --- a/tests/transport_sendmail.rs +++ b/tests/transport_sendmail.rs @@ -24,7 +24,7 @@ mod test { } #[cfg(feature = "async-std1")] - #[async_attributes::test] + #[async_std::test] async fn sendmail_transport_asyncstd1() { use lettre::AsyncStd1Transport; diff --git a/tests/transport_stub.rs b/tests/transport_stub.rs index 2266f00..f75e7b2 100644 --- a/tests/transport_stub.rs +++ b/tests/transport_stub.rs @@ -24,7 +24,7 @@ mod test { } #[cfg(feature = "async-std1")] - #[async_attributes::test] + #[async_std::test] async fn stub_transport_asyncstd1() { use lettre::AsyncStd1Transport;