email/backend/
mapper.rs

1//! # Backend feature mapper
2//!
3//! This module contains [`BackendContextBuilder`] helpers to map
4//! features from a subcontext B to a context A.
5
6use std::sync::Arc;
7
8use paste::paste;
9
10use super::{
11    context::{BackendContext, BackendContextBuilder},
12    feature::{BackendFeature, CheckUp},
13};
14#[cfg(feature = "thread")]
15use crate::envelope::thread::ThreadEnvelopes;
16#[cfg(feature = "watch")]
17use crate::envelope::watch::WatchEnvelopes;
18use crate::{
19    envelope::{get::GetEnvelope, list::ListEnvelopes},
20    flag::{add::AddFlags, remove::RemoveFlags, set::SetFlags},
21    folder::{
22        add::AddFolder, delete::DeleteFolder, expunge::ExpungeFolder, list::ListFolders,
23        purge::PurgeFolder,
24    },
25    message::{
26        add::AddMessage, copy::CopyMessages, delete::DeleteMessages, get::GetMessages,
27        peek::PeekMessages, r#move::MoveMessages, remove::RemoveMessages, send::SendMessage,
28    },
29};
30
31/// Macro for defining some [`BackendContextBuilder`] feature mapper.
32macro_rules! some_feature_mapper {
33    ($feat:ty) => {
34        paste! {
35            fn [<$feat:snake _with_some>](
36                &self,
37                cb: &Option<CB>,
38            ) -> Option<BackendFeature<Self::Context, dyn $feat>> {
39                let cb = cb.as_ref()?;
40                self.map_feature(cb.[<$feat:snake>]())
41            }
42        }
43    };
44}
45
46/// Map a backend feature from subcontext B to context A.
47///
48/// This is useful when you have a context composed of multiple
49/// subcontexts. It prevents you to manually map the feature.
50///
51/// See a usage example at `../../tests/dynamic_backend.rs`.
52pub trait SomeBackendContextBuilderMapper<CB>
53where
54    Self: BackendContextBuilder,
55    Self::Context: AsRef<Option<CB::Context>> + 'static,
56    CB: BackendContextBuilder,
57    CB::Context: BackendContext + 'static,
58{
59    fn map_feature<T: ?Sized + 'static>(
60        &self,
61        f: Option<BackendFeature<CB::Context, T>>,
62    ) -> Option<BackendFeature<Self::Context, T>> {
63        let f = f?;
64        Some(Arc::new(move |ctx| f(ctx.as_ref().as_ref()?)))
65    }
66
67    some_feature_mapper!(CheckUp);
68
69    some_feature_mapper!(AddFolder);
70    some_feature_mapper!(ListFolders);
71    some_feature_mapper!(ExpungeFolder);
72    some_feature_mapper!(PurgeFolder);
73    some_feature_mapper!(DeleteFolder);
74    some_feature_mapper!(GetEnvelope);
75    some_feature_mapper!(ListEnvelopes);
76    #[cfg(feature = "thread")]
77    some_feature_mapper!(ThreadEnvelopes);
78    #[cfg(feature = "watch")]
79    some_feature_mapper!(WatchEnvelopes);
80    some_feature_mapper!(AddFlags);
81    some_feature_mapper!(SetFlags);
82    some_feature_mapper!(RemoveFlags);
83    some_feature_mapper!(AddMessage);
84    some_feature_mapper!(SendMessage);
85    some_feature_mapper!(PeekMessages);
86    some_feature_mapper!(GetMessages);
87    some_feature_mapper!(CopyMessages);
88    some_feature_mapper!(MoveMessages);
89    some_feature_mapper!(DeleteMessages);
90    some_feature_mapper!(RemoveMessages);
91}
92
93/// Automatically implement [`SomeBackendContextBuilderMapper`].
94impl<CB1, CB2> SomeBackendContextBuilderMapper<CB2> for CB1
95where
96    CB1: BackendContextBuilder,
97    CB1::Context: AsRef<Option<CB2::Context>> + 'static,
98    CB2: BackendContextBuilder,
99    CB2::Context: BackendContext + 'static,
100{
101}
102
103/// Macro for defining [`BackendContextBuilder`] feature mapper.
104macro_rules! feature_mapper {
105    ($feat:ty) => {
106        paste! {
107            fn [<$feat:snake _with>] (
108                &self,
109                cb: &CB,
110            ) -> Option<BackendFeature<Self::Context, dyn $feat>> {
111               self.map_feature(cb.[<$feat:snake>]())
112            }
113        }
114    };
115}
116
117/// Same as [`SomeBackendContextBuilderMapper`] but without Option.
118pub trait BackendContextBuilderMapper<CB>
119where
120    Self: BackendContextBuilder,
121    Self::Context: AsRef<CB::Context> + 'static,
122    CB: BackendContextBuilder,
123    CB::Context: BackendContext + 'static,
124{
125    fn map_feature<T: ?Sized + 'static>(
126        &self,
127        f: Option<BackendFeature<CB::Context, T>>,
128    ) -> Option<BackendFeature<Self::Context, T>> {
129        let f = f?;
130        Some(Arc::new(move |ctx| f(ctx.as_ref())))
131    }
132
133    feature_mapper!(AddFolder);
134    feature_mapper!(ListFolders);
135    feature_mapper!(ExpungeFolder);
136    feature_mapper!(PurgeFolder);
137    feature_mapper!(DeleteFolder);
138    feature_mapper!(GetEnvelope);
139    feature_mapper!(ListEnvelopes);
140    #[cfg(feature = "thread")]
141    feature_mapper!(ThreadEnvelopes);
142    #[cfg(feature = "watch")]
143    feature_mapper!(WatchEnvelopes);
144    feature_mapper!(AddFlags);
145    feature_mapper!(SetFlags);
146    feature_mapper!(RemoveFlags);
147    feature_mapper!(AddMessage);
148    feature_mapper!(SendMessage);
149    feature_mapper!(PeekMessages);
150    feature_mapper!(GetMessages);
151    feature_mapper!(CopyMessages);
152    feature_mapper!(MoveMessages);
153    feature_mapper!(DeleteMessages);
154    feature_mapper!(RemoveMessages);
155}
156
157/// Automatically implement [`BackendContextBuilderMapper`].
158impl<CB1, CB2> BackendContextBuilderMapper<CB2> for CB1
159where
160    CB1: BackendContextBuilder,
161    CB1::Context: AsRef<CB2::Context> + 'static,
162    CB2: BackendContextBuilder,
163    CB2::Context: BackendContext + 'static,
164{
165}