//! Lettre provides an email builder and several email transports. //! #![doc(html_root_url = "https://docs.rs/lettre/0.10.0")] #![doc(html_favicon_url = "https://lettre.at/favicon.png")] #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")] #![deny( missing_copy_implementations, trivial_casts, trivial_numeric_casts, unstable_features, unused_import_braces )] pub mod address; pub mod error; #[cfg(feature = "builder")] pub mod message; pub mod transport; pub use crate::address::Address; use crate::error::Error; #[cfg(feature = "builder")] pub use crate::message::{ header::{self, Headers}, Mailboxes, Message, }; #[cfg(feature = "file-transport")] pub use crate::transport::file::FileTransport; #[cfg(feature = "sendmail-transport")] pub use crate::transport::sendmail::SendmailTransport; #[cfg(feature = "smtp-transport")] pub use crate::transport::smtp::client::net::ClientTlsParameters; #[cfg(all(feature = "smtp-transport", feature = "connection-pool"))] pub use crate::transport::smtp::r2d2::SmtpConnectionManager; #[cfg(feature = "smtp-transport")] pub use crate::transport::smtp::{ClientSecurity, SmtpClient, SmtpTransport}; #[cfg(feature = "builder")] use std::convert::TryFrom; use std::fmt::Display; /// Simple email envelope representation /// /// We only accept mailboxes, and do not support source routes (as per RFC). #[derive(PartialEq, Eq, Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Envelope { /// The envelope recipients' addresses /// /// This can not be empty. forward_path: Vec
, /// The envelope sender address reverse_path: Option
, } impl Envelope { /// Creates a new envelope, which may fail if `to` is empty. pub fn new(from: Option
, to: Vec
) -> Result { if to.is_empty() { return Err(Error::MissingTo); } Ok(Envelope { forward_path: to, reverse_path: from, }) } /// Destination addresses of the envelope pub fn to(&self) -> &[Address] { self.forward_path.as_slice() } /// Source address of the envelope pub fn from(&self) -> Option<&Address> { self.reverse_path.as_ref() } } impl TryFrom<&Headers> for Envelope { type Error = Error; fn try_from(headers: &Headers) -> Result { let from = match headers.get::() { // If there is a Sender, use it Some(header::Sender(a)) => Some(a.email.clone()), // ... else use the first From address None => match headers.get::() { Some(header::From(ref a)) => Some(a.iter().next().unwrap().email.clone()), None => None, }, }; fn add_addresses_from_mailboxes( addresses: &mut Vec
, mailboxes: Option<&Mailboxes>, ) { if let Some(mailboxes) = mailboxes { for mailbox in mailboxes.iter() { addresses.push(mailbox.email.clone()); } } } let mut to = vec![]; add_addresses_from_mailboxes(&mut to, headers.get::().map(|h| &h.0)); add_addresses_from_mailboxes(&mut to, headers.get::().map(|h| &h.0)); add_addresses_from_mailboxes(&mut to, headers.get::().map(|h| &h.0)); Self::new(from, to) } } // FIXME generate random log id /// Transport method for emails pub trait Transport<'a, B> { /// Result type for the transport type Result; /// Sends the email /// FIXME not mut fn send(&mut self, email: Message) -> Self::Result where B: Display; /* { &mut self, Box::new(Cursor::new(email.to_string().as_bytes())), email.envelope(), Uuid::new_v4().to_string(), }*/ // TODO allow sending generic data } #[cfg(test)] mod test { use super::*; use crate::message::{header, Mailbox, Mailboxes}; use hyperx::header::Headers; #[test] fn envelope_from_headers() { let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); let to = Mailboxes::new().with("amousset@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(header::From(from)); headers.set(header::To(to)); assert_eq!( Envelope::try_from(&headers).unwrap(), Envelope::new( Some(Address::new("kayo", "example.com").unwrap()), vec![Address::new("amousset", "example.com").unwrap()] ) .unwrap() ); } #[test] fn envelope_from_headers_sender() { let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap()); let to = Mailboxes::new().with("amousset@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(header::From(from)); headers.set(header::Sender(sender)); headers.set(header::To(to)); assert_eq!( Envelope::try_from(&headers).unwrap(), Envelope::new( Some(Address::new("kayo2", "example.com").unwrap()), vec![Address::new("amousset", "example.com").unwrap()] ) .unwrap() ); } #[test] fn envelope_from_headers_no_to() { let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(header::From(from)); headers.set(header::Sender(sender)); assert!(Envelope::try_from(&headers).is_err(),); } }