1use super::{AddressList, ContentType, Received, Resolver};
8use crate::{
9 DateTime, HeaderForm, HeaderName, ParsedValue,
10 header_name::{self, HeaderId, HeaderKey},
11 store::{HeaderEntry, MessageData, NONE, OTHER_NAME, Str, Value},
12 thread_name,
13};
14use std::{borrow::Cow, fmt, iter::FusedIterator, slice};
15
16#[derive(Clone, Copy)]
34pub struct Headers<'m> {
35 resolver: Resolver<'m>,
36 data: &'m MessageData,
37 entries: &'m [HeaderEntry],
38}
39
40#[derive(Clone, Copy)]
43pub struct Header<'m> {
44 resolver: Resolver<'m>,
45 data: &'m MessageData,
46 entry: &'m HeaderEntry,
47}
48
49#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
53pub enum HeaderValue<'m> {
54 #[default]
58 Empty,
59 Text(&'m str),
63 TextList(TextList<'m>),
66 Address(AddressList<'m>),
68 DateTime(DateTime),
70 ContentType(ContentType<'m>),
73 Received(Received<'m>),
75}
76
77#[derive(Clone)]
79pub struct NamedHeaders<'m, 'n> {
80 headers: Headers<'m>,
81 rest: &'m [HeaderEntry],
82 name: HeaderName<'n>,
83 id: u16,
84}
85
86impl<'m> Iterator for NamedHeaders<'m, '_> {
87 type Item = Header<'m>;
88
89 #[inline]
90 fn next(&mut self) -> Option<Header<'m>> {
91 while let Some((entry, rest)) = self.rest.split_first() {
92 self.rest = rest;
93 if self.headers.matches(entry, self.name.as_str(), self.id) {
94 return Some(self.headers.header(entry));
95 }
96 }
97 None
98 }
99}
100
101#[derive(Clone)]
104pub struct HeaderIter<'m> {
105 headers: Headers<'m>,
106 entries: slice::Iter<'m, HeaderEntry>,
107}
108
109impl<'m> Iterator for HeaderIter<'m> {
110 type Item = Header<'m>;
111
112 fn next(&mut self) -> Option<Header<'m>> {
113 self.entries.next().map(|entry| self.headers.header(entry))
114 }
115
116 fn size_hint(&self) -> (usize, Option<usize>) {
117 self.entries.size_hint()
118 }
119}
120
121impl<'m> DoubleEndedIterator for HeaderIter<'m> {
122 fn next_back(&mut self) -> Option<Header<'m>> {
123 self.entries
124 .next_back()
125 .map(|entry| self.headers.header(entry))
126 }
127}
128
129impl ExactSizeIterator for HeaderIter<'_> {}
130
131impl FusedIterator for HeaderIter<'_> {}
132
133impl<'m> IntoIterator for Headers<'m> {
134 type Item = Header<'m>;
135 type IntoIter = HeaderIter<'m>;
136
137 fn into_iter(self) -> HeaderIter<'m> {
138 self.iter()
139 }
140}
141
142impl<'m> IntoIterator for &Headers<'m> {
143 type Item = Header<'m>;
144 type IntoIter = HeaderIter<'m>;
145
146 fn into_iter(self) -> HeaderIter<'m> {
147 self.iter()
148 }
149}
150
151#[derive(Clone, Copy)]
154pub struct TextList<'m> {
155 resolver: Resolver<'m>,
156 items: &'m [Str],
157}
158
159impl<'m> Headers<'m> {
160 pub(crate) fn new(
161 resolver: Resolver<'m>,
162 data: &'m MessageData,
163 entries: &'m [HeaderEntry],
164 ) -> Self {
165 Headers {
166 resolver,
167 data,
168 entries,
169 }
170 }
171
172 #[inline]
173 fn header(&self, entry: &'m HeaderEntry) -> Header<'m> {
174 Header {
175 resolver: self.resolver,
176 data: self.data,
177 entry,
178 }
179 }
180
181 pub fn len(&self) -> usize {
183 self.entries.len()
184 }
185
186 pub fn is_empty(&self) -> bool {
188 self.entries.is_empty()
189 }
190
191 pub fn iter(&self) -> HeaderIter<'m> {
194 HeaderIter {
195 headers: *self,
196 entries: self.entries.iter(),
197 }
198 }
199
200 #[inline]
201 fn matches(&self, entry: &HeaderEntry, name: &str, id: u16) -> bool {
202 if id != OTHER_NAME {
203 entry.name == id
204 } else {
205 entry.name == OTHER_NAME
206 && self
207 .resolver
208 .raw_header_name(self.data, entry)
209 .eq_ignore_ascii_case(name)
210 }
211 }
212
213 pub fn get<'n>(&self, name: impl Into<HeaderName<'n>>) -> Option<Header<'m>> {
216 let name = name.into();
217 self.find_last(name.as_str(), name.resolved_id())
218 }
219
220 #[inline]
222 pub fn get_key(&self, key: HeaderKey<'_>) -> Option<Header<'m>> {
223 self.find_last(key.as_str(), key.id())
224 }
225
226 #[inline]
227 fn find_last(&self, name: &str, id: u16) -> Option<Header<'m>> {
228 match id {
229 OTHER_NAME => self
230 .entries
231 .iter()
232 .rev()
233 .find(|entry| self.matches(entry, name, OTHER_NAME))
234 .map(|entry| self.header(entry)),
235 id => self.last_known(id),
236 }
237 }
238
239 #[inline]
240 fn last_known(&self, id: u16) -> Option<Header<'m>> {
241 self.entries
242 .iter()
243 .rev()
244 .find(|entry| entry.name == id)
245 .map(|entry| self.header(entry))
246 }
247
248 #[inline]
249 fn known_value(&self, id: HeaderId) -> Option<HeaderValue<'m>> {
250 self.last_known(id as u16).map(|header| header.value())
251 }
252
253 #[inline]
256 pub fn all<'n>(&self, name: impl Into<HeaderName<'n>>) -> NamedHeaders<'m, 'n> {
257 let name = name.into();
258 NamedHeaders {
259 headers: *self,
260 rest: self.entries,
261 id: name.resolved_id(),
262 name,
263 }
264 }
265
266 #[inline]
269 pub fn all_key<'k>(&self, key: HeaderKey<'k>) -> NamedHeaders<'m, 'k> {
270 NamedHeaders {
271 headers: *self,
272 rest: self.entries,
273 id: key.id(),
274 name: HeaderName::Other(Cow::Borrowed(key.as_str())),
275 }
276 }
277
278 pub fn contains<'n>(&self, name: impl Into<HeaderName<'n>>) -> bool {
280 self.get(name).is_some()
281 }
282
283 pub fn has_known(&self) -> bool {
286 self.entries.iter().any(|entry| entry.name != OTHER_NAME)
287 }
288
289 pub fn value<'n>(&self, name: impl Into<HeaderName<'n>>) -> Option<HeaderValue<'m>> {
291 self.get(name).map(|header| header.value())
292 }
293
294 fn text(&self, id: HeaderId) -> Option<&'m str> {
295 self.known_value(id)?.as_text()
296 }
297
298 fn address(&self, id: HeaderId) -> Option<AddressList<'m>> {
299 self.known_value(id)?.as_address()
300 }
301
302 fn text_list(&self, id: HeaderId) -> Option<TextList<'m>> {
303 self.known_value(id)?.as_text_list()
304 }
305
306 fn all_addresses(
307 &self,
308 name: HeaderName<'static>,
309 ) -> impl Iterator<Item = AddressList<'m>> + use<'m> {
310 self.all(name)
311 .filter_map(|header| header.value().as_address())
312 }
313
314 pub fn subject(&self) -> Option<&'m str> {
316 self.text(HeaderId::Subject)
317 }
318
319 pub fn comments(&self) -> Option<&'m str> {
321 self.text(HeaderId::Comments)
322 }
323
324 pub fn mime_version(&self) -> Option<&'m str> {
326 self.text(HeaderId::MimeVersion)
327 }
328
329 pub fn thread_name(&self) -> Option<&'m str> {
332 self.subject().map(thread_name)
333 }
334
335 pub fn from(&self) -> Option<AddressList<'m>> {
337 self.address(HeaderId::From)
338 }
339
340 pub fn to(&self) -> Option<AddressList<'m>> {
342 self.address(HeaderId::To)
343 }
344
345 pub fn cc(&self) -> Option<AddressList<'m>> {
347 self.address(HeaderId::Cc)
348 }
349
350 pub fn bcc(&self) -> Option<AddressList<'m>> {
352 self.address(HeaderId::Bcc)
353 }
354
355 pub fn reply_to(&self) -> Option<AddressList<'m>> {
357 self.address(HeaderId::ReplyTo)
358 }
359
360 pub fn sender(&self) -> Option<AddressList<'m>> {
362 self.address(HeaderId::Sender)
363 }
364
365 pub fn all_from(&self) -> impl Iterator<Item = AddressList<'m>> + use<'m> {
367 self.all_addresses(HeaderName::From)
368 }
369
370 pub fn all_to(&self) -> impl Iterator<Item = AddressList<'m>> + use<'m> {
372 self.all_addresses(HeaderName::To)
373 }
374
375 pub fn all_cc(&self) -> impl Iterator<Item = AddressList<'m>> + use<'m> {
377 self.all_addresses(HeaderName::Cc)
378 }
379
380 pub fn all_bcc(&self) -> impl Iterator<Item = AddressList<'m>> + use<'m> {
382 self.all_addresses(HeaderName::Bcc)
383 }
384
385 pub fn resent_to(&self) -> Option<AddressList<'m>> {
387 self.address(HeaderId::ResentTo)
388 }
389
390 pub fn resent_from(&self) -> Option<AddressList<'m>> {
392 self.address(HeaderId::ResentFrom)
393 }
394
395 pub fn resent_cc(&self) -> Option<AddressList<'m>> {
397 self.address(HeaderId::ResentCc)
398 }
399
400 pub fn resent_bcc(&self) -> Option<AddressList<'m>> {
402 self.address(HeaderId::ResentBcc)
403 }
404
405 pub fn resent_sender(&self) -> Option<AddressList<'m>> {
407 self.address(HeaderId::ResentSender)
408 }
409
410 pub fn list_archive(&self) -> Option<AddressList<'m>> {
413 self.address(HeaderId::ListArchive)
414 }
415
416 pub fn list_help(&self) -> Option<AddressList<'m>> {
419 self.address(HeaderId::ListHelp)
420 }
421
422 pub fn list_id(&self) -> Option<AddressList<'m>> {
425 self.address(HeaderId::ListId)
426 }
427
428 pub fn list_owner(&self) -> Option<AddressList<'m>> {
431 self.address(HeaderId::ListOwner)
432 }
433
434 pub fn list_post(&self) -> Option<AddressList<'m>> {
437 self.address(HeaderId::ListPost)
438 }
439
440 pub fn list_subscribe(&self) -> Option<AddressList<'m>> {
443 self.address(HeaderId::ListSubscribe)
444 }
445
446 pub fn list_unsubscribe(&self) -> Option<AddressList<'m>> {
449 self.address(HeaderId::ListUnsubscribe)
450 }
451
452 pub fn date(&self) -> Option<DateTime> {
454 self.known_value(HeaderId::Date)?.as_datetime()
455 }
456
457 pub fn resent_date(&self) -> Option<DateTime> {
459 self.known_value(HeaderId::ResentDate)?.as_datetime()
460 }
461
462 pub fn message_id(&self) -> Option<&'m str> {
464 self.known_value(HeaderId::MessageId)?.as_text()
465 }
466
467 pub fn in_reply_to(&self) -> Option<TextList<'m>> {
469 self.text_list(HeaderId::InReplyTo)
470 }
471
472 pub fn references(&self) -> Option<TextList<'m>> {
474 self.text_list(HeaderId::References)
475 }
476
477 pub fn resent_message_id(&self) -> Option<TextList<'m>> {
479 self.text_list(HeaderId::ResentMessageId)
480 }
481
482 pub fn return_path(&self) -> Option<TextList<'m>> {
485 self.text_list(HeaderId::ReturnPath)
486 }
487
488 pub fn keywords(&self) -> Option<TextList<'m>> {
490 self.text_list(HeaderId::Keywords)
491 }
492
493 pub fn return_address(&self) -> Option<&'m str> {
495 match self.known_value(HeaderId::ReturnPath) {
496 Some(value @ (HeaderValue::Text(_) | HeaderValue::TextList(_))) => value.as_text(),
497 _ => self.from()?.first()?.address(),
498 }
499 }
500
501 pub fn received(&self) -> Option<Received<'m>> {
503 self.known_value(HeaderId::Received)?.as_received()
504 }
505
506 pub fn all_received(&self) -> impl Iterator<Item = Received<'m>> + use<'m> {
508 self.all(HeaderName::Received)
509 .filter_map(|header| header.value().as_received())
510 }
511}
512
513impl<'m> Resolver<'m> {
514 fn raw_header_name(self, data: &'m MessageData, entry: &HeaderEntry) -> &'m str {
515 if entry.other_name != NONE {
516 return data
517 .text_items
518 .get(entry.other_name as usize)
519 .map_or("", |text| self.str(*text));
520 }
521 let start = entry.offset_field as usize;
522 let region = self
523 .source()
524 .get(start..(entry.offset_start as usize).saturating_sub(1))
525 .unwrap_or_default();
526 let canonical = header_name::canonical(entry.name);
527 if region == canonical.as_bytes() {
528 return canonical;
529 }
530 let name = header_name::trim_blank_end(region);
531 self.borrow(start..start + name.len()).unwrap_or_default()
532 }
533}
534
535impl<'m> Header<'m> {
536 #[inline]
538 pub fn name(&self) -> HeaderName<'m> {
539 match header_name::known(self.entry.name) {
540 Some(known) => known.clone(),
541 None => HeaderName::Other(Cow::Borrowed(self.raw_name())),
542 }
543 }
544
545 pub fn raw_name(&self) -> &'m str {
547 self.resolver.raw_header_name(self.data, self.entry)
548 }
549
550 #[inline]
552 pub fn value(&self) -> HeaderValue<'m> {
553 HeaderValue::new(self.resolver, self.data, self.entry.value)
554 }
555
556 pub fn raw_value(&self) -> &'m [u8] {
559 self.resolver
560 .source()
561 .get(self.entry.offset_start as usize..self.entry.offset_end as usize)
562 .unwrap_or_default()
563 }
564
565 pub fn parse_as(&self, form: HeaderForm) -> ParsedValue<'m> {
579 ParsedValue::new(
580 form,
581 self.resolver.source(),
582 self.entry.offset_start as usize..self.entry.offset_end as usize,
583 )
584 }
585
586 pub fn offset_field(&self) -> u32 {
588 self.entry.offset_field
589 }
590
591 pub fn offset_start(&self) -> u32 {
593 self.entry.offset_start
594 }
595
596 pub fn offset_end(&self) -> u32 {
600 self.entry.offset_end
601 }
602}
603
604impl<'m> HeaderValue<'m> {
605 #[inline]
606 pub(crate) fn new(resolver: Resolver<'m>, data: &'m MessageData, value: Value) -> Self {
607 match value {
608 Value::Empty => HeaderValue::Empty,
609 Value::Text(text) => HeaderValue::Text(resolver.str(text)),
610 Value::TextList(span) => HeaderValue::TextList(TextList {
611 resolver,
612 items: data.text_items.get(span.range()).unwrap_or_default(),
613 }),
614 Value::Address(span) => HeaderValue::Address(AddressList::new(
615 resolver,
616 data.address_items.get(span.range()).unwrap_or_default(),
617 )),
618 Value::DateTime(date) => HeaderValue::DateTime(date),
619 Value::ContentType(index) => data
620 .content_types
621 .get(index as usize)
622 .map_or(HeaderValue::Empty, |entry| {
623 HeaderValue::ContentType(ContentType::new(resolver, data, entry))
624 }),
625 Value::Received(index) => data
626 .received
627 .get(index as usize)
628 .map_or(HeaderValue::Empty, |entry| {
629 HeaderValue::Received(Received::new(resolver, entry))
630 }),
631 }
632 }
633
634 pub fn is_empty(&self) -> bool {
636 matches!(self, HeaderValue::Empty)
637 }
638
639 pub fn as_text(&self) -> Option<&'m str> {
642 match self {
643 HeaderValue::Text(text) => Some(text),
644 HeaderValue::TextList(list) => list.last(),
645 _ => None,
646 }
647 }
648
649 pub fn as_text_list(&self) -> Option<TextList<'m>> {
651 match self {
652 HeaderValue::TextList(list) => Some(*list),
653 _ => None,
654 }
655 }
656
657 pub fn as_address(&self) -> Option<AddressList<'m>> {
659 match self {
660 HeaderValue::Address(list) => Some(*list),
661 _ => None,
662 }
663 }
664
665 pub fn as_datetime(&self) -> Option<DateTime> {
667 match self {
668 HeaderValue::DateTime(date) => Some(*date),
669 _ => None,
670 }
671 }
672
673 pub fn as_content_type(&self) -> Option<ContentType<'m>> {
675 match self {
676 HeaderValue::ContentType(content_type) => Some(*content_type),
677 _ => None,
678 }
679 }
680
681 pub fn as_received(&self) -> Option<Received<'m>> {
683 match self {
684 HeaderValue::Received(received) => Some(*received),
685 _ => None,
686 }
687 }
688}
689
690impl<'m> TextList<'m> {
691 pub fn len(&self) -> usize {
693 self.items.len()
694 }
695
696 pub fn is_empty(&self) -> bool {
698 self.items.is_empty()
699 }
700
701 pub fn get(&self, index: usize) -> Option<&'m str> {
703 self.items.get(index).map(|item| self.resolver.str(*item))
704 }
705
706 pub fn first(&self) -> Option<&'m str> {
708 self.items.first().map(|item| self.resolver.str(*item))
709 }
710
711 pub fn last(&self) -> Option<&'m str> {
713 self.items.last().map(|item| self.resolver.str(*item))
714 }
715
716 pub fn iter(&self) -> impl ExactSizeIterator<Item = &'m str> + DoubleEndedIterator + use<'m> {
718 let resolver = self.resolver;
719 self.items.iter().map(move |item| resolver.str(*item))
720 }
721
722 pub fn contains(&self, text: &str) -> bool {
724 self.iter().any(|item| item == text)
725 }
726}
727
728impl fmt::Debug for Headers<'_> {
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 f.debug_list().entries(self.iter()).finish()
731 }
732}
733
734impl fmt::Debug for Header<'_> {
735 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
736 f.debug_struct("Header")
737 .field("name", &self.raw_name())
738 .field("value", &self.value())
739 .field("offset_field", &self.offset_field())
740 .field("offset_start", &self.offset_start())
741 .field("offset_end", &self.offset_end())
742 .finish()
743 }
744}
745
746impl fmt::Debug for HeaderIter<'_> {
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 f.debug_struct("HeaderIter")
749 .field("remaining", &self.entries.len())
750 .finish_non_exhaustive()
751 }
752}
753
754impl fmt::Debug for NamedHeaders<'_, '_> {
755 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756 f.debug_struct("NamedHeaders")
757 .field("name", &self.name)
758 .finish_non_exhaustive()
759 }
760}
761
762impl fmt::Debug for TextList<'_> {
763 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
764 f.debug_list().entries(self.iter()).finish()
765 }
766}
767
768impl PartialEq for TextList<'_> {
769 fn eq(&self, other: &Self) -> bool {
770 self.items.len() == other.items.len() && self.iter().eq(other.iter())
771 }
772}
773
774impl Eq for TextList<'_> {}