Merge pull request #439 from paolobarbolini/async-01
Refactor async-std support to prepare for more async runtimes
This commit is contained in:
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
|||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --features=async
|
args: --features=async-std1
|
||||||
|
|
||||||
check:
|
check:
|
||||||
name: Check
|
name: Check
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ harness = false
|
|||||||
name = "transport_smtp"
|
name = "transport_smtp"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
async = ["async-std", "async-trait", "async-attributes"]
|
async-std1 = ["async-std", "async-trait", "async-attributes"]
|
||||||
builder = ["mime", "base64", "hyperx", "rand", "quoted_printable"]
|
builder = ["mime", "base64", "hyperx", "rand", "quoted_printable"]
|
||||||
default = ["file-transport", "smtp-transport", "native-tls", "hostname", "r2d2", "sendmail-transport", "builder"]
|
default = ["file-transport", "smtp-transport", "native-tls", "hostname", "r2d2", "sendmail-transport", "builder"]
|
||||||
file-transport = ["serde", "serde_json"]
|
file-transport = ["serde", "serde_json"]
|
||||||
@@ -62,6 +62,9 @@ sendmail-transport = []
|
|||||||
smtp-transport = ["bufstream", "base64", "nom"]
|
smtp-transport = ["bufstream", "base64", "nom"]
|
||||||
unstable = []
|
unstable = []
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["async-std1"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "smtp"
|
name = "smtp"
|
||||||
required-features = ["smtp-transport"]
|
required-features = ["smtp-transport"]
|
||||||
|
|||||||
43
src/lib.rs
43
src/lib.rs
@@ -55,6 +55,8 @@ pub use crate::transport::smtp::r2d2::SmtpConnectionManager;
|
|||||||
#[cfg(feature = "smtp-transport")]
|
#[cfg(feature = "smtp-transport")]
|
||||||
pub use crate::transport::smtp::{SmtpTransport, Tls};
|
pub use crate::transport::smtp::{SmtpTransport, Tls};
|
||||||
pub use crate::{address::Address, transport::stub::StubTransport};
|
pub use crate::{address::Address, transport::stub::StubTransport};
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use async_trait::async_trait;
|
||||||
#[cfg(feature = "builder")]
|
#[cfg(feature = "builder")]
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::{error::Error as StdError, fmt};
|
use std::{error::Error as StdError, fmt};
|
||||||
@@ -151,33 +153,24 @@ pub trait Transport {
|
|||||||
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error>;
|
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
/// Async Transport method for emails
|
||||||
pub mod r#async {
|
#[cfg(feature = "async-std1")]
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AsyncStd1Transport {
|
||||||
|
/// Result types for the transport
|
||||||
|
type Ok: fmt::Debug;
|
||||||
|
type Error: StdError;
|
||||||
|
|
||||||
use super::*;
|
/// Sends the email
|
||||||
use async_trait::async_trait;
|
#[cfg(feature = "builder")]
|
||||||
|
// TODO take &Message
|
||||||
#[async_trait]
|
async fn send(&self, message: Message) -> Result<Self::Ok, Self::Error> {
|
||||||
pub trait Transport {
|
let raw = message.formatted();
|
||||||
/// Result types for the transport
|
let envelope = message.envelope();
|
||||||
type Ok: fmt::Debug;
|
self.send_raw(&envelope, &raw).await
|
||||||
type Error: StdError;
|
|
||||||
|
|
||||||
/// Sends the email
|
|
||||||
#[cfg(feature = "builder")]
|
|
||||||
// TODO take &Message
|
|
||||||
async fn send(&self, message: Message) -> Result<Self::Ok, Self::Error> {
|
|
||||||
let raw = message.formatted();
|
|
||||||
let envelope = message.envelope();
|
|
||||||
self.send_raw(&envelope, &raw).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_raw(
|
|
||||||
&self,
|
|
||||||
envelope: &Envelope,
|
|
||||||
email: &[u8],
|
|
||||||
) -> Result<Self::Ok, Self::Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -35,10 +35,12 @@
|
|||||||
//! TODO
|
//! TODO
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use crate::AsyncStd1Transport;
|
||||||
use crate::{transport::file::error::Error, Envelope, Transport};
|
use crate::{transport::file::error::Error, Envelope, Transport};
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
|
||||||
io::prelude::*,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str,
|
str,
|
||||||
};
|
};
|
||||||
@@ -72,11 +74,12 @@ struct SerializableEmail<'a> {
|
|||||||
message: Option<&'a str>,
|
message: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transport for FileTransport {
|
impl FileTransport {
|
||||||
type Ok = Id;
|
fn send_raw_impl(
|
||||||
type Error = Error;
|
&self,
|
||||||
|
envelope: &Envelope,
|
||||||
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
email: &[u8],
|
||||||
|
) -> Result<(Uuid, PathBuf, String), serde_json::Error> {
|
||||||
let email_id = Uuid::new_v4();
|
let email_id = Uuid::new_v4();
|
||||||
let file = self.path.join(format!("{}.json", email_id));
|
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<Self::Ok, Self::Error> {
|
||||||
|
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())
|
Ok(email_id.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
pub mod r#async {
|
#[async_trait]
|
||||||
use super::{FileTransport, Id, SerializableEmail};
|
impl AsyncStd1Transport for FileTransport {
|
||||||
use crate::{r#async::Transport, transport::file::error::Error, Envelope};
|
type Ok = Id;
|
||||||
use async_std::fs::File;
|
type Error = Error;
|
||||||
use async_std::prelude::*;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::str;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[async_trait]
|
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||||
impl Transport for FileTransport {
|
use async_std::fs::File;
|
||||||
type Ok = Id;
|
use async_std::io::prelude::WriteExt;
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
async fn send_raw(
|
let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?;
|
||||||
&self,
|
|
||||||
envelope: &Envelope,
|
|
||||||
email: &[u8],
|
|
||||||
) -> Result<Self::Ok, Self::Error> {
|
|
||||||
let email_id = Uuid::new_v4();
|
|
||||||
let file = self.path.join(format!("{}.json", email_id));
|
|
||||||
|
|
||||||
let serialized = match str::from_utf8(email) {
|
let mut file = File::create(file).await?;
|
||||||
// Serialize as UTF-8 string if possible
|
file.write_all(serialized.as_bytes()).await?;
|
||||||
Ok(m) => serde_json::to_string(&SerializableEmail {
|
Ok(email_id.to_string())
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,11 @@
|
|||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use crate::AsyncStd1Transport;
|
||||||
use crate::{transport::sendmail::error::Error, Envelope, Transport};
|
use crate::{transport::sendmail::error::Error, Envelope, Transport};
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::{
|
use std::{
|
||||||
convert::AsRef,
|
convert::AsRef,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
@@ -88,41 +92,30 @@ impl Transport for SendmailTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
pub mod r#async {
|
#[async_trait]
|
||||||
use super::SendmailTransport;
|
impl AsyncStd1Transport for SendmailTransport {
|
||||||
use crate::{r#async::Transport, transport::sendmail::error::Error, Envelope};
|
type Ok = ();
|
||||||
use async_trait::async_trait;
|
type Error = Error;
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
#[async_trait]
|
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||||
impl Transport for SendmailTransport {
|
let mut command = self.command(envelope);
|
||||||
type Ok = ();
|
let email = email.to_vec();
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
// TODO: Convert to real async, once async-std has a process implementation.
|
// TODO: Convert to real async, once async-std has a process implementation.
|
||||||
async fn send_raw(
|
let output = async_std::task::spawn_blocking(move || {
|
||||||
&self,
|
// Spawn the sendmail command
|
||||||
envelope: &Envelope,
|
let mut process = command.spawn()?;
|
||||||
email: &[u8],
|
|
||||||
) -> Result<Self::Ok, Self::Error> {
|
|
||||||
let mut command = self.command(envelope);
|
|
||||||
let email = email.to_vec();
|
|
||||||
|
|
||||||
let output = async_std::task::spawn_blocking(move || {
|
process.stdin.as_mut().unwrap().write_all(&email)?;
|
||||||
// Spawn the sendmail command
|
process.wait_with_output()
|
||||||
let mut process = command.spawn()?;
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
process.stdin.as_mut().unwrap().write_all(&email)?;
|
if output.status.success() {
|
||||||
process.wait_with_output()
|
Ok(())
|
||||||
})
|
} else {
|
||||||
.await?;
|
Err(Error::Client(String::from_utf8(output.stderr)?))
|
||||||
|
|
||||||
if output.status.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::Client(String::from_utf8(output.stderr)?))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,11 @@
|
|||||||
//! assert!(result.is_ok());
|
//! assert!(result.is_ok());
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use crate::AsyncStd1Transport;
|
||||||
use crate::{Envelope, Transport};
|
use crate::{Envelope, Transport};
|
||||||
|
#[cfg(feature = "async-std1")]
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::{error::Error as StdError, fmt};
|
use std::{error::Error as StdError, fmt};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@@ -74,23 +78,13 @@ impl Transport for StubTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
pub mod r#async {
|
#[async_trait]
|
||||||
use super::StubTransport;
|
impl AsyncStd1Transport for StubTransport {
|
||||||
use crate::{r#async::Transport, transport::stub::Error, Envelope};
|
type Ok = ();
|
||||||
use async_trait::async_trait;
|
type Error = Error;
|
||||||
|
|
||||||
#[async_trait]
|
async fn send_raw(&self, _envelope: &Envelope, _email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||||
impl Transport for StubTransport {
|
self.response
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
async fn send_raw(
|
|
||||||
&self,
|
|
||||||
_envelope: &Envelope,
|
|
||||||
_email: &[u8],
|
|
||||||
) -> Result<Self::Ok, Self::Error> {
|
|
||||||
self.response
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,11 @@ mod test {
|
|||||||
remove_file(file).unwrap();
|
remove_file(file).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
#[async_attributes::test]
|
#[async_attributes::test]
|
||||||
async fn file_transport_async() {
|
async fn file_transport_async() {
|
||||||
use lettre::r#async::Transport;
|
use lettre::AsyncStd1Transport;
|
||||||
|
|
||||||
let sender = FileTransport::new(temp_dir());
|
let sender = FileTransport::new(temp_dir());
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ mod test {
|
|||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
#[async_attributes::test]
|
#[async_attributes::test]
|
||||||
async fn sendmail_transport_async() {
|
async fn sendmail_transport_async() {
|
||||||
use lettre::r#async::Transport;
|
use lettre::AsyncStd1Transport;
|
||||||
|
|
||||||
let sender = SendmailTransport::new();
|
let sender = SendmailTransport::new();
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ fn stub_transport() {
|
|||||||
sender_ko.send(&email).unwrap_err();
|
sender_ko.send(&email).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async-std1")]
|
||||||
#[async_attributes::test]
|
#[async_attributes::test]
|
||||||
async fn stub_transport_async() {
|
async fn stub_transport_async() {
|
||||||
use lettre::r#async::Transport;
|
use lettre::AsyncStd1Transport;
|
||||||
|
|
||||||
let sender_ok = StubTransport::new_ok();
|
let sender_ok = StubTransport::new_ok();
|
||||||
let sender_ko = StubTransport::new_error();
|
let sender_ko = StubTransport::new_error();
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
|
|||||||
Reference in New Issue
Block a user