lettre/message/header/
content_type.rsuse std::{
error::Error as StdError,
fmt::{self, Display},
str::FromStr,
};
use mime::Mime;
use super::{Header, HeaderName, HeaderValue};
use crate::BoxError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContentType(Mime);
impl ContentType {
pub const TEXT_PLAIN: ContentType = Self::from_mime(mime::TEXT_PLAIN_UTF_8);
pub const TEXT_HTML: ContentType = Self::from_mime(mime::TEXT_HTML_UTF_8);
pub fn parse(s: &str) -> Result<ContentType, ContentTypeErr> {
Ok(Self::from_mime(s.parse().map_err(ContentTypeErr)?))
}
pub(crate) const fn from_mime(mime: Mime) -> Self {
Self(mime)
}
pub(crate) fn as_ref(&self) -> &Mime {
&self.0
}
}
impl Header for ContentType {
fn name() -> HeaderName {
HeaderName::new_from_ascii_str("Content-Type")
}
fn parse(s: &str) -> Result<Self, BoxError> {
Ok(Self(s.parse()?))
}
fn display(&self) -> HeaderValue {
HeaderValue::new(Self::name(), self.0.to_string())
}
}
impl FromStr for ContentType {
type Err = ContentTypeErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
#[cfg(feature = "mime03")]
#[cfg_attr(docsrs, doc(cfg(feature = "mime03")))]
impl From<Mime> for ContentType {
fn from(mime: Mime) -> Self {
Self::from_mime(mime)
}
}
#[derive(Debug)]
pub struct ContentTypeErr(mime::FromStrError);
impl StdError for ContentTypeErr {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&self.0)
}
}
impl Display for ContentTypeErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[cfg(feature = "serde")]
mod serde {
use std::fmt;
use serde::{
de::{self, Deserialize, Deserializer, Visitor},
ser::{Serialize, Serializer},
};
use super::ContentType;
impl Serialize for ContentType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_newtype_struct("ContentType", &format!("{}", &self.0))
}
}
impl<'de> Deserialize<'de> for ContentType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ContentTypeVisitor;
impl<'de> Visitor<'de> for ContentTypeVisitor {
type Value = ContentType;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a ContentType string like `text/plain`")
}
fn visit_str<E>(self, mime: &str) -> Result<ContentType, E>
where
E: de::Error,
{
match ContentType::parse(mime) {
Ok(content_type) => Ok(content_type),
Err(_) => Err(E::custom(format!(
"Couldn't parse the following MIME-Type: {mime}"
))),
}
}
}
deserializer.deserialize_str(ContentTypeVisitor)
}
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use super::ContentType;
use crate::message::header::{HeaderName, HeaderValue, Headers};
#[test]
fn format_content_type() {
let mut headers = Headers::new();
headers.set(ContentType::TEXT_PLAIN);
assert_eq!(
headers.to_string(),
"Content-Type: text/plain; charset=utf-8\r\n"
);
headers.set(ContentType::TEXT_HTML);
assert_eq!(
headers.to_string(),
"Content-Type: text/html; charset=utf-8\r\n"
);
}
#[test]
fn parse_content_type() {
let mut headers = Headers::new();
headers.insert_raw(HeaderValue::new(
HeaderName::new_from_ascii_str("Content-Type"),
"text/plain; charset=utf-8".to_owned(),
));
assert_eq!(headers.get::<ContentType>(), Some(ContentType::TEXT_PLAIN));
headers.insert_raw(HeaderValue::new(
HeaderName::new_from_ascii_str("Content-Type"),
"text/html; charset=utf-8".to_owned(),
));
assert_eq!(headers.get::<ContentType>(), Some(ContentType::TEXT_HTML));
}
}