dust_mail/client/
mod.rs

1use std::{collections::HashMap, fmt::Display, sync::Arc};
2
3use crate::{
4    error::{Error, ErrorKind},
5    runtime::thread::RwLock,
6    tree::Node,
7};
8
9#[cfg(feature = "imap")]
10use self::incoming::imap;
11
12#[cfg(feature = "pop")]
13use self::incoming::pop;
14
15#[cfg(feature = "maildir")]
16use self::incoming::maildir;
17
18#[cfg(all(feature = "smtp", feature = "runtime-tokio"))]
19use self::outgoing::smtp;
20
21use self::{
22    incoming::types::{
23        mailbox::Mailbox,
24        message::{Message, Preview},
25    },
26    outgoing::types::sendable::SendableMessage,
27    protocol::{IncomingProtocol, OutgoingProtocol},
28};
29
30pub use self::{
31    keep_alive::KeepAlive,
32    protocol::{Credentials, IncomingEmailProtocol, OutgoingEmailProtocol, ServerCredentials},
33};
34
35use crate::error::Result;
36
37mod incoming;
38mod outgoing;
39
40pub use incoming::types::*;
41pub use outgoing::types::*;
42
43pub mod address;
44pub mod builder;
45pub mod connection;
46pub mod content;
47
48mod parser;
49
50mod protocol;
51
52mod keep_alive;
53
54pub type Headers = HashMap<String, String>;
55
56pub struct EmailClient {
57    incoming: Box<dyn IncomingProtocol + Sync + Send>,
58    outgoing: Box<dyn OutgoingProtocol + Sync + Send>,
59}
60
61impl EmailClient {
62    pub fn new(
63        incoming: Box<dyn IncomingProtocol + Sync + Send>,
64        outgoing: Box<dyn OutgoingProtocol + Sync + Send>,
65    ) -> Self {
66        Self { incoming, outgoing }
67    }
68
69    pub async fn send_keep_alive(&mut self) -> Result<()> {
70        self.incoming.send_keep_alive().await
71    }
72
73    pub fn should_keep_alive(&self) -> bool {
74        self.incoming.should_keep_alive()
75    }
76
77    pub async fn get_mailbox_list(&mut self) -> Result<Node<Mailbox>> {
78        self.incoming.get_mailbox_list().await
79    }
80
81    pub async fn get_mailbox<BoxId: AsRef<str>>(
82        &mut self,
83        mailbox_id: BoxId,
84    ) -> Result<Node<Mailbox>> {
85        self.incoming.get_mailbox(mailbox_id.as_ref()).await
86    }
87
88    pub async fn rename_mailbox<OldName: AsRef<str>, NewName: AsRef<str>>(
89        &mut self,
90        old_name: OldName,
91        new_name: NewName,
92    ) -> Result<()> {
93        self.incoming
94            .rename_mailbox(old_name.as_ref(), new_name.as_ref())
95            .await
96    }
97
98    pub async fn delete_mailbox<BoxId: AsRef<str>>(&mut self, box_id: BoxId) -> Result<()> {
99        self.incoming.delete_mailbox(box_id.as_ref()).await
100    }
101
102    pub async fn create_mailbox<BoxName: AsRef<str>>(&mut self, box_id: BoxName) -> Result<()> {
103        self.incoming.create_mailbox(box_id.as_ref()).await
104    }
105
106    pub async fn get_messages<BoxId: AsRef<str>, S: Into<usize>, E: Into<usize>>(
107        &mut self,
108        box_id: BoxId,
109        start: S,
110        end: E,
111    ) -> Result<Vec<Preview>> {
112        let start = start.into();
113        let end = end.into();
114
115        if start >= end {
116            return Ok(Vec::new());
117        }
118
119        self.incoming
120            .get_messages(box_id.as_ref(), start, end)
121            .await
122    }
123
124    pub async fn get_message<BoxId: AsRef<str>, MessageId: AsRef<str>>(
125        &mut self,
126        box_id: BoxId,
127        message_id: MessageId,
128    ) -> Result<Message> {
129        self.incoming
130            .get_message(box_id.as_ref(), message_id.as_ref())
131            .await
132    }
133
134    pub async fn send_message<M: TryInto<SendableMessage, Error = impl Display>>(
135        &mut self,
136        message: M,
137    ) -> Result<()> {
138        let sendable = message.try_into().map_err(|err| {
139            Error::new(
140                ErrorKind::InvalidMessage,
141                format!("Failed to create sendable message: {}", err),
142            )
143        })?;
144
145        self.outgoing.send_message(sendable).await
146    }
147
148    pub async fn logout(&mut self) -> Result<()> {
149        self.incoming.logout().await
150    }
151}
152
153pub async fn create(
154    incoming: IncomingEmailProtocol,
155    outgoing: OutgoingEmailProtocol,
156) -> Result<EmailClient> {
157    let incoming_protocol = match incoming {
158        #[cfg(feature = "imap")]
159        IncomingEmailProtocol::Imap(credentials) => {
160            imap::create(&credentials, Default::default()).await?
161        }
162
163        #[cfg(feature = "pop")]
164        IncomingEmailProtocol::Pop(credentials) => pop::create(&credentials).await?,
165
166        #[cfg(feature = "maildir")]
167        IncomingEmailProtocol::Maildir(path) => maildir::create(path)?,
168
169        #[cfg(not(any(feature = "imap", feature = "pop")))]
170        _ => {
171            use crate::error::{err, ErrorKind};
172
173            err!(
174                ErrorKind::NoClientAvailable,
175                "There are no incoming mail clients supported",
176            );
177        }
178    };
179
180    let outgoing_protocol = match outgoing {
181        #[cfg(all(feature = "smtp", feature = "runtime-tokio"))]
182        OutgoingEmailProtocol::Smtp(credentials) => smtp::create(credentials)?,
183        #[cfg(not(any(all(feature = "smtp", feature = "runtime-tokio"))))]
184        _ => {
185            use crate::error::{err, ErrorKind};
186
187            err!(
188                ErrorKind::NoClientAvailable,
189                "There are no outgoing mail clients supported",
190            );
191        }
192    };
193
194    let client = EmailClient::new(incoming_protocol, outgoing_protocol);
195
196    Ok(client)
197}
198
199/// An email client suitable for multithreading applications.
200pub struct ThreadableEmailClient {
201    client: Arc<RwLock<EmailClient>>,
202    keep_alive: KeepAlive,
203}
204
205impl AsRef<Arc<RwLock<EmailClient>>> for ThreadableEmailClient {
206    fn as_ref(&self) -> &Arc<RwLock<EmailClient>> {
207        &self.client
208    }
209}
210
211impl ThreadableEmailClient {
212    pub fn new(client: Arc<RwLock<EmailClient>>, mut keep_alive: KeepAlive) -> Self {
213        keep_alive.start();
214
215        Self { client, keep_alive }
216    }
217
218    pub fn keep_alive(&self) -> &KeepAlive {
219        &self.keep_alive
220    }
221}
222
223impl From<EmailClient> for ThreadableEmailClient {
224    fn from(client: EmailClient) -> Self {
225        let client = Arc::new(RwLock::new(client));
226
227        let keep_alive: KeepAlive = Arc::clone(&client).into();
228
229        Self::new(client, keep_alive)
230    }
231}