summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Korber <p.korber@1aim.com>2018-09-06 18:15:26 +0200
committerPhilipp Korber <p.korber@1aim.com>2018-09-06 18:15:26 +0200
commit708a5454bb3579ba94a922b94e1e21d177db4ff2 (patch)
tree2239d68765f71a1eb740902232770022c7afbe10
parent8df7b8b1604bb340f25c58005e1b424255012495 (diff)
chore(deps) use new-tokio-smtp::send_mails functionality
- reduces complexity of the interface _and_ the implementation
-rw-r--r--Cargo.toml2
-rw-r--r--src/error.rs64
-rw-r--r--src/lib.rs22
-rw-r--r--src/send_mail.rs232
4 files changed, 125 insertions, 195 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 305a8d0..3c3e708 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
authors = ["Philipp Korber <p.korber@1aim.com>"]
name = "mail-smtp"
-version = "0.1.0"
+version = "0.2.0-wip"
categories = []
description = "[internal/mail-api] combines mail-types with new-tokio-smtp"
documentation = "https://docs.rs/mail-smtp"
diff --git a/src/error.rs b/src/error.rs
index 850e0ee..8cc1c8e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,10 +1,9 @@
//! Module containing all custom errors.
use std::{io as std_io};
-use new_tokio_smtp::error::{ConnectingFailed, LogicError};
+use new_tokio_smtp::error::{ConnectingFailed, LogicError, GeneralError};
use mail::error::MailError;
-
/// Error used when sending a mail fails.
///
/// Failing to encode a mail before sending
@@ -12,7 +11,8 @@ use mail::error::MailError;
/// it's done "on the fly" when sending a mail.
#[derive(Debug, Fail)]
pub enum MailSendError {
- /// Creating the mail failed.
+
+ /// Something is wrong with the mail instance (e.g. it can't be encoded).
///
/// This can happen for a number of reasons including:
///
@@ -32,33 +32,8 @@ pub enum MailSendError {
/// 3. Server rejects sending the mail for other reasons (it's
/// closing, overloaded etc.).
#[fail(display = "{}", _0)]
- Smtp(LogicError)
-}
-
-impl From<MailError> for MailSendError {
- fn from(err: MailError) -> Self {
- MailSendError::Mail(err)
- }
-}
+ Smtp(LogicError),
-impl From<LogicError> for MailSendError {
- fn from(err: LogicError) -> Self {
- MailSendError::Smtp(err)
- }
-}
-
-/// Error returned when something on the transport layer failed.
-///
-/// Thinks causing this error include:
-///
-/// - TLS required but not supported by server
-/// - authentication not valid
-/// - connection "broke" (e.g. because you
-/// internet connection is gone or the server
-/// crashed)
-///
-#[derive(Debug, Fail)]
-pub enum TransportError {
/// Setting up the connection failed.
///
/// Failures can include but are not limited to:
@@ -79,14 +54,37 @@ pub enum TransportError {
Io(std_io::Error)
}
-impl From<std_io::Error> for TransportError {
+impl From<MailError> for MailSendError {
+ fn from(err: MailError) -> Self {
+ MailSendError::Mail(err)
+ }
+}
+
+impl From<LogicError> for MailSendError {
+ fn from(err: LogicError) -> Self {
+ MailSendError::Smtp(err)
+ }
+}
+
+impl From<std_io::Error> for MailSendError {
fn from(err: std_io::Error) -> Self {
- TransportError::Io(err)
+ MailSendError::Io(err)
}
}
-impl From<ConnectingFailed> for TransportError {
+impl From<ConnectingFailed> for MailSendError {
fn from(err: ConnectingFailed) -> Self {
- TransportError::Connecting(err)
+ MailSendError::Connecting(err)
+ }
+}
+
+impl From<GeneralError> for MailSendError {
+ fn from(err: GeneralError) -> Self {
+ use self::GeneralError::*;
+ match err {
+ Connecting(err) => Self::from(err),
+ Cmd(err) => Self::from(err),
+ Io(err) => Self::from(err)
+ }
}
}
diff --git a/src/lib.rs b/src/lib.rs
index edbb6b2..6b510fc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,7 +30,7 @@
//! use mail_headers::*;
//! use mail_headers::components::Domain;
//! use mail_types::{Mail, default_impl::simple_context};
-//! use mail_smtp::{send_mails, ConnectionConfig};
+//! use mail_smtp::{self as smtp, ConnectionConfig};
//!
//! # fn main() {
//! // this is normally done _once per application instance_
@@ -50,7 +50,7 @@
//! // simplified examples
//! let con_config = ConnectionConfig::build_local_unencrypted().build();
//!
-//! let fut = send_mails(con_config, vec![mail.into()], ctx);
+//! let fut = smtp::send(mail.into(), con_config, ctx);
//! let results = fut.wait();
//! # }
//! ```
@@ -74,13 +74,9 @@ pub use self::request::MailRequest;
#[cfg(feature="extended-api")]
pub use self::request::derive_envelop_data_from_mail;
-pub use self::send_mail::{
- send_mails,
- SendMailResult
-};
+pub use self::send_mail::{send, send_batch};
#[cfg(feature="extended-api")]
-pub use self::send_mail::{encode_mails, send_encoded_mails};
-
+pub use self::send_mail::encode;
pub use new_tokio_smtp::{ConnectionConfig, ConnectionBuilder};
@@ -100,7 +96,11 @@ pub mod auth {
pub mod misc {
//! A small collection of usefull types re-exported from `new-tokio-smtp`.
- pub use new_tokio_smtp::ClientId;
- pub use new_tokio_smtp::Domain;
- pub use new_tokio_smtp::AddressLiteral;
+ pub use new_tokio_smtp::{
+ ClientId,
+ Domain,
+ AddressLiteral,
+ SetupTls,
+ DefaultTlsSetup
+ };
} \ No newline at end of file
diff --git a/src/send_mail.rs b/src/send_mail.rs
index 8730647..861159e 100644
--- a/src/send_mail.rs
+++ b/src/send_mail.rs
@@ -1,166 +1,98 @@
-use std::iter::FromIterator;
-use std::vec;
+//! Module implementing mail sending using `new-tokio-smtp::send_mail`.
-use futures::future::{self, Either, Loop, Future};
-use new_tokio_smtp::{Cmd, Connection, ConnectionConfig, SetupTls};
-use new_tokio_smtp::send_mail::{self as smtp};
+use std::iter::{once as one};
-use mail_common::MailType;
-use mail_common::encoder::EncodingBuffer;
-use mail::Context;
-use mail::error::MailError;
-
-use ::resolve_all::ResolveAll;
-use ::request::MailRequest;
-use ::error::{MailSendError, TransportError};
-
-/// Result of encoding a mail.
-pub type EncodeMailResult = Result<smtp::MailEnvelop, MailError>;
-
-/// Creates a `Future` which encodes all mails.
-///
-/// To encode the mails this function turns every `MailRequest` into mails with
-/// envelope data, then creates a `Future` resolving when the mail is ready to
-/// be encoded and chain this result by offloading the actual encoding of each
-/// mail to a thread pool. Lastly all of these `Future`s are polled in parallel
-/// by the returned `Future`.
-///
-/// # Error
-///
-/// The `Future` will never error, but it will resolve to a vector of results
-/// representing the encoding result for each mail in the input separately.
-pub fn encode_mails(
- requests: impl IntoIterator<Item=MailRequest>,
- ctx: impl Context
- //TODO[futures/v>=0.2 | rust/! type]: use Never or !
-) -> impl Future<Item=Vec<EncodeMailResult>, Error=()> + Send
-{
- let pending = requests
- .into_iter()
- .map(|request| {
- let (mail, envelop_data) =
- match request.into_mail_with_envelop() {
- Ok(pair) => pair,
- Err(e) => return Either::A(future::err(e.into()))
- };
+use futures::{
+ stream::{self, Stream},
+ future::{self, Future, Either}
+};
- let _ctx = ctx.clone();
- let fut = mail
- .into_encodeable_mail(ctx.clone())
- .and_then(move |enc_mail| _ctx.offload_fn(move || {
- let (mail_type, requirement) =
- if envelop_data.needs_smtputf8() {
- (MailType::Internationalized, smtp::EncodingRequirement::Smtputf8)
- } else {
- (MailType::Ascii, smtp::EncodingRequirement::None)
- };
+use mail_common::{
+ MailType,
+ encoder::EncodingBuffer
+};
+use mail::Context;
- let mut buffer = EncodingBuffer::new(mail_type);
- enc_mail.encode(&mut buffer)?;
+use new_tokio_smtp::{
+ ConnectionConfig,
+ Cmd,
+ SetupTls,
+ send_mail::MailEnvelop,
+ Connection,
+ send_mail as smtp
+};
+
+use ::{
+ error::MailSendError,
+ request::MailRequest
+};
+
+pub fn send<A, S>(mail: MailRequest, conconf: ConnectionConfig<A, S>, ctx: impl Context)
+ -> impl Future<Item=(), Error=MailSendError>
+ where A: Cmd, S: SetupTls
+{
+ let fut = encode(mail, ctx)
+ .then(move |envelop_res| Connection
+ ::connect_send_quit(conconf, one(envelop_res))
+ .collect())
+ .map(|mut results| results.pop().expect("[BUG] sending one mail expects one result"));
- let vec_buffer: Vec<_> = buffer.into();
- let smtp_mail = smtp::Mail::new(requirement, vec_buffer);
+ fut
+}
- Ok(smtp::MailEnvelop::from((smtp_mail, envelop_data)))
- }));
+pub fn send_batch<A, S, C>(
+ mails: Vec<MailRequest>,
+ conconf: ConnectionConfig<A, S>,
+ ctx: C
+) -> impl Stream<Item=(), Error=MailSendError>
+ where A: Cmd, S: SetupTls, C: Context
+{
+ let iter = mails.into_iter().map(move |mail| encode(mail, ctx.clone()));
- Either::B(fut)
- });
+ let fut = collect_res(stream::futures_ordered(iter))
+ .map(move |vec_of_res| Connection::connect_send_quit(conconf, vec_of_res))
+ .flatten_stream();
- ResolveAll::from_iter(pending)
+ fut
}
-/// Results of sending an encoded mail.
-pub type SendMailResult = Result<(), MailSendError>;
-
-/// Sends all encoded mails through the given `Connection`.
-///
-/// This method accepts an iterator of `EncodedMailResult`'s as it's
-/// meant to be chained with `encode_mails`.
-///
-/// # Error
-///
-/// The returned `Future` resolves to a vector of results, one for each mail
-/// sent.
-///
-/// If a transport error happens (e.g. an I/O error) a tuple consisting of
-/// the `Error`, the already sent mails and and iterator of the remaining mails is
-/// returned.
-pub fn send_encoded_mails<I>(con: Connection, mails: I)
- -> impl Future<
- Item=(Connection, Vec<SendMailResult>),
- Error=(TransportError, Vec<SendMailResult>, I::IntoIter)>
- where I: IntoIterator<Item=EncodeMailResult>, I::IntoIter: 'static
+//FIXME[futures/v>=0.2] use Error=Never
+fn collect_res<S, E>(stream: S) -> impl Future<Item=Vec<Result<S::Item, S::Error>>, Error=E>
+ where S: Stream
{
- let iter = mails.into_iter();
- let results = Vec::new();
- let fut = future::loop_fn((con, iter, results), |(con, mut iter, mut results)| match iter.next() {
- None => Either::A(future::ok(Loop::Break((con, results)))),
- Some(Err(err)) => {
- results.push(Err(MailSendError::from(err)));
- Either::A(future::ok(Loop::Continue((con, iter, results))))
- },
- Some(Ok(envelop)) => Either::B(con
- .send_mail(envelop)
- .then(move |res| match res {
- Ok((con, logic_result)) => {
- results.push(logic_result.map_err(|(_idx, err)| MailSendError::from(err)));
- Ok(Loop::Continue((con, iter, results)))
- },
- Err(err) => {
- Err((TransportError::Io(err), results, iter))
- }
- }))
- });
-
- fut
+ stream.then(|res| Ok(res)).collect()
}
-/// Send mails to _one_ specific mail server.
-///
-/// This encodes the mails, opens a connection, sends the mails over and
-/// closes the connection again.
-///
-/// While this uses the `To` field of a mail to determine the SMTP receiver it
-/// does not resolve the server based on the mail address domain. This means
-/// it's best suited for sending to a Mail Submission Agent (MSA), but less for
-/// sending to a Mail Exchanger (MX).
-///
-/// Automatically handling `Bcc`/`Cc` is _not yet_ implemented.
-pub fn send_mails(
- config: ConnectionConfig<impl Cmd, impl SetupTls>,
- requests: impl IntoIterator<Item=MailRequest>,
- ctx: impl Context)
- -> impl Future<
- Item=Vec<SendMailResult>,
- Error=(TransportError, Vec<SendMailResult>, vec::IntoIter<EncodeMailResult>)>
+
+pub fn encode<C>(request: MailRequest, ctx: C)
+ -> impl Future<Item=MailEnvelop, Error=MailSendError>
+ where C: Context
{
+ let (mail, envelop_data) =
+ match request.into_mail_with_envelop() {
+ Ok(pair) => pair,
+ Err(e) => return Either::A(future::err(e.into()))
+ };
+
+ let fut = mail
+ .into_encodeable_mail(ctx.clone())
+ .and_then(move |enc_mail| ctx.offload_fn(move || {
+ let (mail_type, requirement) =
+ if envelop_data.needs_smtputf8() {
+ (MailType::Internationalized, smtp::EncodingRequirement::Smtputf8)
+ } else {
+ (MailType::Ascii, smtp::EncodingRequirement::None)
+ };
- let fut = encode_mails(requests, ctx)
- .map_err(|_| unreachable!())
- .and_then(|mails| {
- if mails.iter().all(|r| r.is_err()) {
- let send_skipped = mails
- .into_iter()
- .map(|result| match result {
- Ok(_) => unreachable!(),
- Err(err) => Err(MailSendError::Mail(err))
- })
- .collect();
-
- Either::A(future::ok(send_skipped))
- } else {
- let fut = Connection
- ::connect(config)
- .then(|result| match result {
- Err(err) => Either::A(future::err((TransportError::Connecting(err), Vec::new(), mails.into_iter()))),
- Ok(con) => Either::B(send_encoded_mails(con, mails))
- })
- .and_then(|(con, results)| con.quit().then(|_| Ok(results)));
-
- Either::B(fut)
- }
- });
+ let mut buffer = EncodingBuffer::new(mail_type);
+ enc_mail.encode(&mut buffer)?;
- fut
-}
+ let vec_buffer: Vec<_> = buffer.into();
+ let smtp_mail = smtp::Mail::new(requirement, vec_buffer);
+
+ Ok(smtp::MailEnvelop::from((smtp_mail, envelop_data)))
+ }))
+ .map_err(MailSendError::from);
+
+ Either::B(fut)
+} \ No newline at end of file