1use async_trait::async_trait;
9use paste::paste;
10
11use super::feature::{BackendFeature, CheckUp};
12#[cfg(feature = "thread")]
13use crate::envelope::thread::ThreadEnvelopes;
14#[cfg(feature = "watch")]
15use crate::envelope::watch::WatchEnvelopes;
16use crate::{
17 envelope::{get::GetEnvelope, list::ListEnvelopes},
18 flag::{add::AddFlags, remove::RemoveFlags, set::SetFlags},
19 folder::{
20 add::AddFolder, delete::DeleteFolder, expunge::ExpungeFolder, list::ListFolders,
21 purge::PurgeFolder,
22 },
23 message::{
24 add::AddMessage, copy::CopyMessages, delete::DeleteMessages, get::GetMessages,
25 peek::PeekMessages, r#move::MoveMessages, remove::RemoveMessages, send::SendMessage,
26 },
27 AnyResult,
28};
29
30pub trait BackendContext: Send + Sync {}
36
37macro_rules! feature {
39 ($feat:ty) => {
40 paste! {
41 fn [<$feat:snake>](&self) -> Option<BackendFeature<Self::Context, dyn $feat>> {
43 None
44 }
45 }
46 };
47}
48
49#[async_trait]
54pub trait BackendContextBuilder: Clone + Send + Sync {
55 type Context: BackendContext;
57
58 async fn check(&self) -> AnyResult<()> {
59 if let Some(feature) = self.check_up() {
60 let ctx = self.clone().build().await?;
61
62 if let Some(feature) = feature(&ctx) {
63 feature.check_up().await?;
64 }
65 }
66
67 Ok(())
68 }
69
70 fn check_configuration(&self) -> AnyResult<()> {
71 Ok(())
72 }
73
74 async fn configure(&mut self) -> AnyResult<()> {
75 Ok(())
76 }
77
78 feature!(CheckUp);
79
80 feature!(AddFolder);
81 feature!(ListFolders);
82 feature!(ExpungeFolder);
83 feature!(PurgeFolder);
84 feature!(DeleteFolder);
85 feature!(GetEnvelope);
86 feature!(ListEnvelopes);
87 #[cfg(feature = "thread")]
88 feature!(ThreadEnvelopes);
89 #[cfg(feature = "watch")]
90 feature!(WatchEnvelopes);
91 feature!(AddFlags);
92 feature!(SetFlags);
93 feature!(RemoveFlags);
94 feature!(AddMessage);
95 feature!(SendMessage);
96 feature!(PeekMessages);
97 feature!(GetMessages);
98 feature!(CopyMessages);
99 feature!(MoveMessages);
100 feature!(DeleteMessages);
101 feature!(RemoveMessages);
102
103 async fn build(self) -> AnyResult<Self::Context>;
105
106 #[cfg(feature = "sync")]
107 fn try_to_sync_cache_builder(
108 &self,
109 account_config: &crate::account::config::AccountConfig,
110 ) -> std::result::Result<crate::maildir::MaildirContextBuilder, crate::account::Error>
111 where
112 Self: crate::sync::hash::SyncHash,
113 {
114 use std::{
115 hash::{DefaultHasher, Hasher},
116 sync::Arc,
117 };
118
119 use dirs::data_dir;
120 use shellexpand_utils::try_shellexpand_path;
121 use tracing::debug;
122
123 use crate::{
124 account::{config::AccountConfig, Error},
125 maildir::{config::MaildirConfig, MaildirContextBuilder},
126 };
127
128 let mut hasher = DefaultHasher::new();
129 self.sync_hash(&mut hasher);
130 let hash = format!("{:x}", hasher.finish());
131
132 let sync_dir = account_config.sync.as_ref().and_then(|c| c.dir.as_ref());
133 let root_dir = match sync_dir {
134 Some(dir) => {
135 let dir = try_shellexpand_path(dir)
136 .map_err(|err| Error::GetSyncDirInvalidError(err, dir.clone()))?;
137 debug!(?dir, "using custom sync directory");
138 dir
139 }
140 None => {
141 let dir = data_dir()
142 .ok_or(Error::GetXdgDataDirSyncError)?
143 .join("pimalaya")
144 .join("email")
145 .join("sync")
146 .join(&hash);
147 debug!(?dir, "using default sync directory");
148 dir
149 }
150 };
151
152 let account_config = Arc::new(AccountConfig {
153 name: account_config.name.clone(),
154 email: account_config.email.clone(),
155 display_name: account_config.display_name.clone(),
156 signature: account_config.signature.clone(),
157 signature_delim: account_config.signature_delim.clone(),
158 downloads_dir: account_config.downloads_dir.clone(),
159 folder: account_config.folder.clone(),
160 envelope: account_config.envelope.clone(),
161 flag: account_config.flag.clone(),
162 message: account_config.message.clone(),
163 template: account_config.template.clone(),
164 sync: None,
165 #[cfg(feature = "pgp")]
166 pgp: account_config.pgp.clone(),
167 });
168
169 let config = Arc::new(MaildirConfig {
170 root_dir,
171 maildirpp: false,
172 });
173
174 let ctx = MaildirContextBuilder::new(account_config.clone(), config);
175
176 Ok(ctx)
177 }
178}