1use nom::combinator::map;
2
3use crate::header;
4use crate::imf::identification::{msg_id, MessageID};
5use crate::mime::mechanism::{mechanism, Mechanism};
6use crate::mime::r#type::{naive_type, NaiveType};
7use crate::text::misc_token::{unstructured, Unstructured};
8
9#[derive(Debug, PartialEq)]
10pub enum Content<'a> {
11 Type(NaiveType<'a>),
12 TransferEncoding(Mechanism<'a>),
13 ID(MessageID<'a>),
14 Description(Unstructured<'a>),
15}
16#[allow(dead_code)]
17impl<'a> Content<'a> {
18 pub fn ctype(&'a self) -> Option<&'a NaiveType<'a>> {
19 match self {
20 Content::Type(v) => Some(v),
21 _ => None,
22 }
23 }
24 pub fn transfer_encoding(&'a self) -> Option<&'a Mechanism<'a>> {
25 match self {
26 Content::TransferEncoding(v) => Some(v),
27 _ => None,
28 }
29 }
30 pub fn id(&'a self) -> Option<&'a MessageID<'a>> {
31 match self {
32 Content::ID(v) => Some(v),
33 _ => None,
34 }
35 }
36 pub fn description(&'a self) -> Option<&'a Unstructured<'a>> {
37 match self {
38 Content::Description(v) => Some(v),
39 _ => None,
40 }
41 }
42}
43
44impl<'a> TryFrom<&header::Field<'a>> for Content<'a> {
45 type Error = ();
46 fn try_from(f: &header::Field<'a>) -> Result<Self, Self::Error> {
47 let content = match f {
48 header::Field::Good(header::Kv2(key, value)) => match key
49 .to_ascii_lowercase()
50 .as_slice()
51 {
52 b"content-type" => map(naive_type, Content::Type)(value),
53 b"content-transfer-encoding" => map(mechanism, Content::TransferEncoding)(value),
54 b"content-id" => map(msg_id, Content::ID)(value),
55 b"content-description" => map(unstructured, Content::Description)(value),
56 _ => return Err(()),
57 },
58 _ => return Err(()),
59 };
60
61 content.map(|(_, content)| content).or(Err(()))
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::header;
70 use crate::mime::r#type::*;
72 use crate::text::misc_token::MIMEWord;
73 use crate::text::quoted::QuotedString;
74
75 #[test]
96 fn test_header() {
97 let fullmail: &[u8] = r#"Date: Sat, 8 Jul 2023 07:14:29 +0200
98From: Grrrnd Zero <grrrndzero@example.org>
99To: John Doe <jdoe@machine.example>
100Subject: Re: Saying Hello
101Message-ID: <NTAxNzA2AC47634Y366BAMTY4ODc5MzQyODY0ODY5@www.grrrndzero.org>
102MIME-Version: 1.0
103Content-Type: multipart/alternative;
104 boundary="b1_e376dc71bafc953c0b0fdeb9983a9956"
105Content-Transfer-Encoding: 7bit
106
107This is a multipart message.
108
109"#
110 .as_bytes();
111
112 assert_eq!(
113 map(header::header_kv, |k| k
114 .iter()
115 .flat_map(Content::try_from)
116 .collect())(fullmail),
117 Ok((
118 &b"This is a multipart message.\n\n"[..],
119 vec![
120 Content::Type(NaiveType {
121 main: &b"multipart"[..],
122 sub: &b"alternative"[..],
123 params: vec![Parameter {
124 name: &b"boundary"[..],
125 value: MIMEWord::Quoted(QuotedString(vec![
126 &b"b1_e376dc71bafc953c0b0fdeb9983a9956"[..]
127 ])),
128 }]
129 }),
130 Content::TransferEncoding(Mechanism::_7Bit),
131 ],
132 )),
133 );
134 }
135}