feat(builder): Add helper methods for attachments and text (#618)
This commit is contained in:
91
src/message/attachment.rs
Normal file
91
src/message/attachment.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use crate::message::{
|
||||
header::{self, ContentType},
|
||||
IntoBody, SinglePart,
|
||||
};
|
||||
|
||||
/// `SinglePart` builder for attachments
|
||||
///
|
||||
/// Allows building attachment parts easily.
|
||||
#[derive(Clone)]
|
||||
pub struct Attachment {
|
||||
disposition: Disposition,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Disposition {
|
||||
/// file name
|
||||
Attached(String),
|
||||
/// content id
|
||||
Inline(String),
|
||||
}
|
||||
|
||||
impl Attachment {
|
||||
/// Creates a new attachment
|
||||
pub fn new(filename: String) -> Self {
|
||||
Attachment {
|
||||
disposition: Disposition::Attached(filename),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inline attachment
|
||||
pub fn new_inline(content_id: String) -> Self {
|
||||
Attachment {
|
||||
disposition: Disposition::Inline(content_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the attachment part
|
||||
pub fn body<T: IntoBody>(self, content: T, content_type: ContentType) -> SinglePart {
|
||||
let mut builder = SinglePart::builder();
|
||||
builder = match self.disposition {
|
||||
Disposition::Attached(filename) => {
|
||||
builder.header(header::ContentDisposition::attachment(&filename))
|
||||
}
|
||||
Disposition::Inline(content_id) => builder
|
||||
.header(header::ContentId::from(format!("<{}>", content_id)))
|
||||
.header(header::ContentDisposition::inline()),
|
||||
};
|
||||
builder = builder.header(content_type);
|
||||
builder.body(content)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::message::header::ContentType;
|
||||
|
||||
#[test]
|
||||
fn attachment() {
|
||||
let part = super::Attachment::new(String::from("test.txt")).body(
|
||||
String::from("Hello world!"),
|
||||
ContentType::parse("text/plain").unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
&String::from_utf8_lossy(&part.formatted()),
|
||||
concat!(
|
||||
"Content-Disposition: attachment; filename=\"test.txt\"\r\n",
|
||||
"Content-Type: text/plain\r\n",
|
||||
"Content-Transfer-Encoding: 7bit\r\n\r\n",
|
||||
"Hello world!\r\n",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attachment_inline() {
|
||||
let part = super::Attachment::new_inline(String::from("id")).body(
|
||||
String::from("Hello world!"),
|
||||
ContentType::parse("text/plain").unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
&String::from_utf8_lossy(&part.formatted()),
|
||||
concat!(
|
||||
"Content-ID: <id>\r\n",
|
||||
"Content-Disposition: inline\r\n",
|
||||
"Content-Type: text/plain\r\n",
|
||||
"Content-Transfer-Encoding: 7bit\r\n\r\n",
|
||||
"Hello world!\r\n"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::io::Write;
|
||||
|
||||
use crate::message::{
|
||||
header::{ContentTransferEncoding, ContentType, Header, Headers},
|
||||
header::{self, ContentTransferEncoding, ContentType, Header, Headers},
|
||||
EmailFormat, IntoBody,
|
||||
};
|
||||
use mime::Mime;
|
||||
@@ -112,6 +112,20 @@ impl SinglePart {
|
||||
SinglePartBuilder::new()
|
||||
}
|
||||
|
||||
/// Directly create a `SinglePart` from an plain UTF-8 content
|
||||
pub fn plain<T: IntoBody>(body: T) -> Self {
|
||||
Self::builder()
|
||||
.header(header::ContentType::TEXT_PLAIN)
|
||||
.body(body)
|
||||
}
|
||||
|
||||
/// Directly create a `SinglePart` from an UTF-8 HTML content
|
||||
pub fn html<T: IntoBody>(body: T) -> Self {
|
||||
Self::builder()
|
||||
.header(header::ContentType::TEXT_HTML)
|
||||
.body(body)
|
||||
}
|
||||
|
||||
/// Get the headers from singlepart
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &Headers {
|
||||
@@ -330,6 +344,13 @@ impl MultiPart {
|
||||
MultiPart::builder().kind(MultiPartKind::Signed { protocol, micalg })
|
||||
}
|
||||
|
||||
/// Alias for HTML and plain text versions of an email
|
||||
pub fn alternative_plain_html<T: IntoBody, V: IntoBody>(plain: T, html: V) -> Self {
|
||||
Self::alternative()
|
||||
.singlepart(SinglePart::plain(plain))
|
||||
.singlepart(SinglePart::html(html))
|
||||
}
|
||||
|
||||
/// Add part to multipart
|
||||
pub fn part(mut self, part: Part) -> Self {
|
||||
self.parts.push(part);
|
||||
|
||||
@@ -71,21 +71,10 @@
|
||||
//! .reply_to("Yuin <yuin@domain.tld>".parse()?)
|
||||
//! .to("Hei <hei@domain.tld>".parse()?)
|
||||
//! .subject("Happy new year")
|
||||
//! .multipart(
|
||||
//! MultiPart::alternative()
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::TEXT_PLAIN)
|
||||
//! .body(String::from("Hello, world! :)")),
|
||||
//! )
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::TEXT_HTML)
|
||||
//! .body(String::from(
|
||||
//! "<p><b>Hello</b>, <i>world</i>! <img src=\"cid:123\"></p>",
|
||||
//! )),
|
||||
//! ),
|
||||
//! )?;
|
||||
//! .multipart(MultiPart::alternative_plain_html(
|
||||
//! String::from("Hello, world! :)"),
|
||||
//! String::from("<p><b>Hello</b>, <i>world</i>! <img src=\"cid:123\"></p>"),
|
||||
//! ))?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
@@ -124,7 +113,7 @@
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use std::error::Error;
|
||||
//! use lettre::message::{header, Body, Message, MultiPart, Part, SinglePart};
|
||||
//! use lettre::message::{header, Attachment, Body, Message, MultiPart, Part, SinglePart};
|
||||
//! use std::fs;
|
||||
//!
|
||||
//! # fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -144,35 +133,22 @@
|
||||
//! MultiPart::mixed()
|
||||
//! .multipart(
|
||||
//! MultiPart::alternative()
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::TEXT_PLAIN)
|
||||
//! .body(String::from("Hello, world! :)")),
|
||||
//! )
|
||||
//! .singlepart(SinglePart::plain(String::from("Hello, world! :)")))
|
||||
//! .multipart(
|
||||
//! MultiPart::related()
|
||||
//! .singlepart(SinglePart::html(String::from(
|
||||
//! "<p><b>Hello</b>, <i>world</i>! <img src=cid:123></p>",
|
||||
//! )))
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::TEXT_HTML)
|
||||
//! .body(String::from(
|
||||
//! "<p><b>Hello</b>, <i>world</i>! <img src=cid:123></p>",
|
||||
//! )),
|
||||
//! )
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::parse("image/png")?)
|
||||
//! .header(header::ContentDisposition::inline())
|
||||
//! .header(header::ContentId::from(String::from("<123>")))
|
||||
//! .body(image_body),
|
||||
//! Attachment::new_inline(String::from("123"))
|
||||
//! .body(image_body, "image/png".parse().unwrap()),
|
||||
//! ),
|
||||
//! ),
|
||||
//! )
|
||||
//! .singlepart(
|
||||
//! SinglePart::builder()
|
||||
//! .header(header::ContentType::TEXT_PLAIN)
|
||||
//! .header(header::ContentDisposition::attachment("example.rs"))
|
||||
//! .body(String::from("fn main() { println!(\"Hello, World!\") }")),
|
||||
//! ),
|
||||
//! .singlepart(Attachment::new(String::from("example.rs")).body(
|
||||
//! String::from("fn main() { println!(\"Hello, World!\") }"),
|
||||
//! "text/plain".parse().unwrap(),
|
||||
//! )),
|
||||
//! )?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
@@ -228,10 +204,12 @@
|
||||
|
||||
use std::{convert::TryFrom, io::Write, iter, time::SystemTime};
|
||||
|
||||
pub use attachment::Attachment;
|
||||
pub use body::{Body, IntoBody, MaybeString};
|
||||
pub use mailbox::*;
|
||||
pub use mimebody::*;
|
||||
|
||||
mod attachment;
|
||||
mod body;
|
||||
pub mod header;
|
||||
mod mailbox;
|
||||
@@ -293,7 +271,8 @@ impl MessageBuilder {
|
||||
|
||||
/// Set `Date` header using current date/time
|
||||
///
|
||||
/// Shortcut for `self.date(SystemTime::now())`.
|
||||
/// Shortcut for `self.date(SystemTime::now())`, it is automatically inserted
|
||||
/// if no date has been provided.
|
||||
pub fn date_now(self) -> Self {
|
||||
self.date(SystemTime::now())
|
||||
}
|
||||
@@ -306,7 +285,7 @@ impl MessageBuilder {
|
||||
self.header(header::Subject::from(s))
|
||||
}
|
||||
|
||||
/// Set `Mime-Version` header to 1.0
|
||||
/// Set `MIME-Version` header to 1.0
|
||||
///
|
||||
/// Shortcut for `self.header(header::MIME_VERSION_1_0)`.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user