use crate::{Error, Result};
use std::collections::HashMap;
mod parsers;
use parsers::get_header;
pub use parsers::{Segment, Subject};
#[doc(hidden)]
pub type HeaderMap = HashMap<String, String>;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Id(pub String);
impl Id {
pub fn from(headers: &HeaderMap, key: &str) -> Result<Self> {
get_header(&headers, key).and_then(|h| match h {
Header::Single(h) => Ok(Self(h)),
_ => Err(Error::FailedParsing),
})
}
}
pub(crate) enum Header {
Single(String),
Multi(Vec<String>),
}
impl Header {
fn single(self) -> Result<String> {
match self {
Self::Single(s) => Ok(s),
Self::Multi(_) => Err(Error::FailedParsing),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Patch<'mail> {
pub id: Id,
pub reply_to: Option<Id>,
pub subject: Subject,
pub headers: HeaderMap,
pub raw: &'mail str,
}
impl<'mail> Patch<'mail> {
#[doc(hidden)]
pub fn preprocess(raw: &'mail str) -> HeaderMap {
let mail = mailparse::parse_mail(raw.as_bytes())
.map_err(|_| Error::FailedParsing)
.unwrap();
mail
.headers
.into_iter()
.fold(HashMap::new(), |mut acc, header| {
let key = header.get_key().unwrap();
let val = header.get_value().unwrap();
acc.insert(key, val);
acc
})
}
pub fn new(raw: &'mail str) -> Result<Self> {
let mail = mailparse::parse_mail(raw.as_bytes())
.map_err(|_| Error::FailedParsing)?;
let headers =
mail
.headers
.into_iter()
.fold(HashMap::new(), |mut acc, header| {
let key = header.get_key().unwrap();
let val = header.get_value().unwrap();
acc.insert(key, val);
acc
});
get_header(&headers, "X-Mailer").and_then(|h| match h {
Header::Single(s) if s.contains("git-send-email") => Ok(()),
_ => Err(Error::NotAGitMail),
})?;
Ok(Self {
id: Id::from(&headers, "Message-Id")?,
reply_to: Id::from(&headers, "In-Reply-To")
.map(|id| Some(id))
.unwrap_or(None),
subject: get_header(&headers, "Subject")
.and_then(|h| h.single())
.and_then(|s| Subject::from(s))?,
headers,
raw,
})
}
}