Rework internal structs
This commit is contained in:
@@ -48,12 +48,12 @@ extern crate lettre;
|
||||
extern crate lettre_email;
|
||||
extern crate mime;
|
||||
|
||||
use lettre::{EmailTransport, SmtpTransport};
|
||||
use lettre_email::EmailBuilder;
|
||||
use lettre::{Transport, SmtpTransport};
|
||||
use lettre_email::Email;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let email = EmailBuilder::new()
|
||||
let email = Email::builder()
|
||||
// Addresses can be specified by the tuple (email, alias)
|
||||
.to(("user@example.org", "Firstname Lastname"))
|
||||
// ... or by an address only
|
||||
@@ -68,7 +68,7 @@ fn main() {
|
||||
let mut mailer = SmtpTransport::builder_unencrypted_localhost().unwrap()
|
||||
.build();
|
||||
// Send the email
|
||||
let result = mailer.send(&email);
|
||||
let result = mailer.send(email.into());
|
||||
|
||||
if result.is_ok() {
|
||||
println!("Email sent");
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
extern crate lettre;
|
||||
extern crate test;
|
||||
|
||||
use lettre::{ClientSecurity, SmtpTransport};
|
||||
use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail};
|
||||
use lettre::{ClientSecurity, SmtpTransport, Envelope};
|
||||
use lettre::{EmailAddress, SendableEmail, Transport};
|
||||
use lettre::smtp::ConnectionReuseParameters;
|
||||
|
||||
#[bench]
|
||||
@@ -13,13 +13,15 @@ fn bench_simple_send(b: &mut test::Bencher) {
|
||||
.unwrap()
|
||||
.build();
|
||||
b.iter(|| {
|
||||
let email = SimpleSendableEmail::new(
|
||||
EmailAddress::new("user@localhost".to_string()),
|
||||
vec![EmailAddress::new("root@localhost".to_string())],
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
let result = sender.send(&email);
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
});
|
||||
}
|
||||
@@ -31,13 +33,15 @@ fn bench_reuse_send(b: &mut test::Bencher) {
|
||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
|
||||
.build();
|
||||
b.iter(|| {
|
||||
let email = SimpleSendableEmail::new(
|
||||
EmailAddress::new("user@localhost".to_string()),
|
||||
vec![EmailAddress::new("root@localhost".to_string())],
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
let result = sender.send(&email);
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
});
|
||||
sender.close()
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
extern crate env_logger;
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::{EmailTransport, SimpleSendableEmail, SmtpTransport};
|
||||
use lettre::{EmailAddress, Envelope, SendableEmail, SmtpTransport, Transport};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"my-message-id".to_string(),
|
||||
"Hello ß☺ example".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
// Open a local connection on port 25
|
||||
let mut mailer = SmtpTransport::builder_unencrypted_localhost()
|
||||
.unwrap()
|
||||
.build();
|
||||
// Send the email
|
||||
let result = mailer.send(&email);
|
||||
let result = mailer.send(email);
|
||||
|
||||
if result.is_ok() {
|
||||
println!("Email sent");
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
//! It can be useful for testing purposes, or if you want to keep track of sent messages.
|
||||
//!
|
||||
|
||||
use EmailTransport;
|
||||
use Transport;
|
||||
use Envelope;
|
||||
use SendableEmail;
|
||||
use SimpleSendableEmail;
|
||||
use file::error::FileResult;
|
||||
use serde_json;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -18,38 +17,46 @@ pub mod error;
|
||||
/// Writes the content and the envelope information to a file
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
|
||||
pub struct FileEmailTransport {
|
||||
pub struct FileTransport {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl FileEmailTransport {
|
||||
impl FileTransport {
|
||||
/// Creates a new transport to the given directory
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> FileEmailTransport {
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> FileTransport {
|
||||
let mut path_buf = PathBuf::new();
|
||||
path_buf.push(path);
|
||||
FileEmailTransport { path: path_buf }
|
||||
FileTransport { path: path_buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read + 'a> EmailTransport<'a, T> for FileEmailTransport {
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
|
||||
struct SerializableEmail {
|
||||
envelope: Envelope,
|
||||
message_id: String,
|
||||
message: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> Transport<'a> for FileTransport {
|
||||
type Result = FileResult;
|
||||
|
||||
fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> FileResult {
|
||||
fn send(&mut self, email: SendableEmail) -> FileResult {
|
||||
let message_id = email.message_id().to_string();
|
||||
let envelope = email.envelope().clone();
|
||||
|
||||
let mut file = self.path.clone();
|
||||
file.push(format!("{}.txt", email.message_id()));
|
||||
file.push(format!("{}.json", message_id));
|
||||
|
||||
let mut f = File::create(file.as_path())?;
|
||||
|
||||
let mut message_content = String::new();
|
||||
let _ = email.message().read_to_string(&mut message_content);
|
||||
let serialized = serde_json::to_string(&SerializableEmail {
|
||||
envelope,
|
||||
message_id,
|
||||
message: email.message_to_string()?.as_bytes().to_vec(),
|
||||
})?;
|
||||
|
||||
let simple_email = SimpleSendableEmail::new_with_envelope(
|
||||
email.envelope().clone(),
|
||||
email.message_id().to_string(),
|
||||
message_content,
|
||||
);
|
||||
|
||||
f.write_all(serde_json::to_string(&simple_email)?.as_bytes())?;
|
||||
f.write_all(serialized.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
//! Lettre is a mailer written in Rust. It provides a simple email builder and several transports.
|
||||
//!
|
||||
//! This mailer contains the available transports for your emails. To be sendable, the
|
||||
//! emails have to implement `SendableEmail`.
|
||||
//! This mailer contains the available transports for your emails.
|
||||
//!
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/lettre/0.9.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
|
||||
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
|
||||
unused_qualifications)]
|
||||
#![deny(missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code,
|
||||
unstable_features, unused_import_braces, unused_qualifications)]
|
||||
#[cfg(feature = "smtp-transport")]
|
||||
extern crate base64;
|
||||
#[cfg(feature = "smtp-transport")]
|
||||
@@ -28,6 +26,8 @@ extern crate native_tls;
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
#[cfg(feature = "serde-impls")]
|
||||
extern crate serde;
|
||||
#[cfg(feature = "serde-impls")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[cfg(feature = "file-transport")]
|
||||
@@ -42,7 +42,7 @@ pub mod stub;
|
||||
pub mod file;
|
||||
|
||||
#[cfg(feature = "file-transport")]
|
||||
pub use file::FileEmailTransport;
|
||||
pub use file::FileTransport;
|
||||
#[cfg(feature = "sendmail-transport")]
|
||||
pub use sendmail::SendmailTransport;
|
||||
#[cfg(feature = "smtp-transport")]
|
||||
@@ -51,6 +51,8 @@ pub use smtp::{ClientSecurity, SmtpTransport};
|
||||
pub use smtp::client::net::ClientTlsParameters;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::io::Read;
|
||||
use std::io::Cursor;
|
||||
use std::io;
|
||||
use std::error::Error as StdError;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -150,133 +152,74 @@ impl Envelope {
|
||||
pub fn from(&self) -> Option<&EmailAddress> {
|
||||
self.reverse_path.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new builder
|
||||
pub fn builder() -> EnvelopeBuilder {
|
||||
EnvelopeBuilder::new()
|
||||
pub enum Message {
|
||||
Reader(Box<Read + Send>),
|
||||
Bytes(Cursor<Vec<u8>>),
|
||||
}
|
||||
|
||||
impl Read for Message {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
Message::Reader(ref mut rdr) => rdr.read(buf),
|
||||
Message::Bytes(ref mut rdr) => rdr.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple email envelope representation
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Default)]
|
||||
pub struct EnvelopeBuilder {
|
||||
/// The envelope recipients' addresses
|
||||
to: Vec<EmailAddress>,
|
||||
/// The envelope sender address
|
||||
from: Option<EmailAddress>,
|
||||
/// Sendable email structure
|
||||
pub struct SendableEmail {
|
||||
envelope: Envelope,
|
||||
message_id: String,
|
||||
message: Message,
|
||||
}
|
||||
|
||||
impl EnvelopeBuilder {
|
||||
/// Constructs an envelope with no recipients and an empty sender
|
||||
pub fn new() -> Self {
|
||||
EnvelopeBuilder {
|
||||
to: vec![],
|
||||
from: None,
|
||||
impl SendableEmail {
|
||||
pub fn new(envelope: Envelope, message_id: String, message: Vec<u8>) -> SendableEmail {
|
||||
SendableEmail {
|
||||
envelope,
|
||||
message_id,
|
||||
message: Message::Bytes(Cursor::new(message)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a recipient
|
||||
pub fn to<S: Into<EmailAddress>>(mut self, address: S) -> Self {
|
||||
self.add_to(address);
|
||||
self
|
||||
pub fn new_with_reader(
|
||||
envelope: Envelope,
|
||||
message_id: String,
|
||||
message: Box<Read + Send>,
|
||||
) -> SendableEmail {
|
||||
SendableEmail {
|
||||
envelope,
|
||||
message_id,
|
||||
message: Message::Reader(message),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a recipient
|
||||
pub fn add_to<S: Into<EmailAddress>>(&mut self, address: S) {
|
||||
self.to.push(address.into());
|
||||
pub fn envelope(&self) -> &Envelope {
|
||||
&self.envelope
|
||||
}
|
||||
|
||||
/// Sets the sender
|
||||
pub fn from<S: Into<EmailAddress>>(mut self, address: S) -> Self {
|
||||
self.set_from(address);
|
||||
self
|
||||
pub fn message_id(&self) -> &str {
|
||||
&self.message_id
|
||||
}
|
||||
|
||||
/// Sets the sender
|
||||
pub fn set_from<S: Into<EmailAddress>>(&mut self, address: S) {
|
||||
self.from = Some(address.into());
|
||||
pub fn message(self) -> Message {
|
||||
self.message
|
||||
}
|
||||
|
||||
/// Build the envelope
|
||||
pub fn build(self) -> EmailResult<Envelope> {
|
||||
Envelope::new(self.from, self.to)
|
||||
pub fn message_to_string(mut self) -> Result<String, io::Error> {
|
||||
let mut message_content = String::new();
|
||||
self.message.read_to_string(&mut message_content)?;
|
||||
Ok(message_content)
|
||||
}
|
||||
}
|
||||
|
||||
/// Email sendable by an SMTP client
|
||||
pub trait SendableEmail<'a, T: Read + 'a> {
|
||||
/// Envelope
|
||||
fn envelope(&self) -> Envelope;
|
||||
/// Message ID, used for logging
|
||||
fn message_id(&self) -> String;
|
||||
/// Message content
|
||||
fn message(&'a self) -> Box<T>;
|
||||
}
|
||||
|
||||
/// Transport method for emails
|
||||
pub trait EmailTransport<'a, U: Read + 'a> {
|
||||
pub trait Transport<'a> {
|
||||
/// Result type for the transport
|
||||
type Result;
|
||||
|
||||
/// Sends the email
|
||||
fn send<T: SendableEmail<'a, U> + 'a>(&mut self, email: &'a T) -> Self::Result;
|
||||
}
|
||||
|
||||
/// Minimal email structure
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
|
||||
pub struct SimpleSendableEmail {
|
||||
/// Envelope
|
||||
envelope: Envelope,
|
||||
/// Message ID
|
||||
message_id: String,
|
||||
/// Message content
|
||||
message: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SimpleSendableEmail {
|
||||
/// Returns a new email
|
||||
pub fn new(
|
||||
from_address: String,
|
||||
to_addresses: &[String],
|
||||
message_id: String,
|
||||
message: String,
|
||||
) -> EmailResult<SimpleSendableEmail> {
|
||||
let to: Result<Vec<EmailAddress>, Error> = to_addresses
|
||||
.iter()
|
||||
.map(|x| EmailAddress::new(x.clone()))
|
||||
.collect();
|
||||
Ok(SimpleSendableEmail::new_with_envelope(
|
||||
Envelope::new(Some(EmailAddress::new(from_address)?), to?)?,
|
||||
message_id,
|
||||
message,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns a new email from a valid envelope
|
||||
pub fn new_with_envelope(
|
||||
envelope: Envelope,
|
||||
message_id: String,
|
||||
message: String,
|
||||
) -> SimpleSendableEmail {
|
||||
SimpleSendableEmail {
|
||||
envelope,
|
||||
message_id,
|
||||
message: message.into_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SendableEmail<'a, &'a [u8]> for SimpleSendableEmail {
|
||||
fn envelope(&self) -> Envelope {
|
||||
self.envelope.clone()
|
||||
}
|
||||
|
||||
fn message_id(&self) -> String {
|
||||
self.message_id.clone()
|
||||
}
|
||||
|
||||
fn message(&'a self) -> Box<&[u8]> {
|
||||
Box::new(self.message.as_slice())
|
||||
}
|
||||
fn send(&mut self, email: SendableEmail) -> Self::Result;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! The sendmail transport sends the email using the local sendmail command.
|
||||
//!
|
||||
|
||||
use {EmailTransport, SendableEmail};
|
||||
use Transport;
|
||||
use SendableEmail;
|
||||
use sendmail::error::SendmailResult;
|
||||
use std::io::Read;
|
||||
use std::io::prelude::*;
|
||||
@@ -32,19 +33,19 @@ impl SendmailTransport {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read + 'a> EmailTransport<'a, T> for SendmailTransport {
|
||||
impl<'a> Transport<'a> for SendmailTransport {
|
||||
type Result = SendmailResult;
|
||||
|
||||
fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SendmailResult {
|
||||
let envelope = email.envelope();
|
||||
fn send(&mut self, email: SendableEmail) -> SendmailResult {
|
||||
let message_id = email.message_id().to_string();
|
||||
|
||||
// Spawn the sendmail command
|
||||
let to_addresses: Vec<String> = envelope.to().iter().map(|x| x.to_string()).collect();
|
||||
let to_addresses: Vec<String> = email.envelope.to().iter().map(|x| x.to_string()).collect();
|
||||
let mut process = Command::new(&self.command)
|
||||
.args(&[
|
||||
"-i",
|
||||
"-f",
|
||||
&match envelope.from() {
|
||||
&match email.envelope().from() {
|
||||
Some(address) => address.to_string(),
|
||||
None => "\"\"".to_string(),
|
||||
},
|
||||
@@ -67,7 +68,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SendmailTransport {
|
||||
Err(error) => return Err(From::from(error)),
|
||||
}
|
||||
|
||||
info!("Wrote message to stdin");
|
||||
info!("Wrote {} message to stdin", message_id);
|
||||
|
||||
if let Ok(output) = process.wait_with_output() {
|
||||
if output.status.success() {
|
||||
|
||||
@@ -194,10 +194,11 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
|
||||
}
|
||||
|
||||
/// Sends the message content
|
||||
pub fn message<T: Read>(&mut self, mut message: Box<T>) -> SmtpResult {
|
||||
pub fn message(&mut self, message: Box<Read>) -> SmtpResult {
|
||||
let mut out_buf: Vec<u8> = vec![];
|
||||
let mut codec = ClientCodec::new();
|
||||
let mut message_reader = BufReader::new(message.as_mut());
|
||||
|
||||
let mut message_reader = BufReader::new(message);
|
||||
|
||||
loop {
|
||||
out_buf.clear();
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
//! * SMTPUTF8 ([RFC 6531](http://tools.ietf.org/html/rfc6531))
|
||||
//!
|
||||
|
||||
use EmailTransport;
|
||||
use SendableEmail;
|
||||
use {SendableEmail, Transport};
|
||||
use native_tls::TlsConnector;
|
||||
use smtp::authentication::{Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS,
|
||||
DEFAULT_UNENCRYPTED_MECHANISMS};
|
||||
@@ -25,7 +24,6 @@ use smtp::client::net::DEFAULT_TLS_PROTOCOLS;
|
||||
use smtp::commands::*;
|
||||
use smtp::error::{Error, SmtpResult};
|
||||
use smtp::extension::{ClientId, Extension, MailBodyParameter, MailParameter, ServerInfo};
|
||||
use std::io::Read;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -306,15 +304,13 @@ impl<'a> SmtpTransport {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport {
|
||||
impl<'a> Transport<'a> for SmtpTransport {
|
||||
type Result = SmtpResult;
|
||||
|
||||
/// Sends an email
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))]
|
||||
fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SmtpResult {
|
||||
// Extract email information
|
||||
let message_id = email.message_id();
|
||||
let envelope = email.envelope();
|
||||
fn send(&mut self, email: SendableEmail) -> SmtpResult {
|
||||
let message_id = email.message_id().to_string();
|
||||
|
||||
// Check if the connection is still available
|
||||
if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) {
|
||||
@@ -419,8 +415,10 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport {
|
||||
}
|
||||
|
||||
try_smtp!(
|
||||
self.client
|
||||
.command(MailCommand::new(envelope.from().cloned(), mail_options,)),
|
||||
self.client.command(MailCommand::new(
|
||||
email.envelope().from().cloned(),
|
||||
mail_options,
|
||||
)),
|
||||
self
|
||||
);
|
||||
|
||||
@@ -428,14 +426,14 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport {
|
||||
info!(
|
||||
"{}: from=<{}>",
|
||||
message_id,
|
||||
match envelope.from() {
|
||||
match email.envelope().from() {
|
||||
Some(address) => address.to_string(),
|
||||
None => "".to_string(),
|
||||
}
|
||||
);
|
||||
|
||||
// Recipient
|
||||
for to_address in envelope.to() {
|
||||
for to_address in email.envelope().to() {
|
||||
try_smtp!(
|
||||
self.client
|
||||
.command(RcptCommand::new(to_address.clone(), vec![]),),
|
||||
@@ -449,7 +447,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport {
|
||||
try_smtp!(self.client.command(DataCommand), self);
|
||||
|
||||
// Message content
|
||||
let result = self.client.message(email.message());
|
||||
let result = self.client.message(Box::new(email.message()));
|
||||
|
||||
if result.is_ok() {
|
||||
// Increment the connection reuse counter
|
||||
|
||||
@@ -2,44 +2,42 @@
|
||||
//! testing purposes.
|
||||
//!
|
||||
|
||||
use EmailTransport;
|
||||
use Transport;
|
||||
use SendableEmail;
|
||||
use std::io::Read;
|
||||
|
||||
/// This transport logs the message envelope and returns the given response
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct StubEmailTransport {
|
||||
pub struct StubTransport {
|
||||
response: StubResult,
|
||||
}
|
||||
|
||||
impl StubEmailTransport {
|
||||
impl StubTransport {
|
||||
/// Creates a new transport that always returns the given response
|
||||
pub fn new(response: StubResult) -> StubEmailTransport {
|
||||
StubEmailTransport { response }
|
||||
pub fn new(response: StubResult) -> StubTransport {
|
||||
StubTransport { response }
|
||||
}
|
||||
|
||||
/// Creates a new transport that always returns a success response
|
||||
pub fn new_positive() -> StubEmailTransport {
|
||||
StubEmailTransport { response: Ok(()) }
|
||||
pub fn new_positive() -> StubTransport {
|
||||
StubTransport { response: Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// SMTP result type
|
||||
pub type StubResult = Result<(), ()>;
|
||||
|
||||
impl<'a, T: Read + 'a> EmailTransport<'a, T> for StubEmailTransport {
|
||||
impl<'a> Transport<'a> for StubTransport {
|
||||
type Result = StubResult;
|
||||
|
||||
fn send<U: SendableEmail<'a, T>>(&mut self, email: &'a U) -> StubResult {
|
||||
let envelope = email.envelope();
|
||||
fn send(&mut self, email: SendableEmail) -> StubResult {
|
||||
info!(
|
||||
"{}: from=<{}> to=<{:?}>",
|
||||
email.message_id(),
|
||||
match envelope.from() {
|
||||
match email.envelope().from() {
|
||||
Some(address) => address.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
envelope.to()
|
||||
email.envelope().to()
|
||||
);
|
||||
self.response
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ extern crate lettre;
|
||||
#[cfg(feature = "file-transport")]
|
||||
mod test {
|
||||
|
||||
use lettre::{EmailTransport, SendableEmail, SimpleSendableEmail};
|
||||
use lettre::file::FileEmailTransport;
|
||||
use lettre::{EmailAddress, Envelope, SendableEmail, Transport};
|
||||
use lettre::file::FileTransport;
|
||||
use std::env::temp_dir;
|
||||
use std::fs::File;
|
||||
use std::fs::remove_file;
|
||||
@@ -13,25 +13,28 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn file_transport() {
|
||||
let mut sender = FileEmailTransport::new(temp_dir());
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"file_id".to_string(),
|
||||
"Hello file".to_string(),
|
||||
).unwrap();
|
||||
let result = sender.send(&email);
|
||||
let mut sender = FileTransport::new(temp_dir());
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
let message_id = email.message_id().to_string();
|
||||
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let message_id = email.message_id();
|
||||
let file = format!("{}/{}.txt", temp_dir().to_str().unwrap(), message_id);
|
||||
let file = format!("{}/{}.json", temp_dir().to_str().unwrap(), message_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\":[\"root@localhost\"],\"reverse_path\":\"user@localhost\"},\"message_id\":\"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}"
|
||||
"{\"envelope\":{\"forward_path\":[\"root@localhost\"],\"reverse_path\":\"user@localhost\"},\"message_id\":\"id\",\"message\":[72,101,108,108,111,32,195,159,226,152,186,32,101,120,97,109,112,108,101]}"
|
||||
);
|
||||
|
||||
remove_file(file).unwrap();
|
||||
|
||||
@@ -3,21 +3,22 @@ extern crate lettre;
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "sendmail-transport")]
|
||||
mod test {
|
||||
|
||||
use lettre::{EmailTransport, SimpleSendableEmail};
|
||||
use lettre::{EmailAddress, Envelope, SendableEmail, Transport};
|
||||
use lettre::sendmail::SendmailTransport;
|
||||
|
||||
#[test]
|
||||
fn sendmail_transport_simple() {
|
||||
let mut sender = SendmailTransport::new();
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"sendmail_id".to_string(),
|
||||
"Hello sendmail".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
let result = sender.send(&email);
|
||||
let result = sender.send(email);
|
||||
println!("{:?}", result);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
@@ -3,22 +3,23 @@ extern crate lettre;
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "smtp-transport")]
|
||||
mod test {
|
||||
|
||||
use lettre::{ClientSecurity, EmailTransport, SimpleSendableEmail, SmtpTransport};
|
||||
use lettre::{ClientSecurity, EmailAddress, Envelope, SendableEmail, SmtpTransport, Transport};
|
||||
|
||||
#[test]
|
||||
fn smtp_transport_simple() {
|
||||
let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None)
|
||||
.unwrap()
|
||||
.build();
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"smtp_id".to_string(),
|
||||
"Hello smtp".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
sender.send(&email).unwrap();
|
||||
sender.send(email).unwrap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::{EmailTransport, SimpleSendableEmail};
|
||||
use lettre::stub::StubEmailTransport;
|
||||
use lettre::{EmailAddress, Envelope, SendableEmail, Transport};
|
||||
use lettre::stub::StubTransport;
|
||||
|
||||
#[test]
|
||||
fn stub_transport() {
|
||||
let mut sender_ok = StubEmailTransport::new_positive();
|
||||
let mut sender_ko = StubEmailTransport::new(Err(()));
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"stub_id".to_string(),
|
||||
"Hello stub".to_string(),
|
||||
).unwrap();
|
||||
let mut sender_ok = StubTransport::new_positive();
|
||||
let mut sender_ko = StubTransport::new(Err(()));
|
||||
let email_ok = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
let email_ko = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello ß☺ example".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
sender_ok.send(&email).unwrap();
|
||||
sender_ko.send(&email).unwrap_err();
|
||||
sender_ok.send(email_ok).unwrap();
|
||||
sender_ko.send(email_ko).unwrap_err();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ lettre = { version = "^0.9", path = "../lettre", features = ["smtp-transport"] }
|
||||
glob = "0.2"
|
||||
|
||||
[dependencies]
|
||||
email = "^0.0"
|
||||
email = { git = "https://github.com/lettre/rust-email" }
|
||||
mime = "^0.3"
|
||||
time = "^0.1"
|
||||
uuid = { version = "^0.6", features = ["v4"] }
|
||||
|
||||
@@ -2,12 +2,12 @@ extern crate lettre;
|
||||
extern crate lettre_email;
|
||||
extern crate mime;
|
||||
|
||||
use lettre::{EmailTransport, SmtpTransport};
|
||||
use lettre_email::EmailBuilder;
|
||||
use lettre::{SmtpTransport, Transport};
|
||||
use lettre_email::Email;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let email = EmailBuilder::new()
|
||||
let email = Email::builder()
|
||||
// Addresses can be specified by the tuple (email, alias)
|
||||
.to(("user@example.org", "Firstname Lastname"))
|
||||
// ... or by an address only
|
||||
@@ -23,7 +23,7 @@ fn main() {
|
||||
.unwrap()
|
||||
.build();
|
||||
// Send the email
|
||||
let result = mailer.send(&email);
|
||||
let result = mailer.send(email.into());
|
||||
|
||||
if result.is_ok() {
|
||||
println!("Email sent");
|
||||
|
||||
@@ -26,242 +26,6 @@ use time::{now, Tm};
|
||||
use uuid::Uuid;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Converts an address or an address with an alias to a `Header`
|
||||
pub trait IntoHeader {
|
||||
/// Converts to a `Header` struct
|
||||
fn into_header(self) -> Header;
|
||||
}
|
||||
|
||||
impl IntoHeader for Header {
|
||||
fn into_header(self) -> Header {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Into<String>, T: Into<String>> IntoHeader for (S, T) {
|
||||
fn into_header(self) -> Header {
|
||||
let (name, value) = self;
|
||||
Header::new(name.into(), value.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an address or an address with an alias to a `Mailbox`
|
||||
pub trait IntoMailbox {
|
||||
/// Converts to a `Mailbox` struct
|
||||
fn into_mailbox(self) -> Mailbox;
|
||||
}
|
||||
|
||||
impl IntoMailbox for Mailbox {
|
||||
fn into_mailbox(self) -> Mailbox {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoMailbox for &'a str {
|
||||
fn into_mailbox(self) -> Mailbox {
|
||||
Mailbox::new(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoMailbox for String {
|
||||
fn into_mailbox(self) -> Mailbox {
|
||||
Mailbox::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Into<String>, T: Into<String>> IntoMailbox for (S, T) {
|
||||
fn into_mailbox(self) -> Mailbox {
|
||||
let (address, alias) = self;
|
||||
Mailbox::new_with_name(alias.into(), address.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Can be transformed to a sendable email
|
||||
pub trait IntoEmail {
|
||||
/// Builds an email
|
||||
fn into_email(self) -> Result<Email, Error>;
|
||||
}
|
||||
|
||||
impl IntoEmail for SimpleEmail {
|
||||
fn into_email(self) -> Result<Email, Error> {
|
||||
let mut builder = EmailBuilder::new();
|
||||
|
||||
if self.from.is_some() {
|
||||
builder.add_from(self.from.unwrap());
|
||||
}
|
||||
|
||||
for to_address in self.to {
|
||||
builder.add_to(to_address.into_mailbox());
|
||||
}
|
||||
|
||||
for cc_address in self.cc {
|
||||
builder.add_cc(cc_address.into_mailbox());
|
||||
}
|
||||
|
||||
if self.reply_to.is_some() {
|
||||
builder.add_reply_to(self.reply_to.unwrap().into_mailbox());
|
||||
}
|
||||
|
||||
if self.subject.is_some() {
|
||||
builder.set_subject(self.subject.unwrap());
|
||||
}
|
||||
|
||||
// No date for now
|
||||
|
||||
match (self.text, self.html) {
|
||||
(Some(text), Some(html)) => builder.set_alternative(html, text),
|
||||
(Some(text), None) => builder.set_text(text),
|
||||
(None, Some(html)) => builder.set_html(html),
|
||||
(None, None) => (),
|
||||
}
|
||||
|
||||
for header in self.headers {
|
||||
builder.add_header(header.into_header());
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple representation of an email, useful for some transports
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Default)]
|
||||
pub struct SimpleEmail {
|
||||
from: Option<Mailbox>,
|
||||
to: Vec<Mailbox>,
|
||||
cc: Vec<Mailbox>,
|
||||
bcc: Vec<Mailbox>,
|
||||
reply_to: Option<Mailbox>,
|
||||
subject: Option<String>,
|
||||
date: Option<Tm>,
|
||||
html: Option<String>,
|
||||
text: Option<String>,
|
||||
attachments: Vec<String>,
|
||||
headers: Vec<Header>,
|
||||
}
|
||||
|
||||
impl SimpleEmail {
|
||||
/// Adds a generic header
|
||||
pub fn header<A: IntoHeader>(mut self, header: A) -> SimpleEmail {
|
||||
self.add_header(header);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a generic header
|
||||
pub fn add_header<A: IntoHeader>(&mut self, header: A) {
|
||||
self.headers.push(header.into_header());
|
||||
}
|
||||
|
||||
/// Adds a `From` header and stores the sender address
|
||||
pub fn from<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||
self.add_from(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `From` header and stores the sender address
|
||||
pub fn add_from<A: IntoMailbox>(&mut self, address: A) {
|
||||
self.from = Some(address.into_mailbox());
|
||||
}
|
||||
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
pub fn to<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||
self.add_to(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
pub fn add_to<A: IntoMailbox>(&mut self, address: A) {
|
||||
self.to.push(address.into_mailbox());
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
pub fn cc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||
self.add_cc(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
pub fn add_cc<A: IntoMailbox>(&mut self, address: A) {
|
||||
self.cc.push(address.into_mailbox());
|
||||
}
|
||||
|
||||
/// Adds a `Bcc` header and stores the recipient address
|
||||
pub fn bcc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||
self.add_bcc(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Bcc` header and stores the recipient address
|
||||
pub fn add_bcc<A: IntoMailbox>(&mut self, address: A) {
|
||||
self.bcc.push(address.into_mailbox());
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn reply_to<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||
self.add_reply_to(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn add_reply_to<A: IntoMailbox>(&mut self, address: A) {
|
||||
self.reply_to = Some(address.into_mailbox());
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
pub fn subject<S: Into<String>>(mut self, subject: S) -> SimpleEmail {
|
||||
self.set_subject(subject);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
pub fn set_subject<S: Into<String>>(&mut self, subject: S) {
|
||||
self.subject = Some(subject.into());
|
||||
}
|
||||
|
||||
/// Adds a `Date` header with the given date
|
||||
pub fn date(mut self, date: Tm) -> SimpleEmail {
|
||||
self.set_date(date);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Date` header with the given date
|
||||
pub fn set_date(&mut self, date: Tm) {
|
||||
self.date = Some(date);
|
||||
}
|
||||
|
||||
/// Adds an attachment to the message
|
||||
pub fn attachment<S: Into<String>>(mut self, path: S) -> SimpleEmail {
|
||||
self.add_attachment(path);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an attachment to the message
|
||||
pub fn add_attachment<S: Into<String>>(&mut self, path: S) {
|
||||
self.attachments.push(path.into());
|
||||
}
|
||||
|
||||
/// Sets the email body to plain text content
|
||||
pub fn text<S: Into<String>>(mut self, body: S) -> SimpleEmail {
|
||||
self.set_text(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email body to plain text content
|
||||
pub fn set_text<S: Into<String>>(&mut self, body: S) {
|
||||
self.text = Some(body.into());
|
||||
}
|
||||
|
||||
/// Sets the email body to HTML content
|
||||
pub fn html<S: Into<String>>(mut self, body: S) -> SimpleEmail {
|
||||
self.set_html(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email body to HTML content
|
||||
pub fn set_html<S: Into<String>>(&mut self, body: S) {
|
||||
self.html = Some(body.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `MimeMessage` structure
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct PartBuilder {
|
||||
@@ -281,17 +45,17 @@ pub struct EmailBuilder {
|
||||
/// Message
|
||||
message: PartBuilder,
|
||||
/// The recipients' addresses for the mail header
|
||||
to_header: Vec<Address>,
|
||||
to: Vec<Address>,
|
||||
/// The sender addresses for the mail header
|
||||
from_header: Vec<Address>,
|
||||
from: Vec<Address>,
|
||||
/// The Cc addresses for the mail header
|
||||
cc_header: Vec<Address>,
|
||||
cc: Vec<Address>,
|
||||
/// The Bcc addresses for the mail header
|
||||
bcc_header: Vec<Address>,
|
||||
bcc: Vec<Address>,
|
||||
/// The Reply-To addresses for the mail header
|
||||
reply_to_header: Vec<Address>,
|
||||
reply_to: Vec<Address>,
|
||||
/// The sender address for the mail header
|
||||
sender_header: Option<Mailbox>,
|
||||
sender: Option<Mailbox>,
|
||||
/// The envelope
|
||||
envelope: Option<Envelope>,
|
||||
/// Date issued
|
||||
@@ -309,6 +73,23 @@ pub struct Email {
|
||||
message_id: Uuid,
|
||||
}
|
||||
|
||||
impl Into<SendableEmail> for Email {
|
||||
fn into(self) -> SendableEmail {
|
||||
SendableEmail::new(
|
||||
self.envelope.clone(),
|
||||
self.message_id.to_string(),
|
||||
self.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Email {
|
||||
/// TODO
|
||||
pub fn builder() -> EmailBuilder {
|
||||
EmailBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartBuilder {
|
||||
/// Creates a new empty part
|
||||
pub fn new() -> PartBuilder {
|
||||
@@ -318,58 +99,32 @@ impl PartBuilder {
|
||||
}
|
||||
|
||||
/// Adds a generic header
|
||||
pub fn header<A: IntoHeader>(mut self, header: A) -> PartBuilder {
|
||||
self.add_header(header);
|
||||
pub fn header<A: Into<Header>>(mut self, header: A) -> PartBuilder {
|
||||
self.message.headers.insert(header.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a generic header
|
||||
pub fn add_header<A: IntoHeader>(&mut self, header: A) {
|
||||
self.message.headers.insert(header.into_header());
|
||||
}
|
||||
|
||||
/// Sets the body
|
||||
pub fn body<S: Into<String>>(mut self, body: S) -> PartBuilder {
|
||||
self.set_body(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the body
|
||||
pub fn set_body<S: Into<String>>(&mut self, body: S) {
|
||||
self.message.body = body.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines a `MimeMultipartType` value
|
||||
pub fn message_type(mut self, mime_type: MimeMultipartType) -> PartBuilder {
|
||||
self.set_message_type(mime_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines a `MimeMultipartType` value
|
||||
pub fn set_message_type(&mut self, mime_type: MimeMultipartType) {
|
||||
self.message.message_type = Some(mime_type);
|
||||
}
|
||||
|
||||
/// Adds a `ContentType` header with the given MIME type
|
||||
pub fn content_type(mut self, content_type: &Mime) -> PartBuilder {
|
||||
self.set_content_type(content_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `ContentType` header with the given MIME type
|
||||
pub fn set_content_type(&mut self, content_type: &Mime) {
|
||||
self.add_header(("Content-Type", format!("{}", content_type).as_ref()));
|
||||
pub fn content_type(self, content_type: &Mime) -> PartBuilder {
|
||||
self.header(("Content-Type", format!("{}", content_type).as_ref()))
|
||||
}
|
||||
|
||||
/// Adds a child part
|
||||
pub fn child(mut self, child: MimeMessage) -> PartBuilder {
|
||||
self.add_child(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a child part
|
||||
pub fn add_child(&mut self, child: MimeMessage) {
|
||||
self.message.children.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets built `MimeMessage`
|
||||
@@ -384,12 +139,12 @@ impl EmailBuilder {
|
||||
pub fn new() -> EmailBuilder {
|
||||
EmailBuilder {
|
||||
message: PartBuilder::new(),
|
||||
to_header: vec![],
|
||||
from_header: vec![],
|
||||
cc_header: vec![],
|
||||
bcc_header: vec![],
|
||||
reply_to_header: vec![],
|
||||
sender_header: None,
|
||||
to: vec![],
|
||||
from: vec![],
|
||||
cc: vec![],
|
||||
bcc: vec![],
|
||||
reply_to: vec![],
|
||||
sender: None,
|
||||
envelope: None,
|
||||
date_issued: false,
|
||||
}
|
||||
@@ -397,142 +152,78 @@ impl EmailBuilder {
|
||||
|
||||
/// Sets the email body
|
||||
pub fn body<S: Into<String>>(mut self, body: S) -> EmailBuilder {
|
||||
self.message.set_body(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email body
|
||||
pub fn set_body<S: Into<String>>(&mut self, body: S) {
|
||||
self.message.set_body(body);
|
||||
}
|
||||
|
||||
/// Add a generic header
|
||||
pub fn header<A: IntoHeader>(mut self, header: A) -> EmailBuilder {
|
||||
self.message.add_header(header);
|
||||
self.message = self.message.body(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a generic header
|
||||
pub fn add_header<A: IntoHeader>(&mut self, header: A) {
|
||||
self.message.add_header(header);
|
||||
}
|
||||
|
||||
/// Adds a `From` header and stores the sender address
|
||||
pub fn from<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.add_from(address);
|
||||
pub fn header<A: Into<Header>>(mut self, header: A) -> EmailBuilder {
|
||||
self.message = self.message.header(header);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `From` header and stores the sender address
|
||||
pub fn add_from<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.from_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
pub fn to<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.add_to(address);
|
||||
pub fn from<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.from.push(Address::Mailbox(mailbox));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
pub fn add_to<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.to_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
pub fn cc<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.add_cc(address);
|
||||
pub fn to<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.to.push(Address::Mailbox(mailbox));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
pub fn add_cc<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.cc_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Bcc` header and stores the recipient address
|
||||
pub fn bcc<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.add_bcc(address);
|
||||
pub fn cc<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.cc.push(Address::Mailbox(mailbox));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Bcc` header and stores the recipient address
|
||||
pub fn add_bcc<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.bcc_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn reply_to<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.add_reply_to(address);
|
||||
pub fn bcc<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.bcc.push(Address::Mailbox(mailbox));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn add_reply_to<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.reply_to_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Sender` header
|
||||
pub fn sender<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||
self.set_sender(address);
|
||||
pub fn reply_to<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.reply_to.push(Address::Mailbox(mailbox));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Sender` header
|
||||
pub fn set_sender<A: IntoMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.into_mailbox();
|
||||
self.sender_header = Some(mailbox);
|
||||
pub fn sender<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
|
||||
let mailbox = address.into();
|
||||
self.sender = Some(mailbox);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
pub fn subject<S: Into<String>>(mut self, subject: S) -> EmailBuilder {
|
||||
self.set_subject(subject);
|
||||
self.message = self.message.header(("Subject".to_string(), subject.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
pub fn set_subject<S: Into<String>>(&mut self, subject: S) {
|
||||
self.message
|
||||
.add_header(("Subject".to_string(), subject.into()));
|
||||
}
|
||||
|
||||
/// Adds a `Date` header with the given date
|
||||
pub fn date(mut self, date: &Tm) -> EmailBuilder {
|
||||
self.set_date(date);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `Date` header with the given date
|
||||
pub fn set_date(&mut self, date: &Tm) {
|
||||
self.message
|
||||
.add_header(("Date", Tm::rfc822z(date).to_string()));
|
||||
self.message = self.message.header(("Date", Tm::rfc822z(date).to_string()));
|
||||
self.date_issued = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an attachment to the email
|
||||
pub fn attachment(
|
||||
mut self,
|
||||
self,
|
||||
path: &Path,
|
||||
filename: Option<&str>,
|
||||
content_type: &Mime,
|
||||
) -> Result<EmailBuilder, Error> {
|
||||
self.set_attachment(path, filename, content_type)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Adds an attachment to the email
|
||||
/// If filename is not provided, the name of the file will be used.
|
||||
pub fn set_attachment(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
filename: Option<&str>,
|
||||
content_type: &Mime,
|
||||
) -> Result<(), Error> {
|
||||
let file = File::open(path);
|
||||
let body = match file {
|
||||
Ok(mut f) => {
|
||||
@@ -572,42 +263,23 @@ impl EmailBuilder {
|
||||
.header(("Content-Transfer-Encoding", "base64"))
|
||||
.build();
|
||||
|
||||
self.set_message_type(MimeMultipartType::Mixed);
|
||||
self.add_child(content);
|
||||
|
||||
Ok(())
|
||||
Ok(self.message_type(MimeMultipartType::Mixed).child(content))
|
||||
}
|
||||
|
||||
/// Set the message type
|
||||
pub fn message_type(mut self, message_type: MimeMultipartType) -> EmailBuilder {
|
||||
self.set_message_type(message_type);
|
||||
self.message = self.message.message_type(message_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the message type
|
||||
pub fn set_message_type(&mut self, message_type: MimeMultipartType) {
|
||||
self.message.set_message_type(message_type);
|
||||
}
|
||||
|
||||
/// Adds a child
|
||||
pub fn child(mut self, child: MimeMessage) -> EmailBuilder {
|
||||
self.add_child(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a child
|
||||
pub fn add_child(&mut self, child: MimeMessage) {
|
||||
self.message.add_child(child);
|
||||
}
|
||||
|
||||
/// Sets the email body to plain text content
|
||||
pub fn text<S: Into<String>>(mut self, body: S) -> EmailBuilder {
|
||||
self.set_text(body);
|
||||
self.message = self.message.child(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email body to plain text content
|
||||
pub fn set_text<S: Into<String>>(&mut self, body: S) {
|
||||
pub fn text<S: Into<String>>(self, body: S) -> EmailBuilder {
|
||||
let text = PartBuilder::new()
|
||||
.body(body)
|
||||
.header((
|
||||
@@ -615,17 +287,11 @@ impl EmailBuilder {
|
||||
format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(),
|
||||
))
|
||||
.build();
|
||||
self.add_child(text);
|
||||
self.child(text)
|
||||
}
|
||||
|
||||
/// Sets the email body to HTML content
|
||||
pub fn html<S: Into<String>>(mut self, body: S) -> EmailBuilder {
|
||||
self.set_html(body);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email body to HTML content
|
||||
pub fn set_html<S: Into<String>>(&mut self, body: S) {
|
||||
pub fn html<S: Into<String>>(self, body: S) -> EmailBuilder {
|
||||
let html = PartBuilder::new()
|
||||
.body(body)
|
||||
.header((
|
||||
@@ -633,28 +299,15 @@ impl EmailBuilder {
|
||||
format!("{}", mime::TEXT_HTML_UTF_8).as_ref(),
|
||||
))
|
||||
.build();
|
||||
self.add_child(html);
|
||||
self.child(html)
|
||||
}
|
||||
|
||||
/// Sets the email content
|
||||
pub fn alternative<S: Into<String>, T: Into<String>>(
|
||||
mut self,
|
||||
self,
|
||||
body_html: S,
|
||||
body_text: T,
|
||||
) -> EmailBuilder {
|
||||
self.set_alternative(body_html, body_text);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the email content
|
||||
pub fn set_alternative<S: Into<String>, T: Into<String>>(
|
||||
&mut self,
|
||||
body_html: S,
|
||||
body_text: T,
|
||||
) {
|
||||
let mut alternate = PartBuilder::new();
|
||||
alternate.set_message_type(MimeMultipartType::Alternative);
|
||||
|
||||
let text = PartBuilder::new()
|
||||
.body(body_text)
|
||||
.header((
|
||||
@@ -671,74 +324,65 @@ impl EmailBuilder {
|
||||
))
|
||||
.build();
|
||||
|
||||
alternate.add_child(text);
|
||||
alternate.add_child(html);
|
||||
let alternate = PartBuilder::new()
|
||||
.message_type(MimeMultipartType::Alternative)
|
||||
.child(text)
|
||||
.child(html);
|
||||
|
||||
self.set_message_type(MimeMultipartType::Mixed);
|
||||
self.add_child(alternate.build());
|
||||
self.message_type(MimeMultipartType::Mixed)
|
||||
.child(alternate.build())
|
||||
}
|
||||
|
||||
/// Sets the envelope for manual destination control
|
||||
/// If this function is not called, the envelope will be calculated
|
||||
/// from the "to" and "cc" addresses you set.
|
||||
pub fn envelope(mut self, envelope: Envelope) -> EmailBuilder {
|
||||
self.set_envelope(envelope);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the envelope for manual destination control
|
||||
/// If this function is not called, the envelope will be calculated
|
||||
/// from the "to" and "cc" addresses you set.
|
||||
pub fn set_envelope(&mut self, envelope: Envelope) {
|
||||
self.envelope = Some(envelope);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the Email
|
||||
pub fn build(mut self) -> Result<Email, Error> {
|
||||
// If there are multiple addresses in "From", the "Sender" is required.
|
||||
if self.from_header.len() >= 2 && self.sender_header.is_none() {
|
||||
if self.from.len() >= 2 && self.sender.is_none() {
|
||||
// So, we must find something to put as Sender.
|
||||
for possible_sender in &self.from_header {
|
||||
for possible_sender in &self.from {
|
||||
// Only a mailbox can be used as sender, not Address::Group.
|
||||
if let Address::Mailbox(ref mbx) = *possible_sender {
|
||||
self.sender_header = Some(mbx.clone());
|
||||
self.sender = Some(mbx.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Address::Group is not yet supported, so the line below will never panic.
|
||||
// If groups are supported one day, add another Error for this case
|
||||
// and return it here, if sender_header is still None at this point.
|
||||
assert!(self.sender_header.is_some());
|
||||
assert!(self.sender.is_some());
|
||||
}
|
||||
// Add the sender header, if any.
|
||||
if let Some(ref v) = self.sender_header {
|
||||
self.message.add_header(("Sender", v.to_string().as_ref()));
|
||||
if let Some(ref v) = self.sender {
|
||||
self.message = self.message.header(("Sender", v.to_string().as_ref()));
|
||||
}
|
||||
// Calculate the envelope
|
||||
let envelope = match self.envelope {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
// we need to generate the envelope
|
||||
let mut e = Envelope::builder();
|
||||
let mut to = vec![];
|
||||
// add all receivers in to_header and cc_header
|
||||
for receiver in self.to_header
|
||||
.iter()
|
||||
.chain(self.cc_header.iter())
|
||||
.chain(self.bcc_header.iter())
|
||||
{
|
||||
for receiver in self.to.iter().chain(self.cc.iter()).chain(self.bcc.iter()) {
|
||||
match *receiver {
|
||||
Address::Mailbox(ref m) => e.add_to(EmailAddress::from_str(&m.address)?),
|
||||
Address::Mailbox(ref m) => to.push(EmailAddress::from_str(&m.address)?),
|
||||
Address::Group(_, ref ms) => for m in ms.iter() {
|
||||
e.add_to(EmailAddress::from_str(&m.address.clone())?);
|
||||
to.push(EmailAddress::from_str(&m.address.clone())?);
|
||||
},
|
||||
}
|
||||
}
|
||||
e.set_from(EmailAddress::from_str(&match self.sender_header {
|
||||
let from = Some(EmailAddress::from_str(&match self.sender {
|
||||
Some(x) => x.address.clone(), // if we have a sender_header, use it
|
||||
None => {
|
||||
// use a from header
|
||||
debug_assert!(self.from_header.len() <= 1); // else we'd have sender_header
|
||||
match self.from_header.first() {
|
||||
debug_assert!(self.from.len() <= 1); // else we'd have sender_header
|
||||
match self.from.first() {
|
||||
Some(a) => match *a {
|
||||
// if we have a from header
|
||||
Address::Mailbox(ref mailbox) => mailbox.address.clone(), // use it
|
||||
@@ -754,41 +398,40 @@ impl EmailBuilder {
|
||||
}
|
||||
}
|
||||
})?);
|
||||
e.build()?
|
||||
Envelope::new(from, to)?
|
||||
}
|
||||
};
|
||||
// Add the collected addresses as mailbox-list all at once.
|
||||
// The unwraps are fine because the conversions for Vec<Address> never errs.
|
||||
if !self.to_header.is_empty() {
|
||||
self.message
|
||||
.add_header(Header::new_with_value("To".into(), self.to_header).unwrap());
|
||||
if !self.to.is_empty() {
|
||||
self.message = self.message
|
||||
.header(Header::new_with_value("To".into(), self.to).unwrap());
|
||||
}
|
||||
if !self.from_header.is_empty() {
|
||||
self.message
|
||||
.add_header(Header::new_with_value("From".into(), self.from_header).unwrap());
|
||||
if !self.from.is_empty() {
|
||||
self.message = self.message
|
||||
.header(Header::new_with_value("From".into(), self.from).unwrap());
|
||||
} else {
|
||||
return Err(Error::Email(EmailError::MissingFrom));
|
||||
}
|
||||
if !self.cc_header.is_empty() {
|
||||
self.message
|
||||
.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());
|
||||
if !self.cc.is_empty() {
|
||||
self.message = self.message
|
||||
.header(Header::new_with_value("Cc".into(), self.cc).unwrap());
|
||||
}
|
||||
if !self.bcc_header.is_empty() {
|
||||
self.message
|
||||
.add_header(Header::new_with_value("Bcc".into(), self.bcc_header).unwrap());
|
||||
if !self.bcc.is_empty() {
|
||||
self.message = self.message
|
||||
.header(Header::new_with_value("Bcc".into(), self.bcc).unwrap());
|
||||
}
|
||||
if !self.reply_to_header.is_empty() {
|
||||
self.message.add_header(
|
||||
Header::new_with_value("Reply-To".into(), self.reply_to_header).unwrap(),
|
||||
);
|
||||
if !self.reply_to.is_empty() {
|
||||
self.message = self.message
|
||||
.header(Header::new_with_value("Reply-To".into(), self.reply_to).unwrap());
|
||||
}
|
||||
|
||||
if !self.date_issued {
|
||||
self.message
|
||||
.add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
|
||||
self.message = self.message
|
||||
.header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
|
||||
}
|
||||
|
||||
self.message.add_header(("MIME-Version", "1.0"));
|
||||
self.message = self.message.header(("MIME-Version", "1.0"));
|
||||
|
||||
let message_id = Uuid::new_v4();
|
||||
|
||||
@@ -796,7 +439,7 @@ impl EmailBuilder {
|
||||
"Message-ID".to_string(),
|
||||
format!("<{}.lettre@localhost>", message_id),
|
||||
) {
|
||||
self.message.add_header(header)
|
||||
self.message = self.message.header(header)
|
||||
}
|
||||
|
||||
Ok(Email {
|
||||
@@ -807,55 +450,17 @@ impl EmailBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SendableEmail<'a, &'a [u8]> for Email {
|
||||
fn envelope(&self) -> Envelope {
|
||||
self.envelope.clone()
|
||||
}
|
||||
|
||||
fn message_id(&self) -> String {
|
||||
self.message_id.to_string()
|
||||
}
|
||||
|
||||
fn message(&'a self) -> Box<&[u8]> {
|
||||
Box::new(self.message.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// Email sendable by any type of client, giving access to all fields
|
||||
pub trait ExtractableEmail {
|
||||
/// From address
|
||||
fn from_address(&self) -> Option<String>;
|
||||
/// To addresses
|
||||
fn to_addresses(&self) -> Vec<String>;
|
||||
/// Cc addresses
|
||||
fn cc_addresses(&self) -> Vec<String>;
|
||||
/// Bcc addresses
|
||||
fn bcc_addresses(&self) -> Vec<String>;
|
||||
/// Replay-To addresses
|
||||
fn reply_to_address(&self) -> String;
|
||||
/// Subject
|
||||
fn subject(&self) -> String;
|
||||
/// Message ID
|
||||
fn message_id(&self) -> String;
|
||||
/// Other Headers
|
||||
fn headers(&self) -> Vec<String>;
|
||||
/// html content
|
||||
fn html(self) -> String;
|
||||
/// text content
|
||||
fn text(self) -> String;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::EmailBuilder;
|
||||
use lettre::{EmailAddress, SendableEmail};
|
||||
use super::{SendableEmail, EmailBuilder};
|
||||
use lettre::EmailAddress;
|
||||
use time::now;
|
||||
|
||||
#[test]
|
||||
fn test_multiple_from() {
|
||||
let email_builder = EmailBuilder::new();
|
||||
let date_now = now();
|
||||
let email = email_builder
|
||||
let email: SendableEmail = email_builder
|
||||
.to("anna@example.com")
|
||||
.from("dieter@example.com")
|
||||
.from("joachim@example.com")
|
||||
@@ -863,16 +468,18 @@ mod test {
|
||||
.subject("Invitation")
|
||||
.body("We invite you!")
|
||||
.build()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.into();
|
||||
let id = email.message_id().to_string();
|
||||
assert_eq!(
|
||||
format!("{}", String::from_utf8_lossy(email.message().as_ref())),
|
||||
email.message_to_string().unwrap(),
|
||||
format!(
|
||||
"Date: {}\r\nSubject: Invitation\r\nSender: \
|
||||
<dieter@example.com>\r\nTo: <anna@example.com>\r\nFrom: \
|
||||
<dieter@example.com>, <joachim@example.com>\r\nMIME-Version: \
|
||||
1.0\r\nMessage-ID: <{}.lettre@localhost>\r\n\r\nWe invite you!\r\n",
|
||||
date_now.rfc822z(),
|
||||
email.message_id()
|
||||
id
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -882,7 +489,7 @@ mod test {
|
||||
let email_builder = EmailBuilder::new();
|
||||
let date_now = now();
|
||||
|
||||
let email = email_builder
|
||||
let email: SendableEmail = email_builder
|
||||
.to("user@localhost")
|
||||
.from("user@localhost")
|
||||
.cc(("cc@localhost", "Alias"))
|
||||
@@ -894,10 +501,11 @@ mod test {
|
||||
.subject("Hello")
|
||||
.header(("X-test", "value"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
.unwrap()
|
||||
.into();
|
||||
let id = email.message_id().to_string();
|
||||
assert_eq!(
|
||||
format!("{}", String::from_utf8_lossy(email.message().as_ref())),
|
||||
email.message_to_string().unwrap(),
|
||||
format!(
|
||||
"Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \
|
||||
<sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \
|
||||
@@ -906,7 +514,7 @@ mod test {
|
||||
MIME-Version: 1.0\r\nMessage-ID: \
|
||||
<{}.lettre@localhost>\r\n\r\nHello World!\r\n",
|
||||
date_now.rfc822z(),
|
||||
email.message_id()
|
||||
id
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -916,7 +524,7 @@ mod test {
|
||||
let email_builder = EmailBuilder::new();
|
||||
let date_now = now();
|
||||
|
||||
let email = email_builder
|
||||
let email: SendableEmail = email_builder
|
||||
.to("user@localhost")
|
||||
.from("user@localhost")
|
||||
.cc(("cc@localhost", "Alias"))
|
||||
@@ -928,7 +536,8 @@ mod test {
|
||||
.subject("Hello")
|
||||
.header(("X-test", "value"))
|
||||
.build()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_eq!(
|
||||
email.envelope().from().unwrap().to_string(),
|
||||
|
||||
@@ -10,17 +10,17 @@ An email is built using an `EmailBuilder`. The simplest email could be:
|
||||
```rust
|
||||
extern crate lettre_email;
|
||||
|
||||
use lettre_email::EmailBuilder;
|
||||
use lettre_email::Email;
|
||||
|
||||
fn main() {
|
||||
// Create an email
|
||||
let email = EmailBuilder::new()
|
||||
let email = Email::builder()
|
||||
// Addresses can be specified by the tuple (email, alias)
|
||||
.to(("user@example.org", "Firstname Lastname"))
|
||||
// ... or by an address only
|
||||
.from("user@example.com")
|
||||
.subject("Hi, Hello world")
|
||||
.text("Hello world.")
|
||||
.alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.")
|
||||
.build();
|
||||
|
||||
assert!(email.is_ok());
|
||||
@@ -34,32 +34,3 @@ then generate an `Email` that can be sent.
|
||||
The `text()` method will create a plain text email, while the `html()` method will create an
|
||||
HTML email. You can use the `alternative()` method to provide both versions, using plain text
|
||||
as fallback for the HTML version.
|
||||
|
||||
#### Complete example
|
||||
|
||||
Below is a more complete example, not using method chaining:
|
||||
|
||||
```rust
|
||||
extern crate lettre_email;
|
||||
|
||||
use lettre_email::EmailBuilder;
|
||||
|
||||
fn main() {
|
||||
let mut builder = EmailBuilder::new();
|
||||
builder.add_to(("user@example.org", "Alias name"));
|
||||
builder.add_cc(("user@example.net", "Alias name"));
|
||||
builder.add_from("no-reply@example.com");
|
||||
builder.add_from("no-reply@example.eu");
|
||||
builder.set_sender("no-reply@example.com");
|
||||
builder.set_subject("Hello world");
|
||||
builder.set_alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.");
|
||||
builder.add_reply_to("contact@example.com");
|
||||
builder.add_header(("X-Custom-Header", "my header"));
|
||||
|
||||
let email = builder.build();
|
||||
assert!(email.is_ok());
|
||||
}
|
||||
```
|
||||
|
||||
See the `EmailBuilder` documentation for a complete list of methods.
|
||||
|
||||
|
||||
@@ -9,20 +9,22 @@ extern crate lettre;
|
||||
|
||||
use std::env::temp_dir;
|
||||
|
||||
use lettre::file::FileEmailTransport;
|
||||
use lettre::{SimpleSendableEmail, EmailTransport};
|
||||
use lettre::file::FileTransport;
|
||||
use lettre::{Transport, Envelope, EmailAddress, SendableEmail};
|
||||
|
||||
fn main() {
|
||||
// Write to the local temp directory
|
||||
let mut sender = FileEmailTransport::new(temp_dir());
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"message_id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
).unwrap();
|
||||
let mut sender = FileTransport::new(temp_dir());
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
let result = sender.send(&email);
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,18 +6,20 @@ The sendmail transport sends the email using the local sendmail command.
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::sendmail::SendmailTransport;
|
||||
use lettre::{SimpleSendableEmail, EmailTransport};
|
||||
use lettre::{SendableEmail, Envelope, EmailAddress, Transport};
|
||||
|
||||
fn main() {
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"message_id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
let mut sender = SendmailTransport::new();
|
||||
let result = sender.send(&email);
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -20,21 +20,23 @@ This is the most basic example of usage:
|
||||
```rust,no_run
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
|
||||
use lettre::{SendableEmail, EmailAddress, Transport, Envelope, SmtpTransport};
|
||||
|
||||
fn main() {
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"message_id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
// Open a local connection on port 25
|
||||
let mut mailer =
|
||||
SmtpTransport::builder_unencrypted_localhost().unwrap().build();
|
||||
// Send the email
|
||||
let result = mailer.send(&email);
|
||||
let result = mailer.send(email);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
@@ -46,17 +48,28 @@ fn main() {
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::smtp::authentication::{Credentials, Mechanism};
|
||||
use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
|
||||
use lettre::{SendableEmail, Envelope, EmailAddress, Transport, SmtpTransport};
|
||||
use lettre::smtp::extension::ClientId;
|
||||
use lettre::smtp::ConnectionReuseParameters;
|
||||
|
||||
fn main() {
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"message_id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
).unwrap();
|
||||
let email_1 = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id1".to_string(),
|
||||
"Hello world".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
let email_2 = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id2".to_string(),
|
||||
"Hello world a second time".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
// Connect to a remote server on a custom port
|
||||
let mut mailer = SmtpTransport::simple_builder("server.tld").unwrap()
|
||||
@@ -71,11 +84,11 @@ fn main() {
|
||||
// Enable connection reuse
|
||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build();
|
||||
|
||||
let result_1 = mailer.send(&email);
|
||||
let result_1 = mailer.send(email_1);
|
||||
assert!(result_1.is_ok());
|
||||
|
||||
// The second email will use the same connection
|
||||
let result_2 = mailer.send(&email);
|
||||
let result_2 = mailer.send(email_2);
|
||||
assert!(result_2.is_ok());
|
||||
|
||||
// Explicitly close the SMTP transaction as we enabled connection reuse
|
||||
|
||||
@@ -6,19 +6,21 @@ testing purposes.
|
||||
```rust
|
||||
extern crate lettre;
|
||||
|
||||
use lettre::stub::StubEmailTransport;
|
||||
use lettre::{SimpleSendableEmail, EmailTransport};
|
||||
use lettre::stub::StubTransport;
|
||||
use lettre::{SendableEmail, Envelope, EmailAddress, Transport};
|
||||
|
||||
fn main() {
|
||||
let email = SimpleSendableEmail::new(
|
||||
"user@localhost".to_string(),
|
||||
&["root@localhost".to_string()],
|
||||
"message_id".to_string(),
|
||||
"Hello world".to_string(),
|
||||
).unwrap();
|
||||
let email = SendableEmail::new(
|
||||
Envelope::new(
|
||||
Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
|
||||
vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
|
||||
).unwrap(),
|
||||
"id".to_string(),
|
||||
"Hello world".to_string().into_bytes(),
|
||||
);
|
||||
|
||||
let mut sender = StubEmailTransport::new_positive();
|
||||
let result = sender.send(&email);
|
||||
let mut sender = StubTransport::new_positive();
|
||||
let result = sender.send(email);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user