Skip to main content

email_message/
lib.rs

1//! Core email message model and parsing primitives.
2//!
3//! Quick examples:
4//!
5//! ```rust
6//! use email_message::{Mailbox, MailboxList};
7//!
8//! let mailbox: Mailbox = "Mary Smith <mary@x.test>".parse().unwrap();
9//! assert_eq!(mailbox.name(), Some("Mary Smith"));
10//! assert_eq!(mailbox.email().as_str(), "mary@x.test");
11//!
12//! let list: MailboxList = "mary@x.test, jdoe@one.test".parse().unwrap();
13//! assert_eq!(list.len(), 2);
14//! ```
15
16pub mod address;
17pub mod email;
18
19pub use address::{
20    Address, AddressBackendError, AddressList, AddressParseError, Group, GroupParseError, Mailbox,
21    MailboxList, MailboxParseError, ParseError,
22};
23pub use email::{Email, EmailParseError};
24
25#[derive(Clone, Debug, PartialEq, Eq, Hash)]
26pub struct Envelope {
27    pub mail_from: Option<Mailbox>,
28    pub rcpt_to: Vec<Mailbox>,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct Header {
33    pub name: String,
34    pub value: String,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq)]
38#[non_exhaustive]
39pub enum AttachmentBody {
40    Bytes(Vec<u8>),
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct Attachment {
45    pub filename: Option<String>,
46    pub content_type: String,
47    pub content_id: Option<String>,
48    pub inline: bool,
49    pub body: AttachmentBody,
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum Body {
54    Text(String),
55    Html(String),
56    TextAndHtml { text: String, html: String },
57    Mime(MimePart),
58    RawRfc822(Vec<u8>),
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum MimePart {
63    Leaf {
64        content_type: String,
65        content_transfer_encoding: Option<String>,
66        content_disposition: Option<String>,
67        body: Vec<u8>,
68    },
69    Multipart {
70        content_type: String,
71        boundary: Option<String>,
72        parts: Vec<MimePart>,
73    },
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
77pub struct Message {
78    pub from: Option<Mailbox>,
79    pub sender: Option<Mailbox>,
80    pub to: Vec<Address>,
81    pub cc: Vec<Address>,
82    pub bcc: Vec<Address>,
83    pub reply_to: Vec<Address>,
84    pub subject: Option<String>,
85    pub date: Option<String>,
86    pub message_id: Option<String>,
87    pub headers: Vec<Header>,
88    pub body: Body,
89    pub attachments: Vec<Attachment>,
90}
91
92#[derive(Clone, Debug, PartialEq, Eq)]
93pub enum MessageValidationError {
94    MissingFrom,
95    MissingRecipients,
96}
97
98impl Display for MessageValidationError {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        match self {
101            Self::MissingFrom => f.write_str("missing From header"),
102            Self::MissingRecipients => f.write_str("no recipients in To/Cc/Bcc"),
103        }
104    }
105}
106
107impl std::error::Error for MessageValidationError {}
108
109impl Message {
110    pub fn validate_basic(&self) -> Result<(), MessageValidationError> {
111        if self.from.is_none() {
112            return Err(MessageValidationError::MissingFrom);
113        }
114
115        if self.to.is_empty() && self.cc.is_empty() && self.bcc.is_empty() {
116            return Err(MessageValidationError::MissingRecipients);
117        }
118
119        Ok(())
120    }
121}
122use std::fmt::Display;