feat(email): Add attachments support (#186)

This commit is contained in:
Alexis Mousset
2017-08-20 02:31:12 +02:00
committed by GitHub
parent f07fe8687d
commit b087dec2d9
4 changed files with 86 additions and 4 deletions

View File

@@ -14,6 +14,9 @@ keywords = ["email", "mailer"]
[badges]
travis-ci = { repository = "lettre/lettre_email" }
[dev-dependencies]
env_logger = "*"
[dependencies]
email = "^0.0"
mime = "^0.3"

View File

@@ -1,8 +1,10 @@
extern crate lettre;
extern crate lettre_email;
extern crate mime;
use lettre::{EmailTransport, SmtpTransport};
use lettre_email::EmailBuilder;
use std::path::Path;
fn main() {
let email = EmailBuilder::new()
@@ -12,6 +14,7 @@ fn main() {
.from("user@example.com")
.subject("Hi, Hello world")
.text("Hello world.")
.attachment(Path::new("Cargo.toml"), None, mime::TEXT_PLAIN).unwrap()
.build()
.unwrap();

View File

@@ -2,16 +2,20 @@
use self::Error::*;
use std::error::Error as StdError;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::io;
use std::fmt::{self, Display, Formatter};
/// An enum of all error kinds.
#[derive(Debug)]
pub enum Error {
/// Missinf sender
/// Missing sender
MissingFrom,
/// Missing recipient
MissingTo,
/// Unparseable filename for attachment
CannotParseFilename,
/// IO error
Io(io::Error),
}
impl Display for Error {
@@ -25,6 +29,15 @@ impl StdError for Error {
match *self {
MissingFrom => "the sender is missing",
MissingTo => "the recipient is missing",
CannotParseFilename => "the attachment filename could not be parsed",
Io(ref err) => err.description(),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Io(err)
}
}

View File

@@ -68,6 +68,9 @@ use lettre::{EmailAddress, SendableEmail};
use mime::Mime;
use time::{Tm, now};
use uuid::Uuid;
use std::fs::File;
use std::path::Path;
use std::io::Read;
/// Converts an address or an address with an alias to a `Header`
pub trait IntoHeader {
@@ -179,7 +182,7 @@ pub struct SimpleEmail {
date: Option<Tm>,
html: Option<String>,
text: Option<String>,
// attachments: Vec<String>,
attachments: Vec<String>,
headers: Vec<Header>,
}
@@ -272,6 +275,17 @@ impl SimpleEmail {
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);
@@ -585,6 +599,55 @@ impl EmailBuilder {
self.date_issued = true;
}
/// Adds an attachment to the email
pub fn attachment(mut 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) => {
let mut data = String::new();
let read = f.read_to_string(&mut data);
match read {
Ok(_) => data,
Err(e) => {
return Err(From::from(e));
}
}
}
Err(e) => {
return Err(From::from(e));
}
};
let actual_filename = match filename {
Some(name) => name,
None => match path.file_name() {
Some(name) => match name.to_str() {
Some(name) => name,
None => return Err(Error::CannotParseFilename),
},
None => return Err(Error::CannotParseFilename),
},
};
let content = PartBuilder::new()
.body(body)
.header(("Content-Disposition", format!("attachment; filename=\"{}\"", actual_filename)))
.header(("Content-Type", content_type.to_string()))
.build();
self.set_message_type(MimeMultipartType::Mixed);
self.add_child(content);
Ok(())
}
/// Set the message type
pub fn message_type(mut self, message_type: MimeMultipartType) -> EmailBuilder {
self.set_message_type(message_type);