1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
use ::{ExecFuture, Cmd, Io, EhloData};
use ::error::{MissingCapabilities};
/// An either of two commands
///
/// Useful for cases when we need either of two commands but wouldn't like to use [`BoxedCmd`].
///
/// For example, `ConnectionConfig<EitherCmd<command::Noop, command::auth::Plain>>` would implement `Clone` trait, but ConnectionConfig<BoxedCmd> would not. So we can use once created config for connection several times.
///
/// ```
/// extern crate new_tokio_smtp;
///
/// use new_tokio_smtp::{command::{auth, Noop, EitherCmd}, ConnectionConfig};
///
/// fn main() {
/// let address = "127.0.0.1:25".parse().unwrap();
/// let hostname = "smtp.example.com".parse().unwrap();
/// let username = "user@example.com";
/// let password = "top-secret";
///
/// let auth_command = match auth::Plain::from_username(username, password) {
/// Ok(plain_auth) => EitherCmd::B(plain_auth),
/// Err(_) => EitherCmd::A(Noop),
/// };
///
/// let config = ConnectionConfig::builder_with_addr(address, hostname)
/// .auth(auth_command)
/// .build();
///
/// {
/// let config = config.clone();
/// // ...connect and send emails
/// }
/// }
/// ```
#[derive(Debug, Clone)]
pub enum EitherCmd<A, B> {
A(A),
B(B),
}
impl<A, B> Cmd for EitherCmd<A, B>
where
A: Cmd,
B: Cmd,
{
fn check_cmd_availability(&self, caps: Option<&EhloData>) -> Result<(), MissingCapabilities> {
match self {
EitherCmd::A(a) => a.check_cmd_availability(caps),
EitherCmd::B(b) => b.check_cmd_availability(caps),
}
}
fn exec(self, con: Io) -> ExecFuture {
match self {
EitherCmd::A(a) => a.exec(con),
EitherCmd::B(b) => b.exec(con),
}
}
}
/// An alternative of two commands
///
/// Useful for cases when we need execute only one of two commands depending from availability.
///
/// For example, `ConnectionConfig<SelectCmd<command::auth::Plain, command::auth::Login>>` can help alternate two auth methods.
///
/// ```
/// extern crate new_tokio_smtp;
///
/// use new_tokio_smtp::{command::{auth, SelectCmd}, ConnectionConfig};
///
/// fn main() {
/// let address = "127.0.0.1:25".parse().unwrap();
/// let hostname = "smtp.example.com".parse().unwrap();
/// let username = "user@example.com";
/// let password = "top-secret";
///
/// let plain_auth = auth::Plain::from_username(username, password).unwrap();
/// let login_auth = auth::Login::new(username, password);
///
/// let config = ConnectionConfig::builder_with_addr(address, hostname)
/// .auth(SelectCmd(plain_auth, login_auth))
/// .build();
/// // ...connect and send emails
/// }
/// ```
#[derive(Debug, Clone)]
pub struct SelectCmd<A, B>(pub A, pub B);
impl<A, B> Cmd for SelectCmd<A, B>
where
A: Cmd,
B: Cmd,
{
fn check_cmd_availability(&self, caps: Option<&EhloData>) -> Result<(), MissingCapabilities> {
self.0
.check_cmd_availability(caps)
.or_else(|_| self.1.check_cmd_availability(caps))
}
fn exec(self, con: Io) -> ExecFuture {
if self.0.check_cmd_availability(con.ehlo_data()).is_ok() {
Box::new(self.0.exec(con))
} else {
Box::new(self.1.exec(con))
}
}
}