kumo_dkim/
parsed_email.rs1use crate::DKIMError;
2use mailparsing::{
3 Header, HeaderMap, HeaderParseResult, MessageConformance, MimePart, SharedString,
4};
5
6pub enum ParsedEmail<'a> {
7 FullyParsed(MimePart<'a>),
8 HeaderOnlyParse {
9 parsed: HeaderParseResult<'a>,
10 bytes: SharedString<'a>,
11 },
12}
13
14impl<'a> TryFrom<MimePart<'a>> for ParsedEmail<'a> {
15 type Error = DKIMError;
16 fn try_from(mail: MimePart<'a>) -> Result<Self, DKIMError> {
17 if mail
18 .conformance()
19 .contains(MessageConformance::NON_CANONICAL_LINE_ENDINGS)
20 {
21 return Err(DKIMError::CanonicalLineEndingsRequired);
22 }
23 Ok(Self::FullyParsed(mail))
24 }
25}
26
27impl<'a> ParsedEmail<'a> {
28 pub fn parse<S: Into<SharedString<'a>>>(bytes: S) -> Result<Self, DKIMError> {
29 let bytes: SharedString = bytes.into();
30 let parsed = Header::parse_headers(bytes.clone())?;
31 if parsed
32 .overall_conformance
33 .contains(MessageConformance::NON_CANONICAL_LINE_ENDINGS)
34 {
35 return Err(DKIMError::CanonicalLineEndingsRequired);
36 }
37 Ok(Self::HeaderOnlyParse { parsed, bytes })
38 }
39
40 pub fn get_body(&'a self) -> SharedString<'a> {
41 match self {
42 Self::FullyParsed(email) => email.raw_body(),
43 Self::HeaderOnlyParse { bytes, parsed } => bytes.slice(parsed.body_offset..bytes.len()),
44 }
45 }
46
47 pub fn get_headers(&'a self) -> &'a HeaderMap<'a> {
48 match self {
49 Self::FullyParsed(email) => email.headers(),
50 Self::HeaderOnlyParse { parsed, .. } => &parsed.headers,
51 }
52 }
53}