mailparsing/
headermap.rs

1use crate::{
2    AddressList, AuthenticationResults, Header, MailParsingError, Mailbox, MailboxList, MessageID,
3    MimeParameters, Result, SharedString,
4};
5use chrono::{DateTime, FixedOffset, TimeZone};
6use paste::paste;
7
8/// Represents an ordered list of headers.
9/// Note that there may be multiple headers with the same name.
10/// Derefs to the underlying `Vec<Header>` for mutation,
11/// but provides some accessors for retrieving headers by name.
12#[derive(Debug, Clone, Default, PartialEq)]
13pub struct HeaderMap<'a> {
14    pub(crate) headers: Vec<Header<'a>>,
15}
16
17impl<'a> std::ops::Deref for HeaderMap<'a> {
18    type Target = Vec<Header<'a>>;
19    fn deref(&self) -> &Vec<Header<'a>> {
20        &self.headers
21    }
22}
23
24impl<'a> std::ops::DerefMut for HeaderMap<'a> {
25    fn deref_mut(&mut self) -> &mut Vec<Header<'a>> {
26        &mut self.headers
27    }
28}
29
30pub trait EncodeHeaderValue {
31    fn encode_value(&self) -> SharedString<'static>;
32    fn as_header(&self, _name: &str) -> Option<Header<'static>> {
33        None
34    }
35}
36
37impl EncodeHeaderValue for &str {
38    fn encode_value(&self) -> SharedString<'static> {
39        unimplemented!();
40    }
41
42    fn as_header(&self, name: &str) -> Option<Header<'static>> {
43        Some(Header::new_unstructured(name.to_string(), self.to_string()))
44    }
45}
46
47impl<T: TimeZone> EncodeHeaderValue for DateTime<T>
48where
49    <T as TimeZone>::Offset: std::fmt::Display,
50{
51    fn encode_value(&self) -> SharedString<'static> {
52        (*self).to_rfc2822().into()
53    }
54}
55
56macro_rules! accessor {
57    ($func_name:ident, $header_name:literal, $ty:path, $parser:ident) => {
58        pub fn $func_name(&self) -> Result<Option<$ty>> {
59            match self.get_first($header_name) {
60                None => Ok(None),
61                Some(header) => Ok(Some(header.$parser().map_err(|error| {
62                    MailParsingError::InvalidHeaderValueDuringGet {
63                        header_name: $header_name.to_string(),
64                        error: error.into(),
65                    }
66                })?)),
67            }
68        }
69
70        paste! {
71            pub fn [<set_ $func_name>](&mut self, v: impl EncodeHeaderValue) -> Result<()> {
72                if let Some(idx) = self
73                    .headers
74                    .iter()
75                    .position(|header| header.get_name().eq_ignore_ascii_case($header_name))
76                {
77                    if let Some(hdr) = v.as_header(self.headers[idx].get_name()) {
78                        hdr.$parser().map_err(|error| {
79                            MailParsingError::InvalidHeaderValueDuringAssignment {
80                                header_name: $header_name.to_string(),
81                                error: error.into(),
82                            }
83                        })?;
84                        self.headers[idx] = hdr;
85                    } else {
86                        self.headers[idx].assign(v);
87                    }
88                } else {
89                    if let Some(hdr) = v.as_header($header_name) {
90                        hdr.$parser().map_err(|error| {
91                            MailParsingError::InvalidHeaderValueDuringAssignment {
92                                header_name: $header_name.to_string(),
93                                error: error.into(),
94                            }
95                        })?;
96                        self.headers.push(hdr);
97                    } else {
98                        self.headers
99                            .push(Header::with_name_value($header_name, v.encode_value()));
100                    }
101                }
102                Ok(())
103            }
104        }
105    };
106}
107
108impl<'a> HeaderMap<'a> {
109    pub fn new(headers: Vec<Header<'a>>) -> Self {
110        Self { headers }
111    }
112
113    pub fn to_owned(&'a self) -> HeaderMap<'static> {
114        HeaderMap {
115            headers: self.headers.iter().map(|h: &Header| h.to_owned()).collect(),
116        }
117    }
118
119    pub fn remove_all_named(&mut self, name: &str) {
120        self.headers
121            .retain(|hdr| !hdr.get_name().eq_ignore_ascii_case(name));
122    }
123
124    pub fn prepend<V: Into<SharedString<'a>>>(&mut self, name: &str, v: V) {
125        self.headers
126            .insert(0, Header::new_unstructured(name.to_string(), v));
127    }
128
129    pub fn get_first(&'a self, name: &str) -> Option<&'a Header<'a>> {
130        self.iter_named(name).next()
131    }
132
133    pub fn get_first_mut(&'a mut self, name: &str) -> Option<&'a mut Header<'a>> {
134        self.iter_named_mut(name).next()
135    }
136
137    pub fn get_last(&'a self, name: &str) -> Option<&'a Header<'a>> {
138        self.iter_named(name).next_back()
139    }
140
141    pub fn get_last_mut(&'a mut self, name: &str) -> Option<&'a mut Header<'a>> {
142        self.iter_named_mut(name).next_back()
143    }
144
145    pub fn iter_named<'name>(
146        &'a self,
147        name: &'name str,
148    ) -> impl DoubleEndedIterator<Item = &'a Header<'a>> + 'name
149    where
150        'a: 'name,
151    {
152        self.headers
153            .iter()
154            .filter(|header| header.get_name().eq_ignore_ascii_case(name))
155    }
156
157    pub fn iter_named_mut<'name>(
158        &'a mut self,
159        name: &'name str,
160    ) -> impl DoubleEndedIterator<Item = &'a mut Header<'a>> + 'name
161    where
162        'a: 'name,
163    {
164        self.headers
165            .iter_mut()
166            .filter(|header| header.get_name().eq_ignore_ascii_case(name))
167    }
168
169    accessor!(from, "From", MailboxList, as_mailbox_list);
170    accessor!(resent_from, "Resent-From", MailboxList, as_mailbox_list);
171
172    accessor!(to, "To", AddressList, as_address_list);
173    accessor!(reply_to, "Reply-To", AddressList, as_address_list);
174    accessor!(cc, "Cc", AddressList, as_address_list);
175    accessor!(bcc, "Bcc", AddressList, as_address_list);
176    accessor!(resent_to, "Resent-To", AddressList, as_address_list);
177    accessor!(resent_cc, "Resent-Cc", AddressList, as_address_list);
178    accessor!(resent_bcc, "Resent-Bcc", AddressList, as_address_list);
179
180    accessor!(date, "Date", DateTime<FixedOffset>, as_date);
181
182    accessor!(sender, "Sender", Mailbox, as_mailbox);
183    accessor!(resent_sender, "Resent-Sender", Mailbox, as_mailbox);
184
185    accessor!(message_id, "Message-ID", MessageID, as_message_id);
186    accessor!(content_id, "Content-ID", MessageID, as_content_id);
187    accessor!(references, "References", Vec<MessageID>, as_message_id_list);
188
189    accessor!(subject, "Subject", String, as_unstructured);
190    accessor!(comments, "Comments", String, as_unstructured);
191    accessor!(
192        content_transfer_encoding,
193        "Content-Transfer-Encoding",
194        MimeParameters,
195        as_content_transfer_encoding
196    );
197    accessor!(mime_version, "Mime-Version", String, as_unstructured);
198    accessor!(
199        content_disposition,
200        "Content-Disposition",
201        MimeParameters,
202        as_content_disposition
203    );
204
205    accessor!(
206        content_type,
207        "Content-Type",
208        MimeParameters,
209        as_content_type
210    );
211
212    accessor!(
213        authentication_results,
214        "Authentication-Results",
215        AuthenticationResults,
216        as_authentication_results
217    );
218}