own ContentType, drop the Deref to mailparse
This commit is contained in:
+235
-51
@@ -17,8 +17,10 @@ use header::build_headers;
|
|||||||
/// Re-exported from [`mailparse`], which parses the MIME structure for this crate: these types
|
/// Re-exported from [`mailparse`], which parses the MIME structure for this crate: these types
|
||||||
/// appear in this crate's public API, so depending on `mailparse` separately is neither necessary
|
/// appear in this crate's public API, so depending on `mailparse` separately is neither necessary
|
||||||
/// nor advisable — the version re-exported here is the one these signatures refer to.
|
/// nor advisable — the version re-exported here is the one these signatures refer to.
|
||||||
pub use mailparse::{DispositionType, ParsedContentDisposition, ParsedContentType};
|
pub use mailparse::{DispositionType, ParsedContentDisposition};
|
||||||
use mailparse::{ParsedMail, body::Body, parse_content_disposition, parse_content_type};
|
use mailparse::{
|
||||||
|
ParsedContentType, ParsedMail, body::Body, parse_content_disposition, parse_content_type,
|
||||||
|
};
|
||||||
|
|
||||||
/// Re-exported from [`mediatype`]. [`MediaType`] is the content type accepted by
|
/// Re-exported from [`mediatype`]. [`MediaType`] is the content type accepted by
|
||||||
/// [`CowMail::set_content`] and [`CowMail::set_content_raw`], and [`names`] / [`values`] hold the
|
/// [`CowMail::set_content`] and [`CowMail::set_content_raw`], and [`names`] / [`values`] hold the
|
||||||
@@ -28,7 +30,7 @@ pub use mediatype::{MediaType, names, values};
|
|||||||
use mediatype::{MediaTypeBuf, Name, ReadParams, WriteParams, names::*, values::UTF_8};
|
use mediatype::{MediaTypeBuf, Name, ReadParams, WriteParams, names::*, values::UTF_8};
|
||||||
use rand::{RngExt, distr::Alphanumeric, rng};
|
use rand::{RngExt, distr::Alphanumeric, rng};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::Deref;
|
use std::fmt::Display;
|
||||||
|
|
||||||
/// A failure reported by the parser the crate uses internally.
|
/// A failure reported by the parser the crate uses internally.
|
||||||
///
|
///
|
||||||
@@ -162,66 +164,248 @@ pub enum MessageType {
|
|||||||
MultiPart(String),
|
MultiPart(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A parsed `Content-Type` header: the mimetype, the charset and the parameters.
|
/// A `Content-Type`: the mimetype plus its parameters.
|
||||||
///
|
///
|
||||||
/// A thin wrapper around [`ParsedContentType`] that it [`Deref`]s to, so `mimetype`, `charset` and
|
/// The type both sides of the crate speak: [`Mail::content_type`] hands one back for a part, and
|
||||||
/// `params` are reachable directly. It exists to add [`Clone`] and a default that knows about
|
/// [`CowMail::set_content`] takes one for the part it writes. [`Display`](std::fmt::Display)
|
||||||
/// `multipart/digest` (see [`default_conditional`](Self::default_conditional)).
|
/// renders it as it belongs in the header, which is how it ends up serialized.
|
||||||
///
|
///
|
||||||
/// Built from a header value with `ContentType::from("text/plain; charset=utf-8")`, or obtained
|
/// Build one with [`new`](Self::new), take a ready-made one from [`mime`], or parse a header value
|
||||||
/// from a part with [`Mail::content_type`]. Note that the latter re-parses the header on every
|
/// with [`parse`](Self::parse) (also `ContentType::from("text/plain; charset=utf-8")`). Note that
|
||||||
/// call, so hold on to the result rather than asking repeatedly.
|
/// [`Mail::content_type`] re-parses the header on every call, so hold on to the result rather than
|
||||||
#[derive(Debug, Default)]
|
/// asking repeatedly.
|
||||||
|
///
|
||||||
|
/// The `charset` is a parameter like any other — read it with [`charset`](Self::charset), which
|
||||||
|
/// falls back to `us-ascii` as RFC 2045 section 5.2 prescribes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use cow_mail::ContentType;
|
||||||
|
///
|
||||||
|
/// let ctype = ContentType::parse("Text/HTML; Charset=\"utf-8\"");
|
||||||
|
/// assert_eq!(ctype.mimetype(), "text/html");
|
||||||
|
/// assert_eq!(ctype.subty(), "html");
|
||||||
|
/// assert_eq!(ctype.charset(), "utf-8");
|
||||||
|
///
|
||||||
|
/// // A parameter is quoted only when it has to be.
|
||||||
|
/// let built = ContentType::new("image", "png").with_param("name", "logo.png");
|
||||||
|
/// assert_eq!(built.to_string(), "image/png; name=logo.png");
|
||||||
|
/// let spaced = built.with_param("name", "my logo.png");
|
||||||
|
/// assert_eq!(spaced.to_string(), "image/png; name=\"my logo.png\"");
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ContentType {
|
pub struct ContentType {
|
||||||
ctype: ParsedContentType,
|
/// Always lowercase, so comparisons can be plain string comparisons.
|
||||||
|
mimetype: Cow<'static, str>,
|
||||||
|
/// Keys lowercase, values unquoted and unescaped. A `Vec` rather than a map, to keep the
|
||||||
|
/// parameters in the order they were added — that order is what gets serialized.
|
||||||
|
params: Vec<(Cow<'static, str>, Cow<'static, str>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentType {
|
impl ContentType {
|
||||||
|
/// A content type from a `&'static str` that is already lowercase, in a `const` context.
|
||||||
|
///
|
||||||
|
/// This is how the constants in [`mime`] are built. Nothing lowercases the string, so an
|
||||||
|
/// uppercase one would break the comparisons the rest of the crate does; use
|
||||||
|
/// [`new`](Self::new) or [`parse`](Self::parse) for anything that is not a literal.
|
||||||
|
pub const fn from_static(mimetype: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
mimetype: Cow::Borrowed(mimetype),
|
||||||
|
params: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A content type from a type and a subtype, e.g. `ContentType::new("image", "png")`.
|
||||||
|
///
|
||||||
|
/// Both are lowercased. For a type that [`mime`] already has a constant for, use that instead.
|
||||||
|
pub fn new(ty: &str, subty: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
mimetype: Cow::Owned(format!(
|
||||||
|
"{}/{}",
|
||||||
|
ty.to_ascii_lowercase(),
|
||||||
|
subty.to_ascii_lowercase()
|
||||||
|
)),
|
||||||
|
params: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a `Content-Type` header value.
|
||||||
|
///
|
||||||
|
/// The mimetype and the parameter keys come back lowercased, parameter values unquoted. An
|
||||||
|
/// unparseable value yields the default, `text/plain`.
|
||||||
|
pub fn parse(value: &str) -> Self {
|
||||||
|
Self::from_parsed(parse_content_type(value))
|
||||||
|
}
|
||||||
|
|
||||||
/// The content type to assume for a part that has no `Content-Type` header.
|
/// The content type to assume for a part that has no `Content-Type` header.
|
||||||
///
|
///
|
||||||
/// `text/plain; charset=us-ascii` normally, but `message/rfc822` for a part of a
|
/// `text/plain` normally, but `message/rfc822` for a part of a `multipart/digest`, per RFC
|
||||||
/// `multipart/digest`, per RFC 2046 section 5.1.5. Pass whether the part sits in such a digest.
|
/// 2046 section 5.1.5. Pass whether the part sits in such a digest.
|
||||||
pub fn default_conditional(in_multipart_digest: bool) -> Self {
|
pub fn default_conditional(in_multipart_digest: bool) -> Self {
|
||||||
let mut ctype = ParsedContentType::default();
|
match in_multipart_digest {
|
||||||
if in_multipart_digest {
|
true => mime::MESSAGE_RFC822,
|
||||||
ctype.mimetype = "message/rfc822".to_string();
|
false => mime::TEXT_PLAIN,
|
||||||
}
|
}
|
||||||
Self { ctype }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for ContentType {
|
/// The full mimetype, lowercase, e.g. `text/plain`.
|
||||||
fn clone(&self) -> Self {
|
pub fn mimetype(&self) -> &str {
|
||||||
|
&self.mimetype
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type half of the mimetype, e.g. `text` of `text/plain`.
|
||||||
|
pub fn ty(&self) -> &str {
|
||||||
|
match self.mimetype.split_once('/') {
|
||||||
|
Some((ty, _)) => ty,
|
||||||
|
None => &self.mimetype,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The subtype half of the mimetype, e.g. `plain` of `text/plain`.
|
||||||
|
///
|
||||||
|
/// Empty for a mimetype without a `/`, which only a malformed header can produce.
|
||||||
|
pub fn subty(&self) -> &str {
|
||||||
|
match self.mimetype.split_once('/') {
|
||||||
|
Some((_, subty)) => subty,
|
||||||
|
None => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `charset` parameter, or `us-ascii` when there is none (RFC 2045 section 5.2).
|
||||||
|
pub fn charset(&self) -> &str {
|
||||||
|
self.get_param("charset").unwrap_or("us-ascii")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The parameters, in the order they will be serialized in.
|
||||||
|
pub fn params(&self) -> impl Iterator<Item = (&str, &str)> {
|
||||||
|
self.params.iter().map(|(k, v)| (k.as_ref(), v.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The parameter `key`, matched case-insensitively.
|
||||||
|
pub fn get_param(&self, key: &str) -> Option<&str> {
|
||||||
|
self.params
|
||||||
|
.iter()
|
||||||
|
.find(|(k, _)| k.eq_ignore_ascii_case(key))
|
||||||
|
.map(|(_, v)| v.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the parameter `key` to `value`, replacing it if it is already there.
|
||||||
|
///
|
||||||
|
/// The key is lowercased; the value is kept as given and quoted on serialization if it needs
|
||||||
|
/// to be. A replaced parameter keeps its position.
|
||||||
|
pub fn set_param(&mut self, key: &str, value: impl Into<String>) {
|
||||||
|
let value = Cow::Owned(value.into());
|
||||||
|
match self.params.iter_mut().find(|(k, _)| k.eq_ignore_ascii_case(key)) {
|
||||||
|
Some(param) => param.1 = value,
|
||||||
|
None => self.params.push((Cow::Owned(key.to_ascii_lowercase()), value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`set_param`](Self::set_param) as a builder step.
|
||||||
|
pub fn with_param(mut self, key: &str, value: impl Into<String>) -> Self {
|
||||||
|
self.set_param(key, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build from what the parser produced.
|
||||||
|
///
|
||||||
|
/// `charset` is dropped: the parser reports it both as a field and as a parameter, defaulting
|
||||||
|
/// the field to `us-ascii`, and keeping both would serialize it twice.
|
||||||
|
pub(crate) fn from_parsed(ctype: ParsedContentType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctype: ParsedContentType {
|
mimetype: Cow::Owned(ctype.mimetype),
|
||||||
mimetype: self.ctype.mimetype.clone(),
|
params: ctype
|
||||||
charset: self.ctype.charset.clone(),
|
.params
|
||||||
params: self.ctype.params.clone(),
|
.into_iter()
|
||||||
},
|
.map(|(k, v)| (Cow::Owned(k), Cow::Owned(v)))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hand back to the parser, for the body decoding it does.
|
||||||
|
pub(crate) fn to_parsed(&self) -> ParsedContentType {
|
||||||
|
ParsedContentType {
|
||||||
|
mimetype: self.mimetype.to_string(),
|
||||||
|
charset: self.charset().to_string(),
|
||||||
|
params: self
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ContentType {
|
impl Default for ContentType {
|
||||||
type Target = ParsedContentType;
|
fn default() -> Self {
|
||||||
|
mime::TEXT_PLAIN
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.ctype
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParsedContentType> for ContentType {
|
impl Display for ContentType {
|
||||||
fn from(ctype: ParsedContentType) -> Self {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
Self { ctype }
|
write!(f, "{}", self.mimetype)?;
|
||||||
|
for (key, value) in &self.params {
|
||||||
|
match is_token(value) {
|
||||||
|
true => write!(f, "; {key}={value}")?,
|
||||||
|
false => {
|
||||||
|
let escaped = value.replace('\\', r"\\").replace('"', "\\\"");
|
||||||
|
write!(f, "; {key}=\"{escaped}\"")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for ContentType {
|
impl From<&str> for ContentType {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
parse_content_type(value).into()
|
Self::parse(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `value` is an RFC 2045 token, i.e. can go into a header unquoted.
|
||||||
|
fn is_token(value: &str) -> bool {
|
||||||
|
!value.is_empty()
|
||||||
|
&& value
|
||||||
|
.bytes()
|
||||||
|
.all(|b| b > 0x20 && b < 0x7f && !br#"()<>@,;:\"/[]?="#.contains(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ready-made [`ContentType`] constants for the types this crate itself deals in.
|
||||||
|
///
|
||||||
|
/// Anything else is a [`ContentType::new`] or a [`ContentType::parse`] away — this is not meant to
|
||||||
|
/// grow into a mimetype registry.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use cow_mail::mime;
|
||||||
|
///
|
||||||
|
/// assert_eq!(mime::TEXT_HTML.mimetype(), "text/html");
|
||||||
|
/// assert_eq!(mime::MULTIPART_MIXED.subty(), "mixed");
|
||||||
|
/// ```
|
||||||
|
pub mod mime {
|
||||||
|
use super::ContentType;
|
||||||
|
|
||||||
|
/// `text/plain`, the default content type of a part without a `Content-Type` header.
|
||||||
|
pub const TEXT_PLAIN: ContentType = ContentType::from_static("text/plain");
|
||||||
|
/// `text/html`.
|
||||||
|
pub const TEXT_HTML: ContentType = ContentType::from_static("text/html");
|
||||||
|
/// `message/rfc822`, the default content type inside a `multipart/digest`.
|
||||||
|
pub const MESSAGE_RFC822: ContentType = ContentType::from_static("message/rfc822");
|
||||||
|
/// `multipart/mixed`, a part and its attachments.
|
||||||
|
pub const MULTIPART_MIXED: ContentType = ContentType::from_static("multipart/mixed");
|
||||||
|
/// `multipart/alternative`, the same content in two representations.
|
||||||
|
pub const MULTIPART_ALTERNATIVE: ContentType =
|
||||||
|
ContentType::from_static("multipart/alternative");
|
||||||
|
/// `multipart/related`, a part and the resources it refers to.
|
||||||
|
pub const MULTIPART_RELATED: ContentType = ContentType::from_static("multipart/related");
|
||||||
|
/// `multipart/digest`, a collection of messages.
|
||||||
|
pub const MULTIPART_DIGEST: ContentType = ContentType::from_static("multipart/digest");
|
||||||
|
}
|
||||||
|
|
||||||
/// The MIME view of a message or one of its parts, on top of its headers.
|
/// The MIME view of a message or one of its parts, on top of its headers.
|
||||||
///
|
///
|
||||||
/// Implementors supply storage — the body bytes, the default content type, and the two ways of
|
/// Implementors supply storage — the body bytes, the default content type, and the two ways of
|
||||||
@@ -280,13 +464,13 @@ pub trait Mail<'a, T: Header<'a> + 'a>: HeaderMap<'a, T> {
|
|||||||
/// [`SinglePart`](MessageType::SinglePart), as does an unparseable content type.
|
/// [`SinglePart`](MessageType::SinglePart), as does an unparseable content type.
|
||||||
fn message_type(&self) -> MessageType {
|
fn message_type(&self) -> MessageType {
|
||||||
let ctype = self.content_type();
|
let ctype = self.content_type();
|
||||||
match ctype.mimetype.parse::<MediaTypeBuf>() {
|
match ctype.mimetype().parse::<MediaTypeBuf>() {
|
||||||
Ok(mtype) => {
|
Ok(mtype) => {
|
||||||
let mut res = MessageType::SinglePart;
|
let mut res = MessageType::SinglePart;
|
||||||
if mtype.ty() == MULTIPART
|
if mtype.ty() == MULTIPART
|
||||||
&& let Some(boundary) = ctype.params.get("boundary")
|
&& let Some(boundary) = ctype.get_param("boundary")
|
||||||
{
|
{
|
||||||
res = MessageType::MultiPart(boundary.clone());
|
res = MessageType::MultiPart(boundary.to_string());
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -358,7 +542,7 @@ pub trait Mail<'a, T: Header<'a> + 'a>: HeaderMap<'a, T> {
|
|||||||
let transfer_encoding = self
|
let transfer_encoding = self
|
||||||
.find_value("Content-Transfer-Encoding")
|
.find_value("Content-Transfer-Encoding")
|
||||||
.map(|encoding| encoding.to_lowercase());
|
.map(|encoding| encoding.to_lowercase());
|
||||||
match Body::new(self.raw_body(), &ctype, &transfer_encoding) {
|
match Body::new(self.raw_body(), &ctype.to_parsed(), &transfer_encoding) {
|
||||||
Body::Base64(body) | Body::QuotedPrintable(body) => {
|
Body::Base64(body) | Body::QuotedPrintable(body) => {
|
||||||
Ok(body.get_decoded_as_string().map_err(parse_error)?)
|
Ok(body.get_decoded_as_string().map_err(parse_error)?)
|
||||||
}
|
}
|
||||||
@@ -380,7 +564,7 @@ pub trait Mail<'a, T: Header<'a> + 'a>: HeaderMap<'a, T> {
|
|||||||
let transfer_encoding = self
|
let transfer_encoding = self
|
||||||
.find_value("Content-Transfer-Encoding")
|
.find_value("Content-Transfer-Encoding")
|
||||||
.map(|encoding| encoding.to_lowercase());
|
.map(|encoding| encoding.to_lowercase());
|
||||||
match Body::new(self.raw_body(), &ctype, &transfer_encoding) {
|
match Body::new(self.raw_body(), &ctype.to_parsed(), &transfer_encoding) {
|
||||||
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded().map_err(parse_error)?),
|
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded().map_err(parse_error)?),
|
||||||
Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::from(body.get_raw())),
|
Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::from(body.get_raw())),
|
||||||
Body::Binary(body) => Ok(Vec::from(body.get_raw())),
|
Body::Binary(body) => Ok(Vec::from(body.get_raw())),
|
||||||
@@ -415,13 +599,13 @@ impl PartInfo {
|
|||||||
self.media.subty()
|
self.media.subty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn boundary(&self) -> Option<&String> {
|
fn boundary(&self) -> Option<&str> {
|
||||||
self.ctype.params.get("boundary")
|
self.ctype.get_param("boundary")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `start` parameter of a multipart/related, naming its root part.
|
/// The `start` parameter of a multipart/related, naming its root part.
|
||||||
fn start(&self) -> Option<&String> {
|
fn start(&self) -> Option<&str> {
|
||||||
self.ctype.params.get("start")
|
self.ctype.get_param("start")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_multipart(&self) -> bool {
|
fn is_multipart(&self) -> bool {
|
||||||
@@ -608,7 +792,7 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
fn part_info(&self) -> PartInfo {
|
fn part_info(&self) -> PartInfo {
|
||||||
let ctype = self.content_type();
|
let ctype = self.content_type();
|
||||||
let media = ctype
|
let media = ctype
|
||||||
.mimetype
|
.mimetype()
|
||||||
.parse::<MediaTypeBuf>()
|
.parse::<MediaTypeBuf>()
|
||||||
.unwrap_or_else(|_| MediaTypeBuf::new(TEXT, PLAIN));
|
.unwrap_or_else(|_| MediaTypeBuf::new(TEXT, PLAIN));
|
||||||
PartInfo { ctype, media }
|
PartInfo { ctype, media }
|
||||||
@@ -629,7 +813,7 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
/// let mut part = CowMail::default();
|
/// let mut part = CowMail::default();
|
||||||
/// part.set_content("<p>hi</p>", Some(MediaType::new(names::TEXT, names::HTML)));
|
/// part.set_content("<p>hi</p>", Some(MediaType::new(names::TEXT, names::HTML)));
|
||||||
///
|
///
|
||||||
/// assert_eq!(part.content_type().mimetype, "text/html");
|
/// assert_eq!(part.content_type().mimetype(), "text/html");
|
||||||
/// assert_eq!(part.get_content().unwrap(), "<p>hi</p>");
|
/// assert_eq!(part.get_content().unwrap(), "<p>hi</p>");
|
||||||
///
|
///
|
||||||
/// // Non-ASCII text gets a charset, and an encoding that survives transport.
|
/// // Non-ASCII text gets a charset, and an encoding that survives transport.
|
||||||
@@ -677,7 +861,7 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
pub fn get_filename(&self) -> Option<String> {
|
pub fn get_filename(&self) -> Option<String> {
|
||||||
self.content_disposition()
|
self.content_disposition()
|
||||||
.and_then(|disp| disp.params.get("filename").cloned())
|
.and_then(|disp| disp.params.get("filename").cloned())
|
||||||
.or_else(|| self.content_type().params.get("name").cloned())
|
.or_else(|| self.content_type().get_param("name").map(str::to_string))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `boundary` parameter of this part's `Content-Type` header, if it has one.
|
/// The `boundary` parameter of this part's `Content-Type` header, if it has one.
|
||||||
@@ -896,7 +1080,7 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
/// mail.make_alternative(Some("boundary".to_string()));
|
/// mail.make_alternative(Some("boundary".to_string()));
|
||||||
///
|
///
|
||||||
/// // The content moved down one level; Subject stayed up top.
|
/// // The content moved down one level; Subject stayed up top.
|
||||||
/// assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
/// assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
/// assert_eq!(mail.find_value("Subject").unwrap(), "Hi");
|
/// assert_eq!(mail.find_value("Subject").unwrap(), "Hi");
|
||||||
/// assert_eq!(mail.subparts[0].get_content().unwrap(), "plain");
|
/// assert_eq!(mail.subparts[0].get_content().unwrap(), "plain");
|
||||||
///
|
///
|
||||||
@@ -963,7 +1147,7 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
/// );
|
/// );
|
||||||
/// mail.add_attachment(attachment);
|
/// mail.add_attachment(attachment);
|
||||||
///
|
///
|
||||||
/// assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
/// assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
/// let last = mail.subparts.last().unwrap();
|
/// let last = mail.subparts.last().unwrap();
|
||||||
/// assert!(last.is_attachment());
|
/// assert!(last.is_attachment());
|
||||||
/// assert_eq!(last.find_value("Content-Transfer-Encoding").unwrap(), "base64");
|
/// assert_eq!(last.find_value("Content-Transfer-Encoding").unwrap(), "base64");
|
||||||
@@ -1127,12 +1311,12 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
/// mail.add_header("Subject", "Hi");
|
/// mail.add_header("Subject", "Hi");
|
||||||
/// mail.set_body("plain", "<p>html</p>").unwrap();
|
/// mail.set_body("plain", "<p>html</p>").unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
/// assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
/// assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "plain");
|
/// assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "plain");
|
||||||
/// assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>html</p>");
|
/// assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>html</p>");
|
||||||
///
|
///
|
||||||
/// // Plain comes first.
|
/// // Plain comes first.
|
||||||
/// assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
/// assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_body<T: ToString>(&mut self, text: T, html: T) -> Result<(), CowMailError> {
|
pub fn set_body<T: ToString>(&mut self, text: T, html: T) -> Result<(), CowMailError> {
|
||||||
self.set_text_body(text)?;
|
self.set_text_body(text)?;
|
||||||
|
|||||||
+61
-61
@@ -74,8 +74,8 @@
|
|||||||
CowMail::try_from(concat!("Content-Type: text/plain\n", "\n", "This is a test message").as_bytes())
|
CowMail::try_from(concat!("Content-Type: text/plain\n", "\n", "This is a test message").as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ctype = mail.content_type();
|
let ctype = mail.content_type();
|
||||||
assert_eq!(ctype.mimetype, "text/plain");
|
assert_eq!(ctype.mimetype(), "text/plain");
|
||||||
assert_eq!(ctype.charset, "us-ascii");
|
assert_eq!(ctype.charset(), "us-ascii");
|
||||||
assert!(!mail.is_multipart());
|
assert!(!mail.is_multipart());
|
||||||
assert_eq!(mail.get_content().unwrap(), "This is a test message");
|
assert_eq!(mail.get_content().unwrap(), "This is a test message");
|
||||||
|
|
||||||
@@ -84,15 +84,15 @@
|
|||||||
assert_eq!(mail.get_content().unwrap(), "");
|
assert_eq!(mail.get_content().unwrap(), "");
|
||||||
mail.set_content("This is a new test message ❤\n".to_string(), None);
|
mail.set_content("This is a new test message ❤\n".to_string(), None);
|
||||||
let ctype = mail.content_type();
|
let ctype = mail.content_type();
|
||||||
assert_eq!(ctype.mimetype, "text/plain");
|
assert_eq!(ctype.mimetype(), "text/plain");
|
||||||
assert_eq!(ctype.charset, "UTF-8");
|
assert_eq!(ctype.charset(), "UTF-8");
|
||||||
|
|
||||||
mail.clear_content();
|
mail.clear_content();
|
||||||
assert_eq!(mail.get_content().unwrap(), "");
|
assert_eq!(mail.get_content().unwrap(), "");
|
||||||
mail.set_content("This is a new test message\n".to_string(), None);
|
mail.set_content("This is a new test message\n".to_string(), None);
|
||||||
let ctype = mail.content_type();
|
let ctype = mail.content_type();
|
||||||
assert_eq!(ctype.mimetype, "text/plain");
|
assert_eq!(ctype.mimetype(), "text/plain");
|
||||||
assert_eq!(ctype.charset, "us-ascii");
|
assert_eq!(ctype.charset(), "us-ascii");
|
||||||
|
|
||||||
mail.add_header("From", "\"John Doe\" <john.doe@example.com>");
|
mail.add_header("From", "\"John Doe\" <john.doe@example.com>");
|
||||||
|
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
let alt_boundary = mail.get_boundary().unwrap();
|
let alt_boundary = mail.get_boundary().unwrap();
|
||||||
|
|
||||||
let ctype = mail.content_type();
|
let ctype = mail.content_type();
|
||||||
assert_eq!(ctype.mimetype, "multipart/alternative");
|
assert_eq!(ctype.mimetype(), "multipart/alternative");
|
||||||
assert_eq!(ctype.params.get("boundary").unwrap(), &alt_boundary);
|
assert_eq!(ctype.get_param("boundary").unwrap(), alt_boundary);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mail.find_header("Content-Type").unwrap().get_value(),
|
mail.find_header("Content-Type").unwrap().get_value(),
|
||||||
format!("multipart/alternative; boundary=\"{}\"", alt_boundary)
|
format!("multipart/alternative; boundary=\"{}\"", alt_boundary)
|
||||||
@@ -187,10 +187,10 @@
|
|||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
||||||
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
||||||
}
|
}
|
||||||
@@ -205,10 +205,10 @@
|
|||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
||||||
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
||||||
}
|
}
|
||||||
@@ -219,15 +219,15 @@
|
|||||||
let mut mail = CowMail::<CowHeader>::new();
|
let mut mail = CowMail::<CowHeader>::new();
|
||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
assert_eq!(mail.content_type().mimetype, "text/plain");
|
assert_eq!(mail.content_type().mimetype(), "text/plain");
|
||||||
assert!(!mail.is_multipart());
|
assert!(!mail.is_multipart());
|
||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: empty + set_html + set_text -> alternative[plain, html]
|
// Test: empty + set_html + set_text -> alternative[plain, html]
|
||||||
@@ -236,15 +236,15 @@
|
|||||||
let mut mail = CowMail::<CowHeader>::new();
|
let mut mail = CowMail::<CowHeader>::new();
|
||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert!(mail.is_multipart());
|
assert!(mail.is_multipart());
|
||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: mixed[attachment] + set_text -> mixed[plain, attachment]
|
// Test: mixed[attachment] + set_text -> mixed[plain, attachment]
|
||||||
@@ -266,14 +266,14 @@
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 1);
|
assert_eq!(mail.subparts.len(), 1);
|
||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,12 +298,12 @@
|
|||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,12 +332,12 @@
|
|||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,13 +363,13 @@
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/related");
|
assert_eq!(mail.content_type().mimetype(), "multipart/related");
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "multipart/related");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "multipart/related");
|
||||||
assert_eq!(mail.subparts[1].subparts.len(), 2);
|
assert_eq!(mail.subparts[1].subparts.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,10 +419,10 @@
|
|||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
// the related container is wrapped in an alternative holding both bodies
|
// the related container is wrapped in an alternative holding both bodies
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "multipart/related");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "multipart/related");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text\n");
|
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text\n");
|
||||||
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
||||||
}
|
}
|
||||||
@@ -460,11 +460,11 @@
|
|||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype, "multipart/related");
|
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype(), "multipart/related");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,10 +487,10 @@
|
|||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: alternative[html] + set_text -> alternative[plain, html]
|
// Test: alternative[html] + set_text -> alternative[plain, html]
|
||||||
@@ -512,10 +512,10 @@
|
|||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: update existing body in alternative[plain, html]
|
// Test: update existing body in alternative[plain, html]
|
||||||
@@ -572,9 +572,9 @@
|
|||||||
|
|
||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[0].get_content().unwrap(), "Plain text");
|
assert_eq!(mail.subparts[0].get_content().unwrap(), "Plain text");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
assert_eq!(mail.subparts[1].get_content().unwrap(), "binary data");
|
assert_eq!(mail.subparts[1].get_content().unwrap(), "binary data");
|
||||||
@@ -599,9 +599,9 @@
|
|||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "text/html");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,12 +622,12 @@
|
|||||||
mail.set_text_body("Plain text").unwrap();
|
mail.set_text_body("Plain text").unwrap();
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 2);
|
assert_eq!(mail.subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "multipart/alternative");
|
||||||
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
assert_eq!(mail.subparts[0].subparts.len(), 2);
|
||||||
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype, "text/plain");
|
assert_eq!(mail.subparts[0].subparts[0].content_type().mimetype(), "text/plain");
|
||||||
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype, "text/html");
|
assert_eq!(mail.subparts[0].subparts[1].content_type().mimetype(), "text/html");
|
||||||
assert!(mail.subparts[1].is_attachment());
|
assert!(mail.subparts[1].is_attachment());
|
||||||
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
assert_eq!(mail.get_body_part(BodyType::PLAIN).unwrap().get_content().unwrap(), "Plain text");
|
||||||
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
assert_eq!(mail.get_body_part(BodyType::HTML).unwrap().get_content().unwrap(), "<p>HTML</p>");
|
||||||
@@ -662,10 +662,10 @@
|
|||||||
|
|
||||||
mail.set_html_body("<p>HTML</p>").unwrap();
|
mail.set_html_body("<p>HTML</p>").unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.content_type().mimetype, "multipart/mixed");
|
assert_eq!(mail.content_type().mimetype(), "multipart/mixed");
|
||||||
assert_eq!(mail.subparts.len(), 3);
|
assert_eq!(mail.subparts.len(), 3);
|
||||||
assert!(mail.subparts[0].is_attachment());
|
assert!(mail.subparts[0].is_attachment());
|
||||||
assert_eq!(mail.subparts[1].content_type().mimetype, "multipart/alternative");
|
assert_eq!(mail.subparts[1].content_type().mimetype(), "multipart/alternative");
|
||||||
assert!(mail.subparts[2].is_attachment());
|
assert!(mail.subparts[2].is_attachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,7 +754,7 @@
|
|||||||
let mail = CowMail::try_from(raw.as_bytes()).unwrap();
|
let mail = CowMail::try_from(raw.as_bytes()).unwrap();
|
||||||
|
|
||||||
assert_eq!(mail.subparts.len(), 1);
|
assert_eq!(mail.subparts.len(), 1);
|
||||||
assert_eq!(mail.subparts[0].content_type().mimetype, "message/rfc822");
|
assert_eq!(mail.subparts[0].content_type().mimetype(), "message/rfc822");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: a nested multipart with a preamble and an epilogue round-trips
|
// Test: a nested multipart with a preamble and an epilogue round-trips
|
||||||
|
|||||||
Reference in New Issue
Block a user