From d95f277bab5eaafa75b25795ea7ba225c79342e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 5 Apr 2024 11:05:55 +0200 Subject: [PATCH] adjust code for pimalaya new errors + sync hash --- Cargo.lock | 104 ++++-- Cargo.toml | 11 +- src/account/command/check_up.rs | 18 +- src/account/command/sync.rs | 187 +++-------- src/account/config.rs | 28 +- src/account/wizard.rs | 6 +- src/backend/mod.rs | 357 +++++++++++++-------- src/config/mod.rs | 7 +- src/email/envelope/command/list.rs | 6 +- src/email/envelope/command/watch.rs | 2 +- src/email/message/command/send.rs | 2 +- src/email/message/template/command/send.rs | 2 +- src/folder/command/add.rs | 7 +- src/folder/command/delete.rs | 7 +- src/folder/command/expunge.rs | 7 +- src/folder/command/list.rs | 8 +- src/folder/command/purge.rs | 7 +- src/ui/editor.rs | 1 - 18 files changed, 439 insertions(+), 328 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1447b2f..fd8ab5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,7 +166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702" dependencies = [ "unicode-width", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -396,6 +396,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "base64ct" version = "1.6.0" @@ -1212,10 +1218,9 @@ dependencies = [ [[package]] name = "email-lib" version = "0.22.3" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "advisory-lock", - "anyhow", "async-trait", "chrono", "chumsky", @@ -1235,7 +1240,6 @@ dependencies = [ "mail-parser", "mail-send", "maildirpp", - "md5", "mml-lib", "notify", "notify-rust", @@ -1263,7 +1267,7 @@ dependencies = [ [[package]] name = "email-macros" version = "0.0.2" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "quote", "syn 2.0.53", @@ -2017,7 +2021,7 @@ dependencies = [ "hyper", "log", "rustls 0.21.10", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -2082,17 +2086,17 @@ dependencies = [ [[package]] name = "imap" -version = "3.0.0-alpha.12" +version = "3.0.0-alpha.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c088f9992a0cd2ac0208dd3b1e0bb106a2f54e262f09756d554aa71219510056" +checksum = "fd82d66124b97aabeba7a4744b82bf244160e56611d8be9f89b74ed4ee481f1e" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "bufstream", "chrono", "imap-proto", "lazy_static", "nom", - "ouroboros 0.16.0", + "ouroboros 0.18.3", "regex", "rustls-connector", ] @@ -2196,6 +2200,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -2243,7 +2256,7 @@ dependencies = [ [[package]] name = "keyring-lib" version = "0.4.0" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "keyring", "log", @@ -2543,7 +2556,7 @@ dependencies = [ [[package]] name = "mml-lib" version = "1.0.8" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "async-recursion", "chumsky", @@ -2759,7 +2772,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "oauth-lib" version = "0.1.0" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "log", "oauth2", @@ -2882,12 +2895,12 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.16.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a6d0919a92ba28d8109a103e0de08f89706be0eeaad1130fd1a34030dee84a" +checksum = "97b7be5a8a3462b752f4be3ff2b2bf2f7f1d00834902e46be2a4d68b87b0573c" dependencies = [ "aliasable", - "ouroboros_macro 0.16.0", + "ouroboros_macro 0.18.3", "static_assertions", ] @@ -2906,13 +2919,14 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.16.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bc2307dc3420554ae349230dac4969c66d7c2feead3a8cab05ea0c604daca6" +checksum = "b645dcde5f119c2c454a92d0dfa271a2a3b205da92e4292a68ead4bdbfde1f33" dependencies = [ "heck 0.4.1", - "proc-macro-error", + "itertools", "proc-macro2", + "proc-macro2-diagnostics", "quote", "syn 2.0.53", ] @@ -3104,7 +3118,8 @@ dependencies = [ [[package]] name = "pgp-lib" version = "0.1.0" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d61233a437ba3de6396593cb27fda4e48ba7c7415756caffe9f9d5d0d07378c" dependencies = [ "async-recursion", "futures", @@ -3277,10 +3292,23 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", + "version_check", + "yansi 1.0.1", +] + [[package]] name = "process-lib" version = "0.4.1" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "log", "serde", @@ -3651,14 +3679,15 @@ dependencies = [ [[package]] name = "rustls-connector" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25da151615461c7347114b1ad1a7458b4cdebc69cb220cd140cd5cb324b1dd37" +checksum = "b5bd40675c79c896f46d0031bf64c448b35e583dd2bc949751ddd800351e453a" dependencies = [ "log", - "rustls 0.21.10", - "rustls-native-certs", - "rustls-webpki 0.101.7", + "rustls 0.22.2", + "rustls-native-certs 0.7.0", + "rustls-pki-types", + "rustls-webpki 0.102.2", ] [[package]] @@ -3673,6 +3702,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.1", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3776,7 +3818,7 @@ dependencies = [ [[package]] name = "secret-lib" version = "0.4.1" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "keyring-lib", "log", @@ -3979,7 +4021,7 @@ dependencies = [ [[package]] name = "shellexpand-utils" version = "0.2.0" -source = "git+https://git.sr.ht/~soywod/pimalaya#381b1ef4939be887330466a7fcbbb6487190ddee" +source = "git+https://git.sr.ht/~soywod/pimalaya#dd8bada6ef4efb097490a7bfe1ca0499dac6dbf0" dependencies = [ "log", "shellexpand", @@ -4968,6 +5010,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "z-base-32" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 947fc17..f7b96e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,12 +93,21 @@ version = "0.1" [patch.crates-io] # waiting for alpha 7 chumsky = { git = "https://github.com/zesterer/chumsky.git", rev = "6837537" } + email-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } email-macros = { git = "https://git.sr.ht/~soywod/pimalaya" } keyring-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } mml-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } oauth-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } -pgp-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } process-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } secret-lib = { git = "https://git.sr.ht/~soywod/pimalaya" } shellexpand-utils = { git = "https://git.sr.ht/~soywod/pimalaya" } + +# email-lib = { path = "/home/soywod/sourcehut/pimalaya/email" } +# email-macros = { path = "/home/soywod/sourcehut/pimalaya/email-macros" } +# keyring-lib = { path = "/home/soywod/sourcehut/pimalaya/keyring" } +# mml-lib = { path = "/home/soywod/sourcehut/pimalaya/mml" } +# oauth-lib = { path = "/home/soywod/sourcehut/pimalaya/oauth" } +# process-lib = { path = "/home/soywod/sourcehut/pimalaya/process" } +# secret-lib = { path = "/home/soywod/sourcehut/pimalaya/secret" } +# shellexpand-utils = { path = "/home/soywod/sourcehut/pimalaya/shellexpand-utils" } diff --git a/src/account/command/check_up.rs b/src/account/command/check_up.rs index 51fc764..bd0cfc5 100644 --- a/src/account/command/check_up.rs +++ b/src/account/command/check_up.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use email::backend::context::BackendContextBuilder; use log::info; @@ -42,7 +42,11 @@ impl AccountCheckUpCommand { ) .await?; - let ctx = ctx_builder.clone().build().await?; + let ctx = ctx_builder + .clone() + .build() + .await + .map_err(|err| anyhow!(err))?; #[cfg(feature = "maildir")] { @@ -55,7 +59,7 @@ impl AccountCheckUpCommand { .and_then(|f| ctx.maildir.as_ref().and_then(|ctx| f(ctx))); if let Some(maildir) = maildir.as_ref() { - maildir.check_up().await?; + maildir.check_up().await.map_err(|err| anyhow!(err))?; } } @@ -70,7 +74,7 @@ impl AccountCheckUpCommand { .and_then(|f| ctx.imap.as_ref().and_then(|ctx| f(ctx))); if let Some(imap) = imap.as_ref() { - imap.check_up().await?; + imap.check_up().await.map_err(|err| anyhow!(err))?; } } @@ -85,7 +89,7 @@ impl AccountCheckUpCommand { .and_then(|f| ctx.notmuch.as_ref().and_then(|ctx| f(ctx))); if let Some(notmuch) = notmuch.as_ref() { - notmuch.check_up().await?; + notmuch.check_up().await.map_err(|err| anyhow!(err))?; } } @@ -100,7 +104,7 @@ impl AccountCheckUpCommand { .and_then(|f| ctx.smtp.as_ref().and_then(|ctx| f(ctx))); if let Some(smtp) = smtp.as_ref() { - smtp.check_up().await?; + smtp.check_up().await.map_err(|err| anyhow!(err))?; } } @@ -115,7 +119,7 @@ impl AccountCheckUpCommand { .and_then(|f| ctx.sendmail.as_ref().and_then(|ctx| f(ctx))); if let Some(sendmail) = sendmail.as_ref() { - sendmail.check_up().await?; + sendmail.check_up().await.map_err(|err| anyhow!(err))?; } } diff --git a/src/account/command/sync.rs b/src/account/command/sync.rs index 892c950..4d8ab9f 100644 --- a/src/account/command/sync.rs +++ b/src/account/command/sync.rs @@ -1,33 +1,27 @@ -use crate::{ - account::{arg::name::OptionalAccountNameArg, config::TomlAccountConfig}, - backend::{Backend, BackendContextBuilder, BackendKind}, - config::TomlConfig, - printer::Printer, -}; -use anyhow::Result; +use anyhow::{anyhow, bail, Result}; use clap::{ArgAction, Parser}; +use email::backend::context::BackendContextBuilder; #[cfg(feature = "imap")] use email::imap::ImapContextBuilder; -use email::maildir::config::MaildirConfig; -#[cfg(feature = "maildir")] -use email::maildir::MaildirContextBuilder; -#[cfg(feature = "notmuch")] -use email::notmuch::NotmuchContextBuilder; use email::{ - account::{config::AccountConfig, sync::AccountSyncBuilder}, + account::sync::AccountSyncBuilder, backend::BackendBuilder, folder::sync::config::FolderSyncStrategy, - sync::SyncEvent, + sync::{hash::SyncHash, SyncEvent}, }; use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle}; use log::info; use once_cell::sync::Lazy; use std::{ collections::{BTreeSet, HashMap}, - ops::Deref, sync::{Arc, Mutex}, }; +use crate::{ + account::arg::name::OptionalAccountNameArg, backend::BackendKind, config::TomlConfig, + printer::Printer, +}; + static MAIN_PROGRESS_STYLE: Lazy = Lazy::new(|| { ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap() }); @@ -93,10 +87,41 @@ impl AccountSyncCommand { pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { info!("executing sync account command"); + let account = self.account.name.as_deref(); + let (toml_account_config, account_config) = + config.clone().into_account_configs(account, true)?; + let account_name = account_config.name.as_str(); + + match toml_account_config.sync_kind() { + Some(BackendKind::Imap) | Some(BackendKind::ImapCache) => { + let imap_config = toml_account_config + .imap + .as_ref() + .map(Clone::clone) + .map(Arc::new) + .ok_or_else(|| anyhow!("imap config not found"))?; + let imap_ctx = ImapContextBuilder::new(account_config.clone(), imap_config) + .with_prebuilt_credentials() + .await + .map_err(|err| anyhow!(err))?; + let imap = BackendBuilder::new(account_config.clone(), imap_ctx); + self.sync(printer, account_name, imap).await + } + Some(backend) => bail!("backend {backend:?} not supported for synchronization"), + None => bail!("no backend configured for synchronization"), + } + } + + async fn sync( + self, + printer: &mut impl Printer, + account_name: &str, + right: BackendBuilder, + ) -> Result<()> { let included_folders = BTreeSet::from_iter(self.include_folder); let excluded_folders = BTreeSet::from_iter(self.exclude_folder); - let strategy = if !included_folders.is_empty() { + let folders_filter = if !included_folders.is_empty() { Some(FolderSyncStrategy::Include(included_folders)) } else if !excluded_folders.is_empty() { Some(FolderSyncStrategy::Exclude(excluded_folders)) @@ -106,19 +131,15 @@ impl AccountSyncCommand { None }; - let account = self.account.name.as_deref(); - let (toml_account_config, account_config) = - config.clone().into_account_configs(account, true)?; - let account_name = account_config.name.as_str(); - - let backend_builder = - AccountSyncBackendBuilder::new(toml_account_config, account_config.clone()).await?; - let sync_builder = AccountSyncBuilder::new(backend_builder.into())? - .with_dry_run(self.dry_run) - .with_some_folders_filter(strategy); + let sync_builder = + AccountSyncBuilder::try_new(right)?.with_some_folders_filter(folders_filter); if self.dry_run { - let report = sync_builder.sync().await?; + let report = sync_builder + .with_dry_run(true) + .sync() + .await + .map_err(|err| anyhow!(err))?; let mut hunks_count = report.folder.patch.len(); if !report.folder.patch.is_empty() { @@ -142,7 +163,7 @@ impl AccountSyncCommand { "Estimated patch length for account {account_name} to be synchronized: {hunks_count}" ))?; } else if printer.is_json() { - sync_builder.sync().await?; + sync_builder.sync().await.map_err(|err| anyhow!(err))?; printer.print(format!("Account {account_name} successfully synchronized!"))?; } else { let multi = MultiProgress::new(); @@ -214,7 +235,8 @@ impl AccountSyncCommand { async { Ok(()) } }) .sync() - .await?; + .await + .map_err(|err| anyhow!(err))?; let folders_patch_err = report .folder @@ -250,110 +272,3 @@ impl AccountSyncCommand { Ok(()) } } - -pub struct AccountSyncBackendBuilder { - toml_account_config: Arc, - builder: BackendBuilder, -} - -impl AccountSyncBackendBuilder { - pub async fn new( - toml_account_config: Arc, - account_config: Arc, - ) -> Result { - #[allow(unused)] - let used_backends = toml_account_config.get_used_backends(); - - #[cfg(feature = "imap")] - let is_imap_used = used_backends.contains(&BackendKind::Imap); - #[cfg(feature = "maildir")] - let is_maildir_used = used_backends.contains(&BackendKind::Maildir); - let is_maildir_for_sync_used = used_backends.contains(&BackendKind::MaildirForSync); - #[cfg(feature = "notmuch")] - let is_notmuch_used = used_backends.contains(&BackendKind::Notmuch); - - let backend_ctx_builder = BackendContextBuilder { - toml_account_config: toml_account_config.clone(), - account_config: account_config.clone(), - - #[cfg(feature = "imap")] - imap: { - let builder = toml_account_config - .imap - .as_ref() - .filter(|_| is_imap_used) - .map(Clone::clone) - .map(Arc::new) - .map(|config| { - ImapContextBuilder::new(account_config.clone(), config) - .with_prebuilt_credentials() - }); - match builder { - Some(builder) => Some(builder.await?), - None => None, - } - }, - - #[cfg(feature = "maildir")] - maildir: toml_account_config - .maildir - .as_ref() - .filter(|_| is_maildir_used) - .map(Clone::clone) - .map(Arc::new) - .map(|mdir_config| MaildirContextBuilder::new(account_config.clone(), mdir_config)), - - maildir_for_sync: Some(MaildirConfig { - root_dir: account_config.get_sync_dir()?, - }) - .filter(|_| is_maildir_for_sync_used) - .map(Arc::new) - .map(|mdir_config| MaildirContextBuilder::new(account_config.clone(), mdir_config)), - - #[cfg(feature = "notmuch")] - notmuch: toml_account_config - .notmuch - .as_ref() - .filter(|_| is_notmuch_used) - .map(Clone::clone) - .map(Arc::new) - .map(|notmuch_config| { - NotmuchContextBuilder::new(account_config.clone(), notmuch_config) - }), - - #[cfg(feature = "smtp")] - smtp: None, - - #[cfg(feature = "sendmail")] - sendmail: None, - }; - - let backend_builder = BackendBuilder::new(account_config.clone(), backend_ctx_builder); - - Ok(Self { - toml_account_config, - builder: backend_builder, - }) - } - - pub async fn build(self) -> Result { - Ok(Backend { - toml_account_config: self.toml_account_config, - backend: self.builder.build().await?, - }) - } -} - -impl Deref for AccountSyncBackendBuilder { - type Target = BackendBuilder; - - fn deref(&self) -> &Self::Target { - &self.builder - } -} - -impl From for BackendBuilder { - fn from(backend_builder: AccountSyncBackendBuilder) -> Self { - backend_builder.builder - } -} diff --git a/src/account/config.rs b/src/account/config.rs index a40ac30..ded830f 100644 --- a/src/account/config.rs +++ b/src/account/config.rs @@ -5,8 +5,6 @@ #[cfg(feature = "pgp")] use email::account::config::pgp::PgpConfig; -#[cfg(feature = "account-sync")] -use email::account::sync::config::SyncConfig; #[cfg(feature = "imap")] use email::imap::config::ImapConfig; #[cfg(feature = "maildir")] @@ -26,6 +24,25 @@ use crate::{ folder::config::FolderConfig, message::config::MessageConfig, }; +#[cfg(feature = "account-sync")] +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +pub struct SyncConfig { + pub backend: Option, + pub enable: Option, + pub dir: Option, +} + +impl From for email::account::sync::config::SyncConfig { + fn from(config: SyncConfig) -> Self { + Self { + enable: config.enable, + dir: config.dir, + ..Default::default() + } + } +} + /// Represents all existing kind of account config. #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] @@ -62,6 +79,13 @@ pub struct TomlAccountConfig { } impl TomlAccountConfig { + pub fn sync_kind(&self) -> Option<&BackendKind> { + self.sync + .as_ref() + .and_then(|sync| sync.backend.as_ref()) + .or(self.backend.as_ref()) + } + pub fn add_folder_kind(&self) -> Option<&BackendKind> { self.folder .as_ref() diff --git a/src/account/wizard.rs b/src/account/wizard.rs index 34f5c76..722be03 100644 --- a/src/account/wizard.rs +++ b/src/account/wizard.rs @@ -1,9 +1,9 @@ +#[cfg(feature = "account-sync")] +use crate::account::config::SyncConfig; use anyhow::{bail, Result}; #[cfg(feature = "account-sync")] use dialoguer::Confirm; use dialoguer::Input; -#[cfg(feature = "account-sync")] -use email::account::sync::config::SyncConfig; use email_address::EmailAddress; use std::str::FromStr; @@ -143,7 +143,7 @@ pub(crate) async fn configure() -> Result> { { let should_configure_sync = Confirm::new() .with_prompt(wizard_prompt!( - "Do you need an offline access to your account?" + "Do you need offline access for your account?" )) .default(false) .interact_opt()? diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 23b6044..74c529c 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,14 +1,12 @@ pub mod config; pub(crate) mod wizard; -use anyhow::Result; +use anyhow::{anyhow, Result}; use async_trait::async_trait; use std::{ops::Deref, sync::Arc}; #[cfg(feature = "imap")] use email::imap::{ImapContextBuilder, ImapContextSync}; -#[cfg(feature = "account-sync")] -use email::maildir::config::MaildirConfig; #[cfg(any(feature = "account-sync", feature = "maildir"))] use email::maildir::{MaildirContextBuilder, MaildirContextSync}; #[cfg(feature = "notmuch")] @@ -34,8 +32,14 @@ use email::{ purge::PurgeFolder, }, message::{ - add::AddMessage, copy::CopyMessages, delete::DeleteMessages, get::GetMessages, - peek::PeekMessages, r#move::MoveMessages, send::SendMessage, Messages, + add::AddMessage, + copy::CopyMessages, + delete::DeleteMessages, + get::GetMessages, + peek::PeekMessages, + r#move::MoveMessages, + send::{SendMessage, SendMessageThenSaveCopy}, + Messages, }, }; use serde::{Deserialize, Serialize}; @@ -45,38 +49,47 @@ use crate::{account::config::TomlAccountConfig, cache::IdMapper, envelope::Envel #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum BackendKind { + None, + #[cfg(feature = "imap")] Imap, + #[cfg(all(feature = "imap", feature = "account-sync"))] + ImapCache, + #[cfg(feature = "maildir")] Maildir, - #[cfg(feature = "account-sync")] - #[serde(skip_deserializing)] - MaildirForSync, + #[cfg(feature = "notmuch")] Notmuch, + #[cfg(feature = "smtp")] Smtp, + #[cfg(feature = "sendmail")] Sendmail, - None, } impl ToString for BackendKind { fn to_string(&self) -> String { let kind = match self { + Self::None => "None", + #[cfg(feature = "imap")] Self::Imap => "IMAP", + #[cfg(all(feature = "imap", feature = "account-sync"))] + Self::ImapCache => "IMAP cache", + #[cfg(feature = "maildir")] Self::Maildir => "Maildir", - #[cfg(feature = "account-sync")] - Self::MaildirForSync => "Maildir", + #[cfg(feature = "notmuch")] Self::Notmuch => "Notmuch", + #[cfg(feature = "smtp")] Self::Smtp => "SMTP", + #[cfg(feature = "sendmail")] Self::Sendmail => "Sendmail", - Self::None => "None", }; kind.to_string() @@ -91,12 +104,12 @@ pub struct BackendContextBuilder { #[cfg(feature = "imap")] pub imap: Option, + #[cfg(all(feature = "imap", feature = "account-sync"))] + pub imap_cache: Option, + #[cfg(feature = "maildir")] pub maildir: Option, - #[cfg(feature = "account-sync")] - pub maildir_for_sync: Option, - #[cfg(feature = "notmuch")] pub notmuch: Option, @@ -130,7 +143,27 @@ impl BackendContextBuilder { .with_prebuilt_credentials() }); match builder { - Some(builder) => Some(builder.await?), + Some(builder) => Some(builder.await.map_err(|err| anyhow!(err))?), + None => None, + } + }, + + #[cfg(all(feature = "imap", feature = "account-sync"))] + imap_cache: { + let builder = toml_account_config + .imap + .as_ref() + .filter(|_| kinds.contains(&&BackendKind::ImapCache)) + .map(Clone::clone) + .map(Arc::new) + .map(|imap_config| { + email::backend::context::BackendContextBuilder::try_to_sync_cache_builder( + &ImapContextBuilder::new(account_config.clone(), imap_config), + &account_config, + ) + }); + match builder { + Some(builder) => Some(builder?), None => None, } }, @@ -144,14 +177,6 @@ impl BackendContextBuilder { .map(Arc::new) .map(|mdir_config| MaildirContextBuilder::new(account_config.clone(), mdir_config)), - #[cfg(feature = "account-sync")] - maildir_for_sync: Some(MaildirConfig { - root_dir: account_config.get_sync_dir()?, - }) - .filter(|_| kinds.contains(&&BackendKind::MaildirForSync)) - .map(Arc::new) - .map(|mdir_config| MaildirContextBuilder::new(account_config.clone(), mdir_config)), - #[cfg(feature = "notmuch")] notmuch: toml_account_config .notmuch @@ -194,13 +219,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.add_folder_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.add_folder_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.add_folder()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.add_folder_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.add_folder()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.add_folder_with_some(&self.notmuch), _ => None, @@ -211,13 +236,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.list_folders_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.list_folders_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.list_folders()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.list_folders_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.list_folders()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.list_folders_with_some(&self.notmuch), _ => None, @@ -228,13 +253,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.expunge_folder_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.expunge_folder_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.expunge_folder()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.expunge_folder_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.expunge_folder()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.expunge_folder_with_some(&self.notmuch), _ => None, @@ -245,13 +270,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.purge_folder_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.purge_folder_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.purge_folder()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.purge_folder_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.purge_folder()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.purge_folder_with_some(&self.notmuch), _ => None, @@ -262,13 +287,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.delete_folder_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.delete_folder_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.delete_folder()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.delete_folder_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.delete_folder()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.delete_folder_with_some(&self.notmuch), _ => None, @@ -279,13 +304,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.get_envelope_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.get_envelope_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.get_envelope()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.get_envelope_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.get_envelope()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.get_envelope_with_some(&self.notmuch), _ => None, @@ -296,13 +321,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.list_envelopes_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.list_envelopes_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.list_envelopes()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.list_envelopes_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.list_envelopes()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.list_envelopes_with_some(&self.notmuch), _ => None, @@ -313,13 +338,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.watch_envelopes_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.watch_envelopes_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.watch_envelopes()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.watch_envelopes_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.watch_envelopes()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.watch_envelopes_with_some(&self.notmuch), _ => None, @@ -330,13 +355,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.add_flags_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.add_flags_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.add_flags()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.add_flags_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.add_flags()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.add_flags_with_some(&self.notmuch), _ => None, @@ -347,13 +372,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.set_flags_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.set_flags_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.set_flags()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.set_flags_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.set_flags()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.set_flags_with_some(&self.notmuch), _ => None, @@ -364,13 +389,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.remove_flags_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.remove_flags_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.remove_flags()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.remove_flags_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.remove_flags()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.remove_flags_with_some(&self.notmuch), _ => None, @@ -381,13 +406,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.add_message_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.add_message_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.add_message()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.add_message_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.add_message()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.add_message_with_some(&self.notmuch), _ => None, @@ -408,13 +433,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.peek_messages_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.peek_messages_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.peek_messages()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.peek_messages_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.peek_messages()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.peek_messages_with_some(&self.notmuch), _ => None, @@ -425,13 +450,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.get_messages_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.get_messages_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.get_messages()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.get_messages_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.get_messages()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.get_messages_with_some(&self.notmuch), _ => None, @@ -442,13 +467,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.copy_messages_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.copy_messages_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.copy_messages()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.copy_messages_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.copy_messages()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.copy_messages_with_some(&self.notmuch), _ => None, @@ -459,13 +484,13 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.move_messages_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.move_messages_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.move_messages()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.move_messages_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.move_messages()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.move_messages_with_some(&self.notmuch), _ => None, @@ -476,20 +501,20 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { match self.toml_account_config.delete_messages_kind() { #[cfg(feature = "imap")] Some(BackendKind::Imap) => self.delete_messages_with_some(&self.imap), + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { + let f = self.imap_cache.as_ref()?.delete_messages()?; + Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?))) + } #[cfg(feature = "maildir")] Some(BackendKind::Maildir) => self.delete_messages_with_some(&self.maildir), - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { - let f = self.maildir_for_sync.as_ref()?.delete_messages()?; - Some(Arc::new(move |ctx| f(ctx.maildir_for_sync.as_ref()?))) - } #[cfg(feature = "notmuch")] Some(BackendKind::Notmuch) => self.delete_messages_with_some(&self.notmuch), _ => None, } } - async fn build(self) -> Result { + async fn build(self) -> email::Result { let mut ctx = BackendContext::default(); #[cfg(feature = "imap")] @@ -497,16 +522,16 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder { ctx.imap = Some(imap.build().await?); } + #[cfg(all(feature = "imap", feature = "account-sync"))] + if let Some(maildir) = self.imap_cache { + ctx.imap_cache = Some(maildir.build().await?); + } + #[cfg(feature = "maildir")] if let Some(maildir) = self.maildir { ctx.maildir = Some(maildir.build().await?); } - #[cfg(feature = "account-sync")] - if let Some(maildir) = self.maildir_for_sync { - ctx.maildir_for_sync = Some(maildir.build().await?); - } - #[cfg(feature = "notmuch")] if let Some(notmuch) = self.notmuch { ctx.notmuch = Some(notmuch.build().await?); @@ -531,12 +556,12 @@ pub struct BackendContext { #[cfg(feature = "imap")] pub imap: Option, + #[cfg(all(feature = "imap", feature = "account-sync"))] + pub imap_cache: Option, + #[cfg(feature = "maildir")] pub maildir: Option, - #[cfg(feature = "account-sync")] - pub maildir_for_sync: Option, - #[cfg(feature = "notmuch")] pub notmuch: Option, @@ -609,7 +634,7 @@ impl Backend { Ok(Self { toml_account_config: toml_account_config.clone(), - backend: backend_builder.build().await?, + backend: backend_builder.build().await.map_err(|err| anyhow!(err))?, }) } @@ -629,8 +654,8 @@ impl Backend { } } - #[cfg(feature = "account-sync")] - Some(BackendKind::MaildirForSync) => { + #[cfg(all(feature = "imap", feature = "account-sync"))] + Some(BackendKind::ImapCache) => { id_mapper = IdMapper::new(&self.backend.account_config, folder)?; } @@ -653,7 +678,11 @@ impl Backend { ) -> Result { let backend_kind = self.toml_account_config.list_envelopes_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; - let envelopes = self.backend.list_envelopes(folder, opts).await?; + let envelopes = self + .backend + .list_envelopes(folder, opts) + .await + .map_err(|err| anyhow!(err))?; let envelopes = Envelopes::from_backend(&self.account_config, &id_mapper, envelopes)?; Ok(envelopes) } @@ -662,48 +691,87 @@ impl Backend { let backend_kind = self.toml_account_config.add_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.add_flags(folder, &ids, flags).await + self.backend + .add_flags(folder, &ids, flags) + .await + .map_err(|err| anyhow!(err)) } pub async fn add_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> { let backend_kind = self.toml_account_config.add_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.add_flag(folder, &ids, flag).await + self.backend + .add_flag(folder, &ids, flag) + .await + .map_err(|err| anyhow!(err)) } pub async fn set_flags(&self, folder: &str, ids: &[usize], flags: &Flags) -> Result<()> { let backend_kind = self.toml_account_config.set_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.set_flags(folder, &ids, flags).await + self.backend + .set_flags(folder, &ids, flags) + .await + .map_err(|err| anyhow!(err)) } pub async fn set_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> { let backend_kind = self.toml_account_config.set_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.set_flag(folder, &ids, flag).await + self.backend + .set_flag(folder, &ids, flag) + .await + .map_err(|err| anyhow!(err)) } pub async fn remove_flags(&self, folder: &str, ids: &[usize], flags: &Flags) -> Result<()> { let backend_kind = self.toml_account_config.remove_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.remove_flags(folder, &ids, flags).await + self.backend + .remove_flags(folder, &ids, flags) + .await + .map_err(|err| anyhow!(err)) } pub async fn remove_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> { let backend_kind = self.toml_account_config.remove_flags_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.remove_flag(folder, &ids, flag).await + self.backend + .remove_flag(folder, &ids, flag) + .await + .map_err(|err| anyhow!(err)) } pub async fn add_message(&self, folder: &str, email: &[u8]) -> Result { let backend_kind = self.toml_account_config.add_message_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; - let id = self.backend.add_message(folder, email).await?; + let id = self + .backend + .add_message(folder, email) + .await + .map_err(|err| anyhow!(err))?; + id_mapper.create_alias(&*id)?; + Ok(id) + } + + pub async fn add_message_with_flags( + &self, + folder: &str, + email: &[u8], + flags: &Flags, + ) -> Result { + let backend_kind = self.toml_account_config.add_message_kind(); + let id_mapper = self.build_id_mapper(folder, backend_kind)?; + let id = self + .backend + .add_message_with_flags(folder, email, flags) + .await + .map_err(|err| anyhow!(err))?; id_mapper.create_alias(&*id)?; Ok(id) } @@ -712,14 +780,20 @@ impl Backend { let backend_kind = self.toml_account_config.get_messages_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.peek_messages(folder, &ids).await + self.backend + .peek_messages(folder, &ids) + .await + .map_err(|err| anyhow!(err)) } pub async fn get_messages(&self, folder: &str, ids: &[usize]) -> Result { let backend_kind = self.toml_account_config.get_messages_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.get_messages(folder, &ids).await + self.backend + .get_messages(folder, &ids) + .await + .map_err(|err| anyhow!(err)) } pub async fn copy_messages( @@ -734,6 +808,7 @@ impl Backend { self.backend .copy_messages(from_folder, to_folder, &ids) .await + .map_err(|err| anyhow!(err)) } pub async fn move_messages( @@ -748,13 +823,31 @@ impl Backend { self.backend .move_messages(from_folder, to_folder, &ids) .await + .map_err(|err| anyhow!(err)) } pub async fn delete_messages(&self, folder: &str, ids: &[usize]) -> Result<()> { let backend_kind = self.toml_account_config.delete_messages_kind(); let id_mapper = self.build_id_mapper(folder, backend_kind)?; let ids = Id::multiple(id_mapper.get_ids(ids)?); - self.backend.delete_messages(folder, &ids).await + self.backend + .delete_messages(folder, &ids) + .await + .map_err(|err| anyhow!(err)) + } + + pub async fn send_message_then_save_copy(&self, msg: &[u8]) -> Result<()> { + self.backend + .send_message_then_save_copy(msg) + .await + .map_err(|err| anyhow!(err)) + } + + pub async fn watch_envelopes(&self, folder: &str) -> Result<()> { + self.backend + .watch_envelopes(folder) + .await + .map_err(|err| anyhow!(err)) } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 62e3cc3..6e4d3eb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -213,7 +213,10 @@ impl TomlConfig { #[cfg(feature = "account-sync")] if let Some(true) = toml_account_config.sync.as_ref().and_then(|c| c.enable) { if !disable_cache { - toml_account_config.backend = Some(BackendKind::MaildirForSync); + toml_account_config.backend = match toml_account_config.backend { + Some(BackendKind::Imap) => Some(BackendKind::ImapCache), + backend => backend, + } } } @@ -260,7 +263,7 @@ impl TomlConfig { }), template: config.template, #[cfg(feature = "account-sync")] - sync: config.sync, + sync: config.sync.map(Into::into), #[cfg(feature = "pgp")] pgp: config.pgp, }, diff --git a/src/email/envelope/command/list.rs b/src/email/envelope/command/list.rs index a4387a2..7b440e8 100644 --- a/src/email/envelope/command/list.rs +++ b/src/email/envelope/command/list.rs @@ -2,8 +2,8 @@ use anyhow::Result; use ariadne::{Color, Label, Report, ReportKind, Source}; use clap::Parser; use email::{ - backend::feature::BackendFeatureSource, envelope::list::ListEnvelopesOptions, - search_query::SearchEmailsQuery, + backend::feature::BackendFeatureSource, email::search_query, + envelope::list::ListEnvelopesOptions, search_query::SearchEmailsQuery, }; use log::info; use std::process::exit; @@ -171,7 +171,7 @@ impl ListEnvelopesCommand { Some(Ok(query)) => Some(query), Some(Err(main_err)) => { let source = "query"; - let email::search_query::parser::Error::ParseError(errs, query) = &main_err; + let search_query::error::Error::ParseError(errs, query) = &main_err; for err in errs { Report::build(ReportKind::Error, source, err.span().start) .with_message(main_err.to_string()) diff --git a/src/email/envelope/command/watch.rs b/src/email/envelope/command/watch.rs index a7c0ceb..0323571 100644 --- a/src/email/envelope/command/watch.rs +++ b/src/email/envelope/command/watch.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::Parser; -use email::{backend::feature::BackendFeatureSource, envelope::watch::WatchEnvelopes}; +use email::backend::feature::BackendFeatureSource; use log::info; #[cfg(feature = "account-sync")] diff --git a/src/email/message/command/send.rs b/src/email/message/command/send.rs index 6d84477..98fa278 100644 --- a/src/email/message/command/send.rs +++ b/src/email/message/command/send.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::Parser; -use email::{backend::feature::BackendFeatureSource, message::send::SendMessageThenSaveCopy}; +use email::backend::feature::BackendFeatureSource; use log::info; use std::io::{self, BufRead, IsTerminal}; diff --git a/src/email/message/template/command/send.rs b/src/email/message/template/command/send.rs index bbd24ab..ea40a33 100644 --- a/src/email/message/template/command/send.rs +++ b/src/email/message/template/command/send.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::Parser; -use email::{backend::feature::BackendFeatureSource, message::send::SendMessageThenSaveCopy}; +use email::backend::feature::BackendFeatureSource; use log::info; use mml::MmlCompilerBuilder; use std::io::{self, BufRead, IsTerminal}; diff --git a/src/folder/command/add.rs b/src/folder/command/add.rs index be3f26e..214707c 100644 --- a/src/folder/command/add.rs +++ b/src/folder/command/add.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use email::{backend::feature::BackendFeatureSource, folder::add::AddFolder}; use log::info; @@ -48,7 +48,10 @@ impl AddFolderCommand { ) .await?; - backend.add_folder(folder).await?; + backend + .add_folder(folder) + .await + .map_err(|err| anyhow!(err))?; printer.print(format!("Folder {folder} successfully created!")) } diff --git a/src/folder/command/delete.rs b/src/folder/command/delete.rs index 552d815..61569bf 100644 --- a/src/folder/command/delete.rs +++ b/src/folder/command/delete.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use dialoguer::Confirm; use email::{backend::feature::BackendFeatureSource, folder::delete::DeleteFolder}; @@ -61,7 +61,10 @@ impl FolderDeleteCommand { ) .await?; - backend.delete_folder(folder).await?; + backend + .delete_folder(folder) + .await + .map_err(|err| anyhow!(err))?; printer.print(format!("Folder {folder} successfully deleted!")) } diff --git a/src/folder/command/expunge.rs b/src/folder/command/expunge.rs index ba10a0f..b4734a3 100644 --- a/src/folder/command/expunge.rs +++ b/src/folder/command/expunge.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use email::{backend::feature::BackendFeatureSource, folder::expunge::ExpungeFolder}; use log::info; @@ -49,7 +49,10 @@ impl FolderExpungeCommand { ) .await?; - backend.expunge_folder(folder).await?; + backend + .expunge_folder(folder) + .await + .map_err(|err| anyhow!(err))?; printer.print(format!("Folder {folder} successfully expunged!")) } diff --git a/src/folder/command/list.rs b/src/folder/command/list.rs index 3cf2860..6cfc211 100644 --- a/src/folder/command/list.rs +++ b/src/folder/command/list.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use email::{backend::feature::BackendFeatureSource, folder::list::ListFolders}; use log::info; @@ -50,7 +50,11 @@ impl FolderListCommand { ) .await?; - let folders: Folders = backend.list_folders().await?.into(); + let folders: Folders = backend + .list_folders() + .await + .map_err(|err| anyhow!(err))? + .into(); printer.print_table( Box::new(folders), diff --git a/src/folder/command/purge.rs b/src/folder/command/purge.rs index ec40bec..93f61d6 100644 --- a/src/folder/command/purge.rs +++ b/src/folder/command/purge.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::Parser; use dialoguer::Confirm; use email::{backend::feature::BackendFeatureSource, folder::purge::PurgeFolder}; @@ -61,7 +61,10 @@ impl FolderPurgeCommand { ) .await?; - backend.purge_folder(folder).await?; + backend + .purge_folder(folder) + .await + .map_err(|err| anyhow!(err))?; printer.print(format!("Folder {folder} successfully purged!")) } diff --git a/src/ui/editor.rs b/src/ui/editor.rs index 2d53e60..1553e9a 100644 --- a/src/ui/editor.rs +++ b/src/ui/editor.rs @@ -4,7 +4,6 @@ use email::{ email::utils::{local_draft_path, remove_local_draft}, flag::{Flag, Flags}, folder::DRAFTS, - message::{add::AddMessage, send::SendMessageThenSaveCopy}, template::Template, }; use log::debug;