Add tokio ^0.2 support (#440)
* Use fs::write for writing files * Fix running tests without tokio
This commit is contained in:
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -39,6 +39,10 @@ jobs:
|
||||
with:
|
||||
command: test
|
||||
args: --features=async-std1
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --features=tokio02
|
||||
|
||||
check:
|
||||
name: Check
|
||||
|
||||
@@ -20,6 +20,7 @@ maintenance = { status = "actively-developed" }
|
||||
async-attributes = { version = "1.1", optional = true }
|
||||
async-std = { version = "1.5", optional = true, features = ["unstable"] }
|
||||
async-trait = { version = "0.1", optional = true }
|
||||
tokio02_crate = { package = "tokio", version = "0.2.7", features = ["fs", "process", "io-util"], optional = true }
|
||||
base64 = { version = "0.12", optional = true }
|
||||
bufstream = { version = "0.1", optional = true }
|
||||
hostname = { version = "0.3", optional = true }
|
||||
@@ -46,6 +47,7 @@ criterion = "0.3"
|
||||
env_logger = "0.7"
|
||||
glob = "0.3"
|
||||
walkdir = "2"
|
||||
tokio02_crate = { package = "tokio", version = "0.2.7", features = ["macros", "rt-threaded"] }
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
@@ -53,6 +55,7 @@ name = "transport_smtp"
|
||||
|
||||
[features]
|
||||
async-std1 = ["async-std", "async-trait", "async-attributes"]
|
||||
tokio02 = ["tokio02_crate", "async-trait"]
|
||||
builder = ["mime", "base64", "hyperx", "rand", "quoted_printable"]
|
||||
default = ["file-transport", "smtp-transport", "native-tls", "hostname", "r2d2", "sendmail-transport", "builder"]
|
||||
file-transport = ["serde", "serde_json"]
|
||||
|
||||
21
src/lib.rs
21
src/lib.rs
@@ -55,7 +55,7 @@ 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")]
|
||||
#[cfg(any(feature = "async-std1", feature = "tokio02"))]
|
||||
use async_trait::async_trait;
|
||||
#[cfg(feature = "builder")]
|
||||
use std::convert::TryFrom;
|
||||
@@ -173,6 +173,25 @@ pub trait AsyncStd1Transport {
|
||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[async_trait]
|
||||
pub trait Tokio02Transport {
|
||||
/// 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<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>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
use crate::AsyncStd1Transport;
|
||||
#[cfg(feature = "tokio02")]
|
||||
use crate::Tokio02Transport;
|
||||
use crate::{transport::file::error::Error, Envelope, Transport};
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[cfg(any(feature = "async-std1", feature = "tokio02"))]
|
||||
use async_trait::async_trait;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
@@ -106,13 +108,11 @@ impl Transport for FileTransport {
|
||||
type Error = Error;
|
||||
|
||||
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::fs;
|
||||
|
||||
let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?;
|
||||
|
||||
let mut file = File::create(file)?;
|
||||
file.write_all(serialized.as_bytes())?;
|
||||
fs::write(file, serialized)?;
|
||||
Ok(email_id.to_string())
|
||||
}
|
||||
}
|
||||
@@ -124,13 +124,27 @@ impl AsyncStd1Transport for FileTransport {
|
||||
type Error = Error;
|
||||
|
||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
use async_std::fs::File;
|
||||
use async_std::io::prelude::WriteExt;
|
||||
use async_std::fs;
|
||||
|
||||
let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?;
|
||||
|
||||
let mut file = File::create(file).await?;
|
||||
file.write_all(serialized.as_bytes()).await?;
|
||||
fs::write(file, serialized).await?;
|
||||
Ok(email_id.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[async_trait]
|
||||
impl Tokio02Transport for FileTransport {
|
||||
type Ok = Id;
|
||||
type Error = Error;
|
||||
|
||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
use tokio02_crate::fs;
|
||||
|
||||
let (email_id, file, serialized) = self.send_raw_impl(envelope, email)?;
|
||||
|
||||
fs::write(file, serialized).await?;
|
||||
Ok(email_id.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
use crate::AsyncStd1Transport;
|
||||
#[cfg(feature = "tokio02")]
|
||||
use crate::Tokio02Transport;
|
||||
use crate::{transport::sendmail::error::Error, Envelope, Transport};
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[cfg(any(feature = "async-std1", feature = "tokio02"))]
|
||||
use async_trait::async_trait;
|
||||
use std::{
|
||||
convert::AsRef,
|
||||
@@ -71,6 +73,21 @@ impl SendmailTransport {
|
||||
.stdout(Stdio::piped());
|
||||
c
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
fn tokio02_command(&self, envelope: &Envelope) -> tokio02_crate::process::Command {
|
||||
use tokio02_crate::process::Command;
|
||||
|
||||
let mut c = Command::new(&self.command);
|
||||
c.kill_on_drop(true);
|
||||
c.arg("-i")
|
||||
.arg("-f")
|
||||
.arg(envelope.from().map(|f| f.as_ref()).unwrap_or("\"\""))
|
||||
.args(envelope.to())
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped());
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
impl Transport for SendmailTransport {
|
||||
@@ -119,3 +136,28 @@ impl AsyncStd1Transport for SendmailTransport {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[async_trait]
|
||||
impl Tokio02Transport for SendmailTransport {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
use tokio02_crate::io::AsyncWriteExt;
|
||||
|
||||
let mut command = self.tokio02_command(envelope);
|
||||
|
||||
// Spawn the sendmail command
|
||||
let mut process = command.spawn()?;
|
||||
|
||||
process.stdin.as_mut().unwrap().write_all(&email).await?;
|
||||
let output = process.wait_with_output().await?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Client(String::from_utf8(output.stderr)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
use crate::AsyncStd1Transport;
|
||||
#[cfg(feature = "tokio02")]
|
||||
use crate::Tokio02Transport;
|
||||
use crate::{Envelope, Transport};
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[cfg(any(feature = "async-std1", feature = "tokio02"))]
|
||||
use async_trait::async_trait;
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
@@ -88,3 +90,14 @@ impl AsyncStd1Transport for StubTransport {
|
||||
self.response
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[async_trait]
|
||||
impl Tokio02Transport for StubTransport {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
async fn send_raw(&self, _envelope: &Envelope, _email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
self.response
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ mod test {
|
||||
io::Read,
|
||||
};
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
use tokio02_crate as tokio;
|
||||
|
||||
#[test]
|
||||
fn file_transport() {
|
||||
use lettre::Transport;
|
||||
@@ -37,7 +40,7 @@ mod test {
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[async_attributes::test]
|
||||
async fn file_transport_async() {
|
||||
async fn file_transport_asyncstd1() {
|
||||
use lettre::AsyncStd1Transport;
|
||||
|
||||
let sender = FileTransport::new(temp_dir());
|
||||
@@ -63,4 +66,33 @@ mod test {
|
||||
"{\"envelope\":{\"forward_path\":[\"hei@domain.tld\"],\"reverse_path\":\"nobody@domain.tld\"},\"raw_message\":null,\"message\":\"From: NoBody <nobody@domain.tld>\\r\\nReply-To: Yuin <yuin@domain.tld>\\r\\nTo: Hei <hei@domain.tld>\\r\\nSubject: Happy new year\\r\\nDate: Tue, 15 Nov 1994 08:12:31 GMT\\r\\n\\r\\nBe happy!\"}");
|
||||
remove_file(file).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[tokio::test]
|
||||
async fn file_transport_tokio02() {
|
||||
use lettre::Tokio02Transport;
|
||||
|
||||
let sender = FileTransport::new(temp_dir());
|
||||
let email = Message::builder()
|
||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
||||
.reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
|
||||
.to("Hei <hei@domain.tld>".parse().unwrap())
|
||||
.subject("Happy new year")
|
||||
.date("Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap())
|
||||
.body("Be happy!")
|
||||
.unwrap();
|
||||
|
||||
let result = sender.send(email).await;
|
||||
let id = result.unwrap();
|
||||
|
||||
let file = temp_dir().join(format!("{}.json", id));
|
||||
let mut f = File::open(file.clone()).unwrap();
|
||||
let mut buffer = String::new();
|
||||
let _ = f.read_to_string(&mut buffer);
|
||||
|
||||
assert_eq!(
|
||||
buffer,
|
||||
"{\"envelope\":{\"forward_path\":[\"hei@domain.tld\"],\"reverse_path\":\"nobody@domain.tld\"},\"raw_message\":null,\"message\":\"From: NoBody <nobody@domain.tld>\\r\\nReply-To: Yuin <yuin@domain.tld>\\r\\nTo: Hei <hei@domain.tld>\\r\\nSubject: Happy new year\\r\\nDate: Tue, 15 Nov 1994 08:12:31 GMT\\r\\n\\r\\nBe happy!\"}");
|
||||
remove_file(file).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
mod test {
|
||||
use lettre::{transport::sendmail::SendmailTransport, Message};
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
use tokio02_crate as tokio;
|
||||
|
||||
#[test]
|
||||
fn sendmail_transport() {
|
||||
use lettre::Transport;
|
||||
@@ -22,7 +25,7 @@ mod test {
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[async_attributes::test]
|
||||
async fn sendmail_transport_async() {
|
||||
async fn sendmail_transport_asyncstd1() {
|
||||
use lettre::AsyncStd1Transport;
|
||||
|
||||
let sender = SendmailTransport::new();
|
||||
@@ -38,4 +41,23 @@ mod test {
|
||||
let result = sender.send(email).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[tokio::test]
|
||||
async fn sendmail_transport_tokio02() {
|
||||
use lettre::Tokio02Transport;
|
||||
|
||||
let sender = SendmailTransport::new();
|
||||
let email = Message::builder()
|
||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
||||
.reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
|
||||
.to("Hei <hei@domain.tld>".parse().unwrap())
|
||||
.subject("Happy new year")
|
||||
.date("Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap())
|
||||
.body("Be happy!")
|
||||
.unwrap();
|
||||
|
||||
let result = sender.send(email).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use lettre::{transport::stub::StubTransport, Message};
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
use tokio02_crate as tokio;
|
||||
|
||||
#[test]
|
||||
fn stub_transport() {
|
||||
use lettre::Transport;
|
||||
@@ -19,7 +22,7 @@ fn stub_transport() {
|
||||
|
||||
#[cfg(feature = "async-std1")]
|
||||
#[async_attributes::test]
|
||||
async fn stub_transport_async() {
|
||||
async fn stub_transport_asyncstd1() {
|
||||
use lettre::AsyncStd1Transport;
|
||||
|
||||
let sender_ok = StubTransport::new_ok();
|
||||
@@ -36,3 +39,23 @@ async fn stub_transport_async() {
|
||||
sender_ok.send(email.clone()).await.unwrap();
|
||||
sender_ko.send(email).await.unwrap_err();
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio02")]
|
||||
#[tokio::test]
|
||||
async fn stub_transport_tokio02() {
|
||||
use lettre::Tokio02Transport;
|
||||
|
||||
let sender_ok = StubTransport::new_ok();
|
||||
let sender_ko = StubTransport::new_error();
|
||||
let email = Message::builder()
|
||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
||||
.reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
|
||||
.to("Hei <hei@domain.tld>".parse().unwrap())
|
||||
.subject("Happy new year")
|
||||
.date("Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap())
|
||||
.body("Be happy!")
|
||||
.unwrap();
|
||||
|
||||
sender_ok.send(email.clone()).await.unwrap();
|
||||
sender_ko.send(email).await.unwrap_err();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user