1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
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>;
/// Message ID of an email in a thread
#[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),
})
}
}
/// A semi typed header value for mail
pub(crate) enum Header {
/// A single header value
Single(String),
/// A set of values that was separated by `,`
Multi(Vec<String>),
}
impl Header {
fn single(self) -> Result<String> {
match self {
Self::Single(s) => Ok(s),
Self::Multi(_) => Err(Error::FailedParsing),
}
}
}
/// A single patch, metadata and email body
///
/// This type is constructed from a single email in a thread, and can
/// then be combined into a [PatchSet](struct.PatchSet.html) via the
/// [PatchTree](struct.PatchTree.html) builder.
#[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> {
/// Hidden function that should only be used
#[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,
})
}
}
|