From e2b641ae894821d7eb5f5cdf6d6a54a0c5e34627 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 26 Jul 2020 14:04:00 +0200 Subject: [PATCH 1/4] Refactor async-std support to prepare for more async runtimes Renames the async feature to async-std1 Moves things out of mod async since it makes things have to be written as r#async and makes them feel less important. --- Cargo.toml | 2 +- src/lib.rs | 43 ++++++++---------- src/transport/file/mod.rs | 84 ++++++++++++++++------------------- src/transport/sendmail/mod.rs | 53 ++++++++++------------ src/transport/stub/mod.rs | 28 +++++------- 5 files changed, 91 insertions(+), 119 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f353bef..1ae174e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ harness = false name = "transport_smtp" [features] -async = ["async-std", "async-trait", "async-attributes"] +async-std1 = ["async-std", "async-trait", "async-attributes"] builder = ["mime", "base64", "hyperx", "rand", "quoted_printable"] default = ["file-transport", "smtp-transport", "native-tls", "hostname", "r2d2", "sendmail-transport", "builder"] file-transport = ["serde", "serde_json"] diff --git a/src/lib.rs b/src/lib.rs index e35f293..1e19b11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,8 @@ pub use crate::transport::smtp::r2d2::SmtpConnectionManager; #[cfg(feature = "smtp-transport")] pub use crate::transport::smtp::{SmtpTransport, Tls}; pub use crate::{address::Address, transport::stub::StubTransport}; +#[cfg(feature = "async-std1")] +use async_trait::async_trait; #[cfg(feature = "builder")] use std::convert::TryFrom; use std::{error::Error as StdError, fmt}; @@ -151,33 +153,24 @@ pub trait Transport { fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result; } -#[cfg(feature = "async")] -pub mod r#async { +/// Async Transport method for emails +#[cfg(feature = "async-std1")] +#[async_trait] +pub trait AsyncStd1Transport { + /// Result types for the transport + type Ok: fmt::Debug; + type Error: StdError; - use super::*; - use async_trait::async_trait; - - #[async_trait] - pub trait Transport { - /// Result types for the transport - type Ok: fmt::Debug; - type Error: StdError; - - /// Sends the email - #[cfg(feature = "builder")] - // TODO take &Message - async fn send(&self, message: Message) -> Result { - let raw = message.formatted(); - let envelope = message.envelope(); - self.send_raw(&envelope, &raw).await - } - - async fn send_raw( - &self, - envelope: &Envelope, - email: &[u8], - ) -> Result; + /// Sends the email + #[cfg(feature = "builder")] + // TODO take &Message + async fn send(&self, message: Message) -> Result { + let raw = message.formatted(); + let envelope = message.envelope(); + self.send_raw(&envelope, &raw).await } + + async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result; } #[cfg(test)] diff --git a/src/transport/file/mod.rs b/src/transport/file/mod.rs index bc2a28e..dda3292 100644 --- a/src/transport/file/mod.rs +++ b/src/transport/file/mod.rs @@ -35,10 +35,12 @@ //! TODO //! ``` +#[cfg(feature = "async-std1")] +use crate::AsyncStd1Transport; use crate::{transport::file::error::Error, Envelope, Transport}; +#[cfg(feature = "async-std1")] +use async_trait::async_trait; use std::{ - fs::File, - io::prelude::*, path::{Path, PathBuf}, str, }; @@ -72,11 +74,12 @@ struct SerializableEmail<'a> { message: Option<&'a str>, } -impl Transport for FileTransport { - type Ok = Id; - type Error = Error; - - fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result { +impl FileTransport { + fn send_raw_impl( + &self, + envelope: &Envelope, + email: &[u8], + ) -> Result<(Uuid, PathBuf, String), serde_json::Error> { let email_id = Uuid::new_v4(); let file = self.path.join(format!("{}.json", email_id)); @@ -94,51 +97,40 @@ impl Transport for FileTransport { }), }?; - File::create(file.as_path())?.write_all(serialized.as_bytes())?; + Ok((email_id, file, serialized)) + } +} + +impl Transport for FileTransport { + type Ok = Id; + type Error = Error; + + fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result { + use std::fs::File; + use std::io::Write; + + let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?; + + let mut file = File::create(file)?; + file.write_all(serialized.as_bytes())?; Ok(email_id.to_string()) } } -#[cfg(feature = "async")] -pub mod r#async { - use super::{FileTransport, Id, SerializableEmail}; - use crate::{r#async::Transport, transport::file::error::Error, Envelope}; - use async_std::fs::File; - use async_std::prelude::*; - use async_trait::async_trait; - use std::str; - use uuid::Uuid; +#[cfg(feature = "async-std1")] +#[async_trait] +impl AsyncStd1Transport for FileTransport { + type Ok = Id; + type Error = Error; - #[async_trait] - impl Transport for FileTransport { - type Ok = Id; - type Error = Error; + async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result { + use async_std::fs::File; + use async_std::io::prelude::WriteExt; - async fn send_raw( - &self, - envelope: &Envelope, - email: &[u8], - ) -> Result { - let email_id = Uuid::new_v4(); - let file = self.path.join(format!("{}.json", email_id)); + let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?; - let serialized = match str::from_utf8(email) { - // Serialize as UTF-8 string if possible - Ok(m) => serde_json::to_string(&SerializableEmail { - envelope: envelope.clone(), - message: Some(m), - raw_message: None, - }), - Err(_) => serde_json::to_string(&SerializableEmail { - envelope: envelope.clone(), - message: None, - raw_message: Some(email), - }), - }?; - - let mut file = File::create(file.as_path()).await?; - file.write_all(serialized.as_bytes()).await?; - Ok(email_id.to_string()) - } + let mut file = File::create(file).await?; + file.write_all(serialized.as_bytes()).await?; + Ok(email_id.to_string()) } } diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs index e8f3f32..3a9df98 100644 --- a/src/transport/sendmail/mod.rs +++ b/src/transport/sendmail/mod.rs @@ -23,7 +23,11 @@ //! # } //! ``` +#[cfg(feature = "async-std1")] +use crate::AsyncStd1Transport; use crate::{transport::sendmail::error::Error, Envelope, Transport}; +#[cfg(feature = "async-std1")] +use async_trait::async_trait; use std::{ convert::AsRef, ffi::OsString, @@ -88,41 +92,30 @@ impl Transport for SendmailTransport { } } -#[cfg(feature = "async")] -pub mod r#async { - use super::SendmailTransport; - use crate::{r#async::Transport, transport::sendmail::error::Error, Envelope}; - use async_trait::async_trait; - use std::io::Write; +#[cfg(feature = "async-std1")] +#[async_trait] +impl AsyncStd1Transport for SendmailTransport { + type Ok = (); + type Error = Error; - #[async_trait] - impl Transport for SendmailTransport { - type Ok = (); - type Error = Error; + async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result { + let mut command = self.command(envelope); + let email = email.to_vec(); // TODO: Convert to real async, once async-std has a process implementation. - async fn send_raw( - &self, - envelope: &Envelope, - email: &[u8], - ) -> Result { - let mut command = self.command(envelope); - let email = email.to_vec(); + let output = async_std::task::spawn_blocking(move || { + // Spawn the sendmail command + let mut process = command.spawn()?; - let output = async_std::task::spawn_blocking(move || { - // Spawn the sendmail command - let mut process = command.spawn()?; + process.stdin.as_mut().unwrap().write_all(&email)?; + process.wait_with_output() + }) + .await?; - process.stdin.as_mut().unwrap().write_all(&email)?; - process.wait_with_output() - }) - .await?; - - if output.status.success() { - Ok(()) - } else { - Err(Error::Client(String::from_utf8(output.stderr)?)) - } + if output.status.success() { + Ok(()) + } else { + Err(Error::Client(String::from_utf8(output.stderr)?)) } } } diff --git a/src/transport/stub/mod.rs b/src/transport/stub/mod.rs index dc5c3f6..0ab2a86 100644 --- a/src/transport/stub/mod.rs +++ b/src/transport/stub/mod.rs @@ -22,7 +22,11 @@ //! assert!(result.is_ok()); //! ``` +#[cfg(feature = "async-std1")] +use crate::AsyncStd1Transport; use crate::{Envelope, Transport}; +#[cfg(feature = "async-std1")] +use async_trait::async_trait; use std::{error::Error as StdError, fmt}; #[derive(Debug, Copy, Clone)] @@ -74,23 +78,13 @@ impl Transport for StubTransport { } } -#[cfg(feature = "async")] -pub mod r#async { - use super::StubTransport; - use crate::{r#async::Transport, transport::stub::Error, Envelope}; - use async_trait::async_trait; +#[cfg(feature = "async-std1")] +#[async_trait] +impl AsyncStd1Transport for StubTransport { + type Ok = (); + type Error = Error; - #[async_trait] - impl Transport for StubTransport { - type Ok = (); - type Error = Error; - - async fn send_raw( - &self, - _envelope: &Envelope, - _email: &[u8], - ) -> Result { - self.response - } + async fn send_raw(&self, _envelope: &Envelope, _email: &[u8]) -> Result { + self.response } } From 95bc3e67453e443abc3120ce5dbb1a7c04deffc8 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 26 Jul 2020 14:09:50 +0200 Subject: [PATCH 2/4] Enable the async-std1 feature when building on docs.rs --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1ae174e..73eb769 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,9 @@ sendmail-transport = [] smtp-transport = ["bufstream", "base64", "nom"] unstable = [] +[package.metadata.docs.rs] +features = ["async-std1"] + [[example]] name = "smtp" required-features = ["smtp-transport"] From 75e309731ee0d5ca51119190a8f967ae8ec6da22 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 26 Jul 2020 14:13:28 +0200 Subject: [PATCH 3/4] Update async-std tests --- tests/transport_file.rs | 5 +++-- tests/transport_sendmail.rs | 5 +++-- tests/transport_stub.rs | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/transport_file.rs b/tests/transport_file.rs index b390930..769918a 100644 --- a/tests/transport_file.rs +++ b/tests/transport_file.rs @@ -35,10 +35,11 @@ mod test { remove_file(file).unwrap(); } - #[cfg(feature = "async")] + #[cfg(feature = "async-std1")] #[async_attributes::test] async fn file_transport_async() { - use lettre::r#async::Transport; + use lettre::AsyncStd1Transport; + let sender = FileTransport::new(temp_dir()); let email = Message::builder() .from("NoBody ".parse().unwrap()) diff --git a/tests/transport_sendmail.rs b/tests/transport_sendmail.rs index 2512c1e..2ca2748 100644 --- a/tests/transport_sendmail.rs +++ b/tests/transport_sendmail.rs @@ -20,10 +20,11 @@ mod test { assert!(result.is_ok()); } - #[cfg(feature = "async")] + #[cfg(feature = "async-std1")] #[async_attributes::test] async fn sendmail_transport_async() { - use lettre::r#async::Transport; + use lettre::AsyncStd1Transport; + let sender = SendmailTransport::new(); let email = Message::builder() .from("NoBody ".parse().unwrap()) diff --git a/tests/transport_stub.rs b/tests/transport_stub.rs index 56aaaaa..905107f 100644 --- a/tests/transport_stub.rs +++ b/tests/transport_stub.rs @@ -17,10 +17,11 @@ fn stub_transport() { sender_ko.send(&email).unwrap_err(); } -#[cfg(feature = "async")] +#[cfg(feature = "async-std1")] #[async_attributes::test] async fn stub_transport_async() { - use lettre::r#async::Transport; + use lettre::AsyncStd1Transport; + let sender_ok = StubTransport::new_ok(); let sender_ko = StubTransport::new_error(); let email = Message::builder() From 213fe1dc4e9c3d952d446da61d32e0a820a326c6 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 26 Jul 2020 14:38:29 +0200 Subject: [PATCH 4/4] Update ci configuration for async feature rename --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0052719..0e1d645 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --features=async + args: --features=async-std1 check: name: Check