1pub mod context;
43mod error;
44pub mod feature;
45pub mod mapper;
46pub mod macros {
47 pub use email_macros::BackendContext;
48}
49
50#[cfg(feature = "sync")]
51use std::hash::DefaultHasher;
52use std::sync::Arc;
53
54use async_trait::async_trait;
55use paste::paste;
56#[cfg(feature = "watch")]
57use tokio::sync::oneshot::{Receiver, Sender};
58
59#[doc(inline)]
60pub use self::error::{Error, Result};
61use self::{
62 context::{BackendContext, BackendContextBuilder},
63 feature::{BackendFeature, BackendFeatureSource, CheckUp},
64};
65#[cfg(feature = "watch")]
66use crate::envelope::watch::WatchEnvelopes;
67#[cfg(feature = "thread")]
68use crate::envelope::{thread::ThreadEnvelopes, ThreadedEnvelopes};
69#[cfg(feature = "sync")]
70use crate::sync::hash::SyncHash;
71use crate::{
72 account::config::{AccountConfig, HasAccountConfig},
73 envelope::{
74 get::GetEnvelope,
75 list::{ListEnvelopes, ListEnvelopesOptions},
76 Envelope, Envelopes, Id, SingleId,
77 },
78 flag::{add::AddFlags, remove::RemoveFlags, set::SetFlags, Flags},
79 folder::{
80 add::AddFolder, delete::DeleteFolder, expunge::ExpungeFolder, list::ListFolders,
81 purge::PurgeFolder, Folders,
82 },
83 message::{
84 add::AddMessage, copy::CopyMessages, delete::DeleteMessages, get::GetMessages,
85 peek::PeekMessages, r#move::MoveMessages, remove::RemoveMessages, send::SendMessage,
86 Messages,
87 },
88 AnyResult,
89};
90
91pub struct Backend<C>
100where
101 C: BackendContext,
102{
103 pub account_config: Arc<AccountConfig>,
105 pub context: Arc<C>,
107
108 pub add_folder: Option<BackendFeature<C, dyn AddFolder>>,
110 pub list_folders: Option<BackendFeature<C, dyn ListFolders>>,
112 pub expunge_folder: Option<BackendFeature<C, dyn ExpungeFolder>>,
114 pub purge_folder: Option<BackendFeature<C, dyn PurgeFolder>>,
116 pub delete_folder: Option<BackendFeature<C, dyn DeleteFolder>>,
118
119 pub get_envelope: Option<BackendFeature<C, dyn GetEnvelope>>,
121 pub list_envelopes: Option<BackendFeature<C, dyn ListEnvelopes>>,
123 #[cfg(feature = "thread")]
125 pub thread_envelopes: Option<BackendFeature<C, dyn ThreadEnvelopes>>,
126 #[cfg(feature = "watch")]
128 pub watch_envelopes: Option<BackendFeature<C, dyn WatchEnvelopes>>,
129
130 pub add_flags: Option<BackendFeature<C, dyn AddFlags>>,
132 pub set_flags: Option<BackendFeature<C, dyn SetFlags>>,
134 pub remove_flags: Option<BackendFeature<C, dyn RemoveFlags>>,
136
137 pub add_message: Option<BackendFeature<C, dyn AddMessage>>,
139 pub send_message: Option<BackendFeature<C, dyn SendMessage>>,
141 pub peek_messages: Option<BackendFeature<C, dyn PeekMessages>>,
143 pub get_messages: Option<BackendFeature<C, dyn GetMessages>>,
145 pub copy_messages: Option<BackendFeature<C, dyn CopyMessages>>,
147 pub move_messages: Option<BackendFeature<C, dyn MoveMessages>>,
149 pub delete_messages: Option<BackendFeature<C, dyn DeleteMessages>>,
151 pub remove_messages: Option<BackendFeature<C, dyn RemoveMessages>>,
153}
154
155impl<C: BackendContext> HasAccountConfig for Backend<C> {
156 fn account_config(&self) -> &AccountConfig {
157 &self.account_config
158 }
159}
160
161#[async_trait]
162impl<C: BackendContext> AddFolder for Backend<C> {
163 async fn add_folder(&self, folder: &str) -> AnyResult<()> {
164 self.add_folder
165 .as_ref()
166 .and_then(|feature| feature(&self.context))
167 .ok_or(Error::AddFolderNotAvailableError)?
168 .add_folder(folder)
169 .await
170 }
171}
172
173#[async_trait]
174impl<C: BackendContext> ListFolders for Backend<C> {
175 async fn list_folders(&self) -> AnyResult<Folders> {
176 self.list_folders
177 .as_ref()
178 .and_then(|feature| feature(&self.context))
179 .ok_or(Error::ListFoldersNotAvailableError)?
180 .list_folders()
181 .await
182 }
183}
184
185#[async_trait]
186impl<C: BackendContext> ExpungeFolder for Backend<C> {
187 async fn expunge_folder(&self, folder: &str) -> AnyResult<()> {
188 self.expunge_folder
189 .as_ref()
190 .and_then(|feature| feature(&self.context))
191 .ok_or(Error::ExpungeFolderNotAvailableError)?
192 .expunge_folder(folder)
193 .await
194 }
195}
196
197#[async_trait]
198impl<C: BackendContext> PurgeFolder for Backend<C> {
199 async fn purge_folder(&self, folder: &str) -> AnyResult<()> {
200 self.purge_folder
201 .as_ref()
202 .and_then(|feature| feature(&self.context))
203 .ok_or(Error::PurgeFolderNotAvailableError)?
204 .purge_folder(folder)
205 .await
206 }
207}
208
209#[async_trait]
210impl<C: BackendContext> DeleteFolder for Backend<C> {
211 async fn delete_folder(&self, folder: &str) -> AnyResult<()> {
212 self.delete_folder
213 .as_ref()
214 .and_then(|feature| feature(&self.context))
215 .ok_or(Error::DeleteFolderNotAvailableError)?
216 .delete_folder(folder)
217 .await
218 }
219}
220
221#[async_trait]
222impl<C: BackendContext> GetEnvelope for Backend<C> {
223 async fn get_envelope(&self, folder: &str, id: &SingleId) -> AnyResult<Envelope> {
224 self.get_envelope
225 .as_ref()
226 .and_then(|feature| feature(&self.context))
227 .ok_or(Error::GetEnvelopeNotAvailableError)?
228 .get_envelope(folder, id)
229 .await
230 }
231}
232
233#[async_trait]
234impl<C: BackendContext> ListEnvelopes for Backend<C> {
235 async fn list_envelopes(
236 &self,
237 folder: &str,
238 opts: ListEnvelopesOptions,
239 ) -> AnyResult<Envelopes> {
240 self.list_envelopes
241 .as_ref()
242 .and_then(|feature| feature(&self.context))
243 .ok_or(Error::ListEnvelopesNotAvailableError)?
244 .list_envelopes(folder, opts)
245 .await
246 }
247}
248
249#[cfg(feature = "thread")]
250#[async_trait]
251impl<C: BackendContext> ThreadEnvelopes for Backend<C> {
252 async fn thread_envelopes(
253 &self,
254 folder: &str,
255 opts: ListEnvelopesOptions,
256 ) -> AnyResult<ThreadedEnvelopes> {
257 self.thread_envelopes
258 .as_ref()
259 .and_then(|feature| feature(&self.context))
260 .ok_or(Error::ThreadEnvelopesNotAvailableError)?
261 .thread_envelopes(folder, opts)
262 .await
263 }
264
265 async fn thread_envelope(
266 &self,
267 folder: &str,
268 id: SingleId,
269 opts: ListEnvelopesOptions,
270 ) -> AnyResult<ThreadedEnvelopes> {
271 self.thread_envelopes
272 .as_ref()
273 .and_then(|feature| feature(&self.context))
274 .ok_or(Error::ThreadEnvelopesNotAvailableError)?
275 .thread_envelope(folder, id, opts)
276 .await
277 }
278}
279
280#[cfg(feature = "watch")]
281#[async_trait]
282impl<C: BackendContext> WatchEnvelopes for Backend<C> {
283 async fn watch_envelopes(
284 &self,
285 folder: &str,
286 wait_for_shutdown_request: Receiver<()>,
287 shutdown: Sender<()>,
288 ) -> AnyResult<()> {
289 self.watch_envelopes
290 .as_ref()
291 .and_then(|feature| feature(&self.context))
292 .ok_or(Error::WatchEnvelopesNotAvailableError)?
293 .watch_envelopes(folder, wait_for_shutdown_request, shutdown)
294 .await
295 }
296}
297
298#[async_trait]
299impl<C: BackendContext> AddFlags for Backend<C> {
300 async fn add_flags(&self, folder: &str, id: &Id, flags: &Flags) -> AnyResult<()> {
301 self.add_flags
302 .as_ref()
303 .and_then(|feature| feature(&self.context))
304 .ok_or(Error::AddFlagsNotAvailableError)?
305 .add_flags(folder, id, flags)
306 .await
307 }
308}
309
310#[async_trait]
311impl<C: BackendContext> SetFlags for Backend<C> {
312 async fn set_flags(&self, folder: &str, id: &Id, flags: &Flags) -> AnyResult<()> {
313 self.set_flags
314 .as_ref()
315 .and_then(|feature| feature(&self.context))
316 .ok_or(Error::SetFlagsNotAvailableError)?
317 .set_flags(folder, id, flags)
318 .await
319 }
320}
321
322#[async_trait]
323impl<C: BackendContext> RemoveFlags for Backend<C> {
324 async fn remove_flags(&self, folder: &str, id: &Id, flags: &Flags) -> AnyResult<()> {
325 self.remove_flags
326 .as_ref()
327 .and_then(|feature| feature(&self.context))
328 .ok_or(Error::RemoveFlagsNotAvailableError)?
329 .remove_flags(folder, id, flags)
330 .await
331 }
332}
333
334#[async_trait]
335impl<C: BackendContext> AddMessage for Backend<C> {
336 async fn add_message_with_flags(
337 &self,
338 folder: &str,
339 msg: &[u8],
340 flags: &Flags,
341 ) -> AnyResult<SingleId> {
342 self.add_message
343 .as_ref()
344 .and_then(|feature| feature(&self.context))
345 .ok_or(Error::AddMessageNotAvailableError)?
346 .add_message_with_flags(folder, msg, flags)
347 .await
348 }
349}
350
351#[async_trait]
352impl<C: BackendContext> SendMessage for Backend<C> {
353 async fn send_message(&self, msg: &[u8]) -> AnyResult<()> {
354 self.send_message
355 .as_ref()
356 .and_then(|feature| feature(&self.context))
357 .ok_or(Error::SendMessageNotAvailableError)?
358 .send_message(msg)
359 .await
360 }
361}
362
363#[async_trait]
364impl<C: BackendContext> PeekMessages for Backend<C> {
365 async fn peek_messages(&self, folder: &str, id: &Id) -> AnyResult<Messages> {
366 self.peek_messages
367 .as_ref()
368 .and_then(|feature| feature(&self.context))
369 .ok_or(Error::PeekMessagesNotAvailableError)?
370 .peek_messages(folder, id)
371 .await
372 }
373}
374
375#[async_trait]
376impl<C: BackendContext> GetMessages for Backend<C> {
377 async fn get_messages(&self, folder: &str, id: &Id) -> AnyResult<Messages> {
378 self.get_messages
379 .as_ref()
380 .and_then(|feature| feature(&self.context))
381 .ok_or(Error::GetMessagesNotAvailableError)?
382 .get_messages(folder, id)
383 .await
384 }
385}
386
387#[async_trait]
388impl<C: BackendContext> CopyMessages for Backend<C> {
389 async fn copy_messages(&self, from_folder: &str, to_folder: &str, id: &Id) -> AnyResult<()> {
390 self.copy_messages
391 .as_ref()
392 .and_then(|feature| feature(&self.context))
393 .ok_or(Error::CopyMessagesNotAvailableError)?
394 .copy_messages(from_folder, to_folder, id)
395 .await
396 }
397}
398
399#[async_trait]
400impl<C: BackendContext> MoveMessages for Backend<C> {
401 async fn move_messages(&self, from_folder: &str, to_folder: &str, id: &Id) -> AnyResult<()> {
402 self.move_messages
403 .as_ref()
404 .and_then(|feature| feature(&self.context))
405 .ok_or(Error::MoveMessagesNotAvailableError)?
406 .move_messages(from_folder, to_folder, id)
407 .await
408 }
409}
410
411#[async_trait]
412impl<C: BackendContext> DeleteMessages for Backend<C> {
413 async fn delete_messages(&self, folder: &str, id: &Id) -> AnyResult<()> {
414 self.delete_messages
415 .as_ref()
416 .and_then(|feature| feature(&self.context))
417 .ok_or(Error::DeleteMessagesNotAvailableError)?
418 .delete_messages(folder, id)
419 .await
420 }
421}
422
423#[async_trait]
424impl<C: BackendContext> RemoveMessages for Backend<C> {
425 async fn remove_messages(&self, folder: &str, id: &Id) -> AnyResult<()> {
426 self.remove_messages
427 .as_ref()
428 .and_then(|feature| feature(&self.context))
429 .ok_or(Error::RemoveMessagesNotAvailableError)?
430 .remove_messages(folder, id)
431 .await
432 }
433}
434
435macro_rules! feature_accessors {
437 ($feat:ty) => {
438 paste! {
439 pub fn [<get_ $feat:snake>](
440 &self
441 ) -> Option<BackendFeature<CB::Context, dyn $feat>> {
442 match &self.[<$feat:snake>] {
443 BackendFeatureSource::None => None,
444 BackendFeatureSource::Context => self.ctx_builder.[<$feat:snake>]().clone(),
445 BackendFeatureSource::Backend(f) => Some(f.clone()),
446 }
447 }
448
449 pub fn [<set_ $feat:snake>](
451 &mut self,
452 f: impl Into<BackendFeatureSource<CB::Context, dyn $feat>>,
453 ) {
454 self.[<$feat:snake>] = f.into();
455 }
456
457 pub fn [<with_ $feat:snake>](
460 mut self,
461 f: impl Into<BackendFeatureSource<CB::Context, dyn $feat>>,
462 ) -> Self {
463 self.[<set_ $feat:snake>](f);
464 self
465 }
466
467 pub fn [<without_ $feat:snake>](mut self) -> Self {
470 self.[<set_ $feat:snake>](BackendFeatureSource::None);
471 self
472 }
473
474 pub fn [<with_context_ $feat:snake>](mut self) -> Self {
477 self.[<set_ $feat:snake>](BackendFeatureSource::Context);
478 self
479 }
480 }
481 };
482}
483
484pub struct BackendBuilder<CB>
500where
501 CB: BackendContextBuilder,
502{
503 pub account_config: Arc<AccountConfig>,
505 pub ctx_builder: CB,
507
508 pub check_up: BackendFeatureSource<CB::Context, dyn CheckUp>,
510
511 pub add_folder: BackendFeatureSource<CB::Context, dyn AddFolder>,
513 pub list_folders: BackendFeatureSource<CB::Context, dyn ListFolders>,
515 pub expunge_folder: BackendFeatureSource<CB::Context, dyn ExpungeFolder>,
517 pub purge_folder: BackendFeatureSource<CB::Context, dyn PurgeFolder>,
519 pub delete_folder: BackendFeatureSource<CB::Context, dyn DeleteFolder>,
521
522 pub get_envelope: BackendFeatureSource<CB::Context, dyn GetEnvelope>,
524 pub list_envelopes: BackendFeatureSource<CB::Context, dyn ListEnvelopes>,
526 #[cfg(feature = "thread")]
528 pub thread_envelopes: BackendFeatureSource<CB::Context, dyn ThreadEnvelopes>,
529 #[cfg(feature = "watch")]
531 pub watch_envelopes: BackendFeatureSource<CB::Context, dyn WatchEnvelopes>,
532
533 pub add_flags: BackendFeatureSource<CB::Context, dyn AddFlags>,
535 pub set_flags: BackendFeatureSource<CB::Context, dyn SetFlags>,
537 pub remove_flags: BackendFeatureSource<CB::Context, dyn RemoveFlags>,
539
540 pub add_message: BackendFeatureSource<CB::Context, dyn AddMessage>,
542 pub send_message: BackendFeatureSource<CB::Context, dyn SendMessage>,
544 pub peek_messages: BackendFeatureSource<CB::Context, dyn PeekMessages>,
546 pub get_messages: BackendFeatureSource<CB::Context, dyn GetMessages>,
548 pub copy_messages: BackendFeatureSource<CB::Context, dyn CopyMessages>,
550 pub move_messages: BackendFeatureSource<CB::Context, dyn MoveMessages>,
552 pub delete_messages: BackendFeatureSource<CB::Context, dyn DeleteMessages>,
554 pub remove_messages: BackendFeatureSource<CB::Context, dyn RemoveMessages>,
556}
557
558impl<CB> BackendBuilder<CB>
559where
560 CB: BackendContextBuilder,
561{
562 feature_accessors!(CheckUp);
563 feature_accessors!(AddFolder);
564 feature_accessors!(ListFolders);
565 feature_accessors!(ExpungeFolder);
566 feature_accessors!(PurgeFolder);
567 feature_accessors!(DeleteFolder);
568 feature_accessors!(GetEnvelope);
569 feature_accessors!(ListEnvelopes);
570 #[cfg(feature = "thread")]
571 feature_accessors!(ThreadEnvelopes);
572 #[cfg(feature = "watch")]
573 feature_accessors!(WatchEnvelopes);
574 feature_accessors!(AddFlags);
575 feature_accessors!(SetFlags);
576 feature_accessors!(RemoveFlags);
577 feature_accessors!(AddMessage);
578 feature_accessors!(SendMessage);
579 feature_accessors!(PeekMessages);
580 feature_accessors!(GetMessages);
581 feature_accessors!(CopyMessages);
582 feature_accessors!(MoveMessages);
583 feature_accessors!(DeleteMessages);
584 feature_accessors!(RemoveMessages);
585
586 pub fn new(account_config: Arc<AccountConfig>, ctx_builder: CB) -> Self {
591 Self {
592 account_config,
593 ctx_builder,
594
595 check_up: BackendFeatureSource::Context,
596
597 add_folder: BackendFeatureSource::Context,
598 list_folders: BackendFeatureSource::Context,
599 expunge_folder: BackendFeatureSource::Context,
600 purge_folder: BackendFeatureSource::Context,
601 delete_folder: BackendFeatureSource::Context,
602
603 get_envelope: BackendFeatureSource::Context,
604 list_envelopes: BackendFeatureSource::Context,
605 #[cfg(feature = "thread")]
606 thread_envelopes: BackendFeatureSource::Context,
607 #[cfg(feature = "watch")]
608 watch_envelopes: BackendFeatureSource::Context,
609
610 add_flags: BackendFeatureSource::Context,
611 set_flags: BackendFeatureSource::Context,
612 remove_flags: BackendFeatureSource::Context,
613
614 add_message: BackendFeatureSource::Context,
615 send_message: BackendFeatureSource::Context,
616 peek_messages: BackendFeatureSource::Context,
617 get_messages: BackendFeatureSource::Context,
618 copy_messages: BackendFeatureSource::Context,
619 move_messages: BackendFeatureSource::Context,
620 delete_messages: BackendFeatureSource::Context,
621 remove_messages: BackendFeatureSource::Context,
622 }
623 }
624
625 pub fn without_features(mut self) -> Self {
627 self.set_list_folders(BackendFeatureSource::None);
628 self
629 }
630
631 pub async fn check_up(self) -> AnyResult<()> {
632 let ctx = self.ctx_builder.clone().build().await?;
633 match self.get_check_up().and_then(move |f| f(&ctx)) {
634 Some(f) => f.check_up().await,
635 None => Ok(()),
636 }
637 }
638
639 pub async fn build(self) -> AnyResult<Backend<CB::Context>> {
640 let add_folder = self.get_add_folder();
641 let list_folders = self.get_list_folders();
642 let expunge_folder = self.get_expunge_folder();
643 let purge_folder = self.get_purge_folder();
644 let delete_folder = self.get_delete_folder();
645
646 let get_envelope = self.get_get_envelope();
647 let list_envelopes = self.get_list_envelopes();
648 #[cfg(feature = "thread")]
649 let thread_envelopes = self.get_thread_envelopes();
650 #[cfg(feature = "watch")]
651 let watch_envelopes = self.get_watch_envelopes();
652
653 let add_flags = self.get_add_flags();
654 let set_flags = self.get_set_flags();
655 let remove_flags = self.get_remove_flags();
656
657 let add_message = self.get_add_message();
658 let send_message = self.get_send_message();
659 let peek_messages = self.get_peek_messages();
660 let get_messages = self.get_get_messages();
661 let copy_messages = self.get_copy_messages();
662 let move_messages = self.get_move_messages();
663 let delete_messages = self.get_delete_messages();
664 let remove_messages = self.get_remove_messages();
665
666 Ok(Backend {
667 account_config: self.account_config,
668 context: Arc::new(self.ctx_builder.build().await?),
669
670 add_folder,
671 list_folders,
672 expunge_folder,
673 purge_folder,
674 delete_folder,
675
676 get_envelope,
677 list_envelopes,
678 #[cfg(feature = "thread")]
679 thread_envelopes,
680 #[cfg(feature = "watch")]
681 watch_envelopes,
682
683 add_flags,
684 set_flags,
685 remove_flags,
686
687 add_message,
688 send_message,
689 peek_messages,
690 get_messages,
691 copy_messages,
692 move_messages,
693 delete_messages,
694 remove_messages,
695 })
696 }
697}
698
699#[async_trait]
700impl<CB> Clone for BackendBuilder<CB>
701where
702 CB: BackendContextBuilder,
703{
704 fn clone(&self) -> Self {
705 Self {
706 account_config: self.account_config.clone(),
707 ctx_builder: self.ctx_builder.clone(),
708
709 check_up: self.check_up.clone(),
710
711 add_folder: self.add_folder.clone(),
712 list_folders: self.list_folders.clone(),
713 expunge_folder: self.expunge_folder.clone(),
714 purge_folder: self.purge_folder.clone(),
715 delete_folder: self.delete_folder.clone(),
716
717 get_envelope: self.get_envelope.clone(),
718 list_envelopes: self.list_envelopes.clone(),
719 #[cfg(feature = "thread")]
720 thread_envelopes: self.thread_envelopes.clone(),
721 #[cfg(feature = "watch")]
722 watch_envelopes: self.watch_envelopes.clone(),
723
724 add_flags: self.add_flags.clone(),
725 set_flags: self.set_flags.clone(),
726 remove_flags: self.remove_flags.clone(),
727
728 add_message: self.add_message.clone(),
729 send_message: self.send_message.clone(),
730 peek_messages: self.peek_messages.clone(),
731 get_messages: self.get_messages.clone(),
732 copy_messages: self.copy_messages.clone(),
733 move_messages: self.move_messages.clone(),
734 delete_messages: self.delete_messages.clone(),
735 remove_messages: self.remove_messages.clone(),
736 }
737 }
738}
739
740#[cfg(feature = "sync")]
741impl<CB> SyncHash for BackendBuilder<CB>
742where
743 CB: BackendContextBuilder + SyncHash,
744{
745 fn sync_hash(&self, state: &mut DefaultHasher) {
746 self.ctx_builder.sync_hash(state)
747 }
748}