Files
lettre/src/lib.rs
Alexis Mousset 53aa5b4df6 Replace email builder by a new implementation (#393)
* Update dependencies (#386)

* Update dependencies and set MSRV to 1.40

* update hyperx

* Use display instead of description for errors

* Make hostname an optional feature

* Envelope from headers

* Update hyperx to 1.0

* rename builder to message

* Cleanup and make Transport send Messages

* Update rustls from 0.16 to 0.17

* Move transports into a common folder

* Merge imports from same crate

* Add message creation example to the site

* Hide "extern crate" in doc examples

* Add References and In-Reply-To methods

* Add message-id header

* Add blog posts and improve doc examples
2020-04-18 21:10:03 +00:00

194 lines
5.8 KiB
Rust

//! 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<Address>,
/// The envelope sender address
reverse_path: Option<Address>,
}
impl Envelope {
/// Creates a new envelope, which may fail if `to` is empty.
pub fn new(from: Option<Address>, to: Vec<Address>) -> Result<Envelope, Error> {
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<Self, Self::Error> {
let from = match headers.get::<header::Sender>() {
// 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::<header::From>() {
Some(header::From(ref a)) => Some(a.iter().next().unwrap().email.clone()),
None => None,
},
};
fn add_addresses_from_mailboxes(
addresses: &mut Vec<Address>,
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::<header::To>().map(|h| &h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Cc>().map(|h| &h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Bcc>().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<B>) -> 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(),);
}
}