kumo_dkim/
parsed_email.rs

1use 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}