mail_parser/lib.rs
1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7//! Fast and robust e-mail parsing.
8//!
9//! A [`MessageParser`] turns the bytes of an RFC 5322 message into a
10//! read-only [`Message`]: the raw bytes plus a flat, index-based store of
11//! every nested message, MIME part and header field. Everything is read
12//! through small `Copy` views ([`MessageRef`], [`MessagePart`], [`Header`],
13//! [`HeaderValue`], ...), and bodies are decoded on access.
14//!
15//! ```
16//! use mail_parser::MessageParser;
17//!
18//! let raw = b"From: Ann <ann@example.com>\r\nSubject: Hi\r\n\r\nHello!\r\n";
19//! let message = MessageParser::new().parse(raw).expect("message");
20//! assert_eq!(message.subject(), Some("Hi"));
21//! assert_eq!(message.body_text(0).as_deref(), Some("Hello!\r\n"));
22//! ```
23//!
24//! - Parsing: [`MessageParser::parse`], [`MessageParser::parse_owned`],
25//! [`MessageParser::parse_headers`], and [`MessageParser::parse_with`]
26//! with [`MessageBuffers`] to reuse storage from one message to the next.
27//! - Structure: [`Message::parts`] and [`Message::messages`] in document
28//! order, [`MessagePart::children`], [`MessagePart::nested`], the RFC 8621
29//! body lists ([`MessageRef::text_body`], [`MessageRef::html_body`],
30//! [`MessageRef::attachments`]) and MIME boundaries
31//! ([`MessagePart::boundary`], [`Message::part_by_boundary`]).
32//! - Bodies: [`MessagePart::decoded`], [`MessagePart::text`],
33//! [`MessagePart::text_prefix`] and [`MessagePart::decoded_len`];
34//! [`MessagePart::decoded_checked`] and [`MessagePart::text_checked`]
35//! also report decoding problems.
36//! - Header fields: [`Headers`] with typed getters, [`Header::raw_name`]
37//! and [`Header::raw_value`] as written, [`Header::parse_as`] to parse a
38//! field in another form, and [`HeaderForm::parse`] for values on their
39//! own.
40//! - Mailboxes: [`mailbox::mbox`] and [`mailbox::maildir`].
41//! - Utilities: [`DateTime`], [`thread_name`], [`html_to_text`],
42//! [`text_to_html`], [`preview_text`], [`Charset`] and the other
43//! [`decoders`].
44//!
45//! Parsing never fails on malformed input: damaged structure is reported
46//! by [`MessagePart::flags`], malformed encodings by the checked decoding
47//! methods. The `full_encoding` feature adds the multi-byte charsets
48//! (through `encoding_rs`), and the `serde` feature serializes messages.
49
50#![warn(missing_docs)]
51
52mod datetime;
53pub mod decoders;
54mod fields;
55mod header_name;
56pub mod mailbox;
57mod parser;
58#[doc(hidden)]
59pub mod scan;
60mod store;
61mod view;
62
63#[cfg(doctest)]
64#[doc = include_str!("../README.md")]
65pub struct ReadmeDoctests;
66
67pub use datetime::{DOW, DateTime, MONTH};
68pub use decoders::{
69 charsets::Charset,
70 html::{add_html_token, html_to_text, strip_charset_meta, text_to_html},
71 preview::{preview_html, preview_text, truncate_html, truncate_text},
72};
73pub use fields::{
74 HeaderForm, ParsedValue,
75 address::{
76 parse_address_detail_part, parse_address_domain, parse_address_local_part,
77 parse_address_user_part,
78 },
79 thread::thread_name,
80};
81pub use header_name::{HeaderKey, HeaderName};
82pub use parser::MessageParser;
83pub use store::{MessageBuffers, MessageId, PartId};
84pub use view::{
85 Address, AddressList, AddressRun, ContentType, DecodeProblems, Greeting, Group, Header,
86 HeaderIter, HeaderValue, Headers, Host, Mailbox, Message, MessagePart, MessageRef,
87 NamedHeaders, PartFlags, PartKind, PartRole, Protocol, Received, Source, TextList, TlsVersion,
88};
89
90/// Declared Content-Transfer-Encoding of a part. The numeric values (0, 1,
91/// 2) are stable.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
93#[repr(u8)]
94pub enum Encoding {
95 /// No transfer encoding: `7bit`, `8bit`, `binary`, no field, or a value
96 /// the parser does not know.
97 #[default]
98 None = 0,
99 /// `quoted-printable`.
100 QuotedPrintable = 1,
101 /// `base64`.
102 Base64 = 2,
103}
104
105impl From<u8> for Encoding {
106 fn from(value: u8) -> Self {
107 match value {
108 1 => Encoding::QuotedPrintable,
109 2 => Encoding::Base64,
110 _ => Encoding::None,
111 }
112 }
113}
114
115impl From<Encoding> for u8 {
116 fn from(value: Encoding) -> Self {
117 value as u8
118 }
119}