neo_email/
mail.rs

1use std::str::from_utf8;
2
3use crate::errors::SMTPError;
4
5use super::headers::EmailHeaders;
6use hashbrown::HashMap;
7
8/// # Mail
9///
10/// This struct represents an email message.
11///
12/// ## Fields
13///
14/// * `headers` - A HashMap of EmailHeaders and its values.
15/// * `body` - The body of the email.
16/// 
17/// ## Example
18/// 
19/// ```rust
20/// use neo_email::mail::Mail;
21/// 
22/// let raw_email = b"From: jean@nervio\nSubject: Hello\n\nHello, World!";
23/// let mail = Mail::<Vec<u8>>::from_bytes(raw_email.to_vec()).unwrap();
24/// ```
25#[derive(Debug, PartialEq, Eq)]
26pub struct Mail<T> {
27    /// # Headers
28    ///
29    /// A HashMap of EmailHeaders and its values.
30    ///
31    /// ## Example
32    ///
33    /// `From -> "jean@nervio.us"`
34    pub headers: HashMap<EmailHeaders, String>,
35    /// # Body
36    ///
37    /// The body of the email.
38    pub body: T,
39}
40
41impl<T> Mail<T> {
42    /// # From Bytes
43    /// 
44    /// This function creates a new Mail from bytes.
45    /// 
46    /// ## Example
47    /// 
48    /// ```rust
49    /// use neo_email::mail::Mail;
50    /// 
51    /// let raw_email = b"From: jean@nervio\nSubject: Hello\n\nHello, World!";
52    /// let mail = Mail::<Vec<u8>>::from_bytes(raw_email.to_vec()).unwrap();
53    /// ```
54    pub fn from_bytes(bytes: Vec<u8>) -> Result<Mail<T>, String>
55    where
56        T: From<Vec<u8>>,
57    {
58        let mut headers = HashMap::new();
59        let mut body = Vec::new();
60        let mut lines = bytes.split(|&b| b == b'\n').peekable();
61        let mut header_complete = false;
62
63        while let Some(line) = lines.next() {
64            if line.is_empty() || line == b"\r" {
65                header_complete = true;
66                break;
67            }
68
69            if let Some(&b' ') | Some(&b'\t') = line.first() {
70                if let Some(last_header) = headers.keys().last().cloned() {
71                    let value: &mut String = headers.get_mut(&last_header).unwrap();
72                    value.push_str(from_utf8(line).map_err(|_| "Invalid header value")?);
73                    continue;
74                }
75            }
76
77            let mut parts = line.splitn(2, |&b| b == b':');
78            let key = parts.next().ok_or("Invalid header")?;
79            let value = parts.next().ok_or("Invalid header value not exist")?;
80            let value = from_utf8(value).map_err(|_| "Invalid header value")?.trim();
81            let value = value.split_whitespace().collect::<Vec<&str>>().join(" ");
82
83            headers.insert(EmailHeaders::from_bytes(key)?, value.to_owned());
84        }
85
86        if header_complete {
87            for line in lines {
88                body.extend_from_slice(line);
89                body.push(b'\n');
90            }
91        } else {
92            return Err("Invalid mail format".to_string());
93        }
94
95        Ok(Mail {
96            headers,
97            body: body.into(),
98        })
99    }
100}
101
102/// # Mail Trait
103/// 
104/// This trait is implemented by Mail and is used to downcast the Mail struct.
105pub trait MailTrait: Send + Sync + 'static {
106    /// # As Any
107    /// 
108    /// This function returns a reference to the dyn Any trait.
109    fn as_any(&self) -> &dyn std::any::Any;
110}
111
112impl<T: Send + Sync + 'static> MailTrait for Mail<T> {
113    fn as_any(&self) -> &dyn std::any::Any {
114        self
115    }
116}
117
118impl<T: Clone + Send + Sync + 'static> Clone for Mail<T> {
119    fn clone(&self) -> Self {
120        Mail {
121            headers: self.headers.clone(),
122            body: self.body.clone(),
123        }
124    }
125}
126
127/// # Email Address
128/// 
129/// This struct represents an email address.
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131pub struct EmailAddress {
132    /// # Username
133    /// 
134    /// The username of the email address.
135    /// 
136    /// ## Example
137    /// 
138    /// `jean`
139    pub username: String,
140    /// # Domain
141    /// 
142    /// The domain of the email address.
143    /// 
144    /// ## Example
145    pub domain: String,
146}
147
148impl EmailAddress {
149    /// # From String
150    /// 
151    /// This function creates a new EmailAddress from a string.
152    pub fn from_string(data: &str) -> Result<Self, SMTPError> {
153        let mut parts = data.split('@');
154        let username = parts
155            .next()
156            .ok_or(SMTPError::ParseError("Invalid email address".to_string()))?
157            .to_owned();
158        let domain = parts
159            .next()
160            .ok_or(SMTPError::ParseError("Invalid email address".to_string()))?
161            .to_owned();
162
163        if domain.is_empty() {
164            return Err(SMTPError::ParseError("Invalid email address".to_string()));
165        }
166
167        if domain.len() > 253 {
168            return Err(SMTPError::ParseError("Invalid email address".to_string()));
169        }
170
171        Ok(EmailAddress { username, domain })
172    }
173
174    /// # To String
175    /// 
176    /// This function converts EmailAdress to a String.
177    pub fn to_string(&self) -> String {
178        format!("{}@{}", self.username, self.domain)
179    }
180}