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#[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}