refactor builders and sync

This commit is contained in:
Clément DOUIN 2023-06-08 23:35:36 +02:00
parent ab1e8b7e45
commit c254f64569
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
14 changed files with 405 additions and 364 deletions

65
Cargo.lock generated
View file

@ -1009,6 +1009,21 @@ dependencies = [
"new_debug_unreachable",
]
[[package]]
name = "futures"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.25"
@ -1016,6 +1031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -1024,6 +1040,17 @@ version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.25"
@ -1074,6 +1101,7 @@ version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
@ -1187,7 +1215,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "himalaya"
version = "0.8.1"
version = "0.8.1-beta"
dependencies = [
"anyhow",
"atty",
@ -1217,6 +1245,7 @@ dependencies = [
"tempfile",
"termcolor",
"terminal_size",
"tokio",
"toml",
"toml_edit",
"unicode-width",
@ -2170,15 +2199,16 @@ dependencies = [
[[package]]
name = "pimalaya-email"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e85cdc815af8f35c67941eaac74dc2e6a8d01c3ed498b2d36eaaf9bc8a517e6"
checksum = "6b881982050e29d8f9141a57f0be3f87471394462a2f1695471d5a81f44b4b3a"
dependencies = [
"advisory-lock",
"ammonia",
"chrono",
"convert_case",
"dirs",
"futures",
"html-escape",
"imap",
"imap-proto",
@ -2214,9 +2244,9 @@ dependencies = [
[[package]]
name = "pimalaya-email-tpl"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25da44b70885d9636ddcb89181a2f0155acff3816697ec7af07a5f6f17cdc0e"
checksum = "f640b701926112e28b025cea9c9d50a2bfe329862eeb4acbf2d62edeb53fb19b"
dependencies = [
"chumsky 0.9.0",
"log",
@ -2231,9 +2261,9 @@ dependencies = [
[[package]]
name = "pimalaya-keyring"
version = "0.0.1"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae5fed5fff1897b4964a5e8efbcd5e41e4492fe7947f827745fe9a14a555fe94"
checksum = "cef72189d57c09a3c682769e7c99d44772bb1164173e77e77cef837ba8dda1b4"
dependencies = [
"keyring",
"log",
@ -2242,9 +2272,9 @@ dependencies = [
[[package]]
name = "pimalaya-oauth2"
version = "0.0.2"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9bec4262b62b6b14ffa244727e3d86d69e608e664e162e2c73332bed3b3f8a1"
checksum = "6a28b3da9e56304f14bd46f769bfae52de32bd3fc2795946079e66838a178ce8"
dependencies = [
"log",
"oauth2",
@ -2265,13 +2295,12 @@ dependencies = [
[[package]]
name = "pimalaya-secret"
version = "0.0.1"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b585f585653ac7f957a608d8cdffd81be6561c2ad92fa82a1e72ed62a1bb31e0"
checksum = "9338dc84e5ec9fc25f3a36d82ed68ffe4888ad728ca1aa42228e27648ddff01f"
dependencies = [
"log",
"pimalaya-keyring",
"pimalaya-oauth2",
"pimalaya-process",
"thiserror",
]
@ -3153,9 +3182,21 @@ dependencies = [
"num_cpus",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.42.0",
]
[[package]]
name = "tokio-macros"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.104",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"

View file

@ -1,7 +1,7 @@
[package]
name = "himalaya"
description = "CLI to manage your emails."
version = "0.8.1"
version = "0.8.1-beta"
authors = ["soywod <clement.douin@posteo.net>"]
edition = "2021"
license = "MIT"
@ -44,16 +44,17 @@ indicatif = "0.17"
log = "0.4"
md5 = "0.7.0"
once_cell = "1.16.0"
pimalaya-email = "=0.9.0"
pimalaya-keyring = "=0.0.1"
pimalaya-oauth2 = "=0.0.2"
pimalaya-email = { version = "=0.10.0", default-features = false }
pimalaya-keyring = "=0.0.4"
pimalaya-oauth2 = "=0.0.3"
pimalaya-process = "=0.0.2"
pimalaya-secret = "=0.0.1"
pimalaya-secret = "=0.0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
shellexpand = "2.1"
termcolor = "1.1"
terminal_size = "0.1"
tokio = { version = "1.23", default-features = false, features = ["macros"] }
toml = "0.7.4"
toml_edit = "0.19.8"
unicode-width = "0.1"

View file

@ -8,11 +8,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1682835640,
"narHash": "sha256-rAYEOd4nZFLjDlrF9KNlcopPKNVtr1svSXcEValVRMY=",
"lastModified": 1686032467,
"narHash": "sha256-KUCS237H0G1QGx5ehhEmh5yKtcDGCxvVXVtz8xEDAKE=",
"owner": "nix-community",
"repo": "fenix",
"rev": "006b429d3c493f4c5b1743a94f71ad961c7693ab",
"rev": "1a3e0f661119a7435099b118912d65bdbbf3bb11",
"type": "github"
},
"original": {
@ -42,11 +42,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
@ -97,11 +97,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1682817260,
"narHash": "sha256-kFMXzKNj4d/0Iqbm5l57rHSLyUeyCLMuvlROZIuuhvk=",
"lastModified": 1685883127,
"narHash": "sha256-zPDaPNrAtBnO24rNqjHLINHsqTdRbgWy1c/TL3EdwlM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "db1e4eeb0f9a9028bcb920e00abbc1409dd3ef36",
"rev": "d4a9ff82fc18723219b60c66fb2ccb0734c460eb",
"type": "github"
},
"original": {
@ -124,11 +124,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1682792082,
"narHash": "sha256-1nuP2rqipsdB8IJ3N5ws3FQm4dX3mKIueIrCUSu1bWw=",
"lastModified": 1685984106,
"narHash": "sha256-dOEuU1AuASOWdXT/SbVpD8uX7JjiW3lCp08SbviHuww=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "7bcb4c2ef23e151a639ff918fbb8ab9d521eabb9",
"rev": "d42d55feaafa71e14521bbfe6e7011fbb41980f0",
"type": "github"
},
"original": {

View file

@ -16,10 +16,7 @@ pub enum IdMapper {
}
impl IdMapper {
fn find_closest_db_path<D>(dir: D) -> PathBuf
where
D: AsRef<Path>,
{
pub fn find_closest_db_path(dir: impl AsRef<Path>) -> PathBuf {
let mut db_path = dir.as_ref().join(ID_MAPPER_DB_FILE_NAME);
let mut db_parent_dir = dir.as_ref().parent();
@ -40,20 +37,20 @@ impl IdMapper {
}
pub fn new(backend: &dyn Backend, account: &str, folder: &str) -> Result<Self> {
let mut db_path = PathBuf::default();
if let Some(backend) = backend.as_any().downcast_ref::<MaildirBackend>() {
db_path = Self::find_closest_db_path(&backend.path());
}
#[cfg(feature = "imap-backend")]
if backend.as_any().is::<ImapBackend>() {
return Ok(Self::Dummy);
return Ok(IdMapper::Dummy);
}
let mut db_path = PathBuf::new();
if let Some(backend) = backend.as_any().downcast_ref::<MaildirBackend>() {
db_path = Self::find_closest_db_path(backend.path())
}
#[cfg(feature = "notmuch-backend")]
if let Some(backend) = backend.as_any().downcast_ref::<NotmuchBackend>() {
db_path = Self::find_closest_db_path(&backend.path());
db_path = Self::find_closest_db_path(backend.path())
}
let digest = md5::compute(account.to_string() + folder);

View file

@ -1,12 +1,13 @@
#[cfg(feature = "imap-backend")]
use pimalaya_email::ImapConfig;
#[cfg(feature = "notmuch-backend")]
use pimalaya_email::NotmuchConfig;
use pimalaya_email::{
folder::sync::Strategy as SyncFoldersStrategy, BackendConfig, EmailHooks, EmailTextPlainFormat,
ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
SenderConfig, SendmailConfig, SmtpAuthConfig, SmtpConfig,
BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy, MaildirConfig,
OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SendmailConfig,
};
#[cfg(feature = "imap-backend")]
use pimalaya_email::{ImapAuthConfig, ImapConfig};
#[cfg(feature = "smtp-sender")]
use pimalaya_email::{SmtpAuthConfig, SmtpConfig};
use pimalaya_keyring::Entry;
use pimalaya_process::{Cmd, Pipeline, SingleCmd};
use pimalaya_secret::Secret;
@ -74,14 +75,16 @@ impl From<OptionCmd> for Option<Cmd> {
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Secret", rename_all = "kebab-case")]
pub enum SecretDef {
Raw(String),
#[serde(with = "CmdDef")]
Cmd(Cmd),
#[serde(with = "EntryDef")]
Keyring(Entry),
#[serde(with = "EntryDef", rename = "keyring")]
KeyringEntry(Entry),
#[default]
Undefined,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
@ -134,6 +137,7 @@ pub struct ImapConfigDef {
pub watch_cmds: Option<Vec<String>>,
}
#[cfg(feature = "imap-backend")]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "ImapAuthConfig", tag = "imap-auth")]
pub enum ImapAuthConfigDef {
@ -150,7 +154,7 @@ pub struct ImapPasswdConfigDef {
rename = "imap-passwd",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub passwd: Secret,
}
@ -166,7 +170,7 @@ pub struct ImapOAuth2ConfigDef {
rename = "imap-oauth2-client-secret",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub client_secret: Secret,
#[serde(rename = "imap-oauth2-auth-url")]
@ -177,20 +181,30 @@ pub struct ImapOAuth2ConfigDef {
rename = "imap-oauth2-access-token",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub access_token: Secret,
#[serde(
rename = "imap-oauth2-refresh-token",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub refresh_token: Secret,
#[serde(flatten, with = "ImapOAuth2ScopesDef")]
pub scopes: OAuth2Scopes,
#[serde(rename = "imap-oauth2-pkce", default)]
pub pkce: bool,
#[serde(
rename = "imap-oauth2-redirect-host",
default = "OAuth2Config::default_redirect_host"
)]
pub redirect_host: String,
#[serde(
rename = "imap-oauth2-redirect-port",
default = "OAuth2Config::default_redirect_port"
)]
pub redirect_port: u16,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
@ -236,12 +250,14 @@ pub enum EmailTextPlainFormatDef {
pub enum SenderConfigDef {
#[default]
None,
#[cfg(feature = "smtp-sender")]
#[serde(with = "SmtpConfigDef")]
Smtp(SmtpConfig),
#[serde(with = "SendmailConfigDef")]
Sendmail(SendmailConfig),
}
#[cfg(feature = "smtp-sender")]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SmtpConfig")]
struct SmtpConfigDef {
@ -261,6 +277,7 @@ struct SmtpConfigDef {
pub auth: SmtpAuthConfig,
}
#[cfg(feature = "smtp-sender")]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SmtpAuthConfig", tag = "smtp-auth")]
pub enum SmtpAuthConfigDef {
@ -277,7 +294,7 @@ pub struct SmtpPasswdConfigDef {
rename = "smtp-passwd",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub passwd: Secret,
}
@ -293,7 +310,7 @@ pub struct SmtpOAuth2ConfigDef {
rename = "smtp-oauth2-client-secret",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub client_secret: Secret,
#[serde(rename = "smtp-oauth2-auth-url")]
@ -304,20 +321,30 @@ pub struct SmtpOAuth2ConfigDef {
rename = "smtp-oauth2-access-token",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub access_token: Secret,
#[serde(
rename = "smtp-oauth2-refresh-token",
with = "SecretDef",
default,
skip_serializing_if = "Secret::is_undefined_entry"
skip_serializing_if = "Secret::is_undefined"
)]
pub refresh_token: Secret,
#[serde(flatten, with = "SmtpOAuth2ScopesDef")]
pub scopes: OAuth2Scopes,
#[serde(rename = "smtp-oauth2-pkce", default)]
pub pkce: bool,
#[serde(
rename = "imap-oauth2-redirect-host",
default = "OAuth2Config::default_redirect_host"
)]
pub redirect_host: String,
#[serde(
rename = "imap-oauth2-redirect-port",
default = "OAuth2Config::default_redirect_port"
)]
pub redirect_port: u16,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
@ -347,8 +374,8 @@ pub struct EmailHooksDef {
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SyncFoldersStrategy", rename_all = "kebab-case")]
pub enum SyncFoldersStrategyDef {
#[serde(remote = "FolderSyncStrategy", rename_all = "kebab-case")]
pub enum FolderSyncStrategyDef {
#[default]
All,
#[serde(alias = "only")]

View file

@ -3,7 +3,7 @@
use anyhow::Result;
use clap::{Arg, ArgAction, ArgMatches, Command};
use log::info;
use pimalaya_email::folder::sync::Strategy as SyncFoldersStrategy;
use pimalaya_email::FolderSyncStrategy;
use std::collections::HashSet;
use crate::{folder, ui::table};
@ -25,7 +25,7 @@ pub enum Cmd {
/// Represents the list accounts command.
List(table::args::MaxTableWidth),
/// Represents the sync account command.
Sync(Option<SyncFoldersStrategy>, DryRun),
Sync(Option<FolderSyncStrategy>, DryRun),
/// Configure the current selected account.
Configure(Reset),
}
@ -39,15 +39,15 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
let include = folder::args::parse_include_arg(m);
let exclude = folder::args::parse_exclude_arg(m);
let folders_strategy = if let Some(folder) = folder::args::parse_source_arg(m) {
Some(SyncFoldersStrategy::Include(HashSet::from_iter([
Some(FolderSyncStrategy::Include(HashSet::from_iter([
folder.to_owned()
])))
} else if !include.is_empty() {
Some(SyncFoldersStrategy::Include(include.to_owned()))
Some(FolderSyncStrategy::Include(include.to_owned()))
} else if !exclude.is_empty() {
Some(SyncFoldersStrategy::Exclude(exclude))
Some(FolderSyncStrategy::Exclude(exclude))
} else if folder::args::parse_all_arg(m) {
Some(SyncFoldersStrategy::All)
Some(FolderSyncStrategy::All)
} else {
None
};

View file

@ -8,8 +8,8 @@ use pimalaya_email::ImapAuthConfig;
#[cfg(feature = "smtp-sender")]
use pimalaya_email::SmtpAuthConfig;
use pimalaya_email::{
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
EmailTextPlainFormat, SenderConfig,
AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy,
SenderConfig,
};
use pimalaya_process::Cmd;
use serde::{Deserialize, Serialize};
@ -76,10 +76,10 @@ pub struct DeserializedAccountConfig {
pub sync_dir: Option<PathBuf>,
#[serde(
default,
with = "SyncFoldersStrategyDef",
skip_serializing_if = "SyncFoldersStrategy::is_default"
with = "FolderSyncStrategyDef",
skip_serializing_if = "FolderSyncStrategy::is_default"
)]
pub sync_folders_strategy: SyncFoldersStrategy,
pub sync_folders_strategy: FolderSyncStrategy,
#[serde(flatten, with = "BackendConfigDef")]
pub backend: BackendConfig,
@ -197,16 +197,16 @@ impl DeserializedAccountConfig {
if let BackendConfig::Imap(config) = &mut backend {
match &mut config.auth {
ImapAuthConfig::Passwd(secret) => {
secret.replace_undefined_entry_with(format!("{name}-imap-passwd"));
secret.set_keyring_entry_if_undefined(format!("{name}-imap-passwd"));
}
ImapAuthConfig::OAuth2(config) => {
config.client_secret.replace_undefined_entry_with(format!(
config.client_secret.set_keyring_entry_if_undefined(format!(
"{name}-imap-oauth2-client-secret"
));
config.access_token.replace_undefined_entry_with(format!(
config.access_token.set_keyring_entry_if_undefined(format!(
"{name}-imap-oauth2-access-token"
));
config.refresh_token.replace_undefined_entry_with(format!(
config.refresh_token.set_keyring_entry_if_undefined(format!(
"{name}-imap-oauth2-refresh-token"
));
}
@ -222,16 +222,16 @@ impl DeserializedAccountConfig {
if let SenderConfig::Smtp(config) = &mut sender {
match &mut config.auth {
SmtpAuthConfig::Passwd(secret) => {
secret.replace_undefined_entry_with(format!("{name}-smtp-passwd"));
secret.set_keyring_entry_if_undefined(format!("{name}-smtp-passwd"));
}
SmtpAuthConfig::OAuth2(config) => {
config.client_secret.replace_undefined_entry_with(format!(
config.client_secret.set_keyring_entry_if_undefined(format!(
"{name}-smtp-oauth2-client-secret"
));
config.access_token.replace_undefined_entry_with(format!(
config.access_token.set_keyring_entry_if_undefined(format!(
"{name}-smtp-oauth2-access-token"
));
config.refresh_token.replace_undefined_entry_with(format!(
config.refresh_token.set_keyring_entry_if_undefined(format!(
"{name}-smtp-oauth2-refresh-token"
));
}

View file

@ -3,12 +3,17 @@
//! This module gathers all account actions triggered by the CLI.
use anyhow::Result;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
use log::{info, trace, warn};
use once_cell::sync::Lazy;
#[cfg(feature = "imap-backend")]
use pimalaya_email::ImapAuthConfig;
#[cfg(feature = "smtp-sender")]
use pimalaya_email::SmtpAuthConfig;
use pimalaya_email::{
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
BackendSyncBuilder, BackendSyncProgressEvent, ImapAuthConfig, SenderConfig, SmtpAuthConfig,
AccountConfig, BackendConfig, BackendSyncBuilder, BackendSyncProgressEvent, SenderConfig,
};
use std::{collections::HashMap, sync::Mutex};
use crate::{
config::{
@ -19,6 +24,21 @@ use crate::{
Accounts,
};
const MAIN_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap()
});
const SUB_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
ProgressStyle::with_template(
" {prefix:.bold} — {wide_msg:.dim} \n {wide_bar:.black/black} {percent}% ",
)
.unwrap()
});
const SUB_PROGRESS_DONE_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
ProgressStyle::with_template(" {prefix:.bold} \n {wide_bar:.green} {percent}% ").unwrap()
});
/// Configure the current selected account
pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
info!("entering the configure account handler");
@ -103,25 +123,16 @@ pub fn list<'a, P: Printer>(
/// Synchronizes the account defined using argument `-a|--account`. If
/// no account given, synchronizes the default one.
pub fn sync<P: Printer>(
account_config: &AccountConfig,
pub fn sync<'a, P: Printer>(
printer: &mut P,
backend: &dyn Backend,
folders_strategy: Option<SyncFoldersStrategy>,
sync_builder: BackendSyncBuilder<'a>,
dry_run: bool,
) -> Result<()> {
info!("entering the sync accounts handler");
trace!("dry run: {dry_run}");
trace!("folders strategy: {folders_strategy:#?}");
let mut sync_builder = BackendSyncBuilder::new(account_config);
if let Some(strategy) = folders_strategy {
sync_builder = sync_builder.folders_strategy(strategy);
}
if dry_run {
let report = sync_builder.dry_run(true).sync(backend)?;
let report = sync_builder.sync()?;
let mut hunks_count = report.folders_patch.len();
if !report.folders_patch.is_empty() {
@ -142,98 +153,77 @@ pub fn sync<P: Printer>(
}
printer.print(format!(
"Estimated patch length for account {} to be synchronized: {hunks_count}",
backend.name(),
"Estimated patch length for account to be synchronized: {hunks_count}",
))?;
} else if printer.is_json() {
sync_builder.sync(backend)?;
printer.print(format!(
"Account {} successfully synchronized!",
backend.name()
))?;
sync_builder.sync()?;
printer.print("Account successfully synchronized!")?;
} else {
let multi = MultiProgress::new();
let progress = multi.add(
ProgressBar::new(0).with_style(
ProgressStyle::with_template(
" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} {pos}/{len} ",
)
.unwrap(),
),
let sub_progresses = Mutex::new(HashMap::new());
let main_progress = multi.add(
ProgressBar::new(100)
.with_style(MAIN_PROGRESS_STYLE.clone())
.with_message("Synchronizing folders…"),
);
// Force the progress bar to show
main_progress.set_position(0);
let report = sync_builder
.on_progress(|evt| {
.with_on_progress(move |evt| {
use BackendSyncProgressEvent::*;
Ok(match evt {
GetLocalCachedFolders => {
progress.set_length(4);
progress.set_position(0);
progress.set_message("Getting local cached folders…");
ApplyFolderPatches(..) => {
main_progress.inc(3);
}
GetLocalFolders => {
progress.inc(1);
progress.set_message("Getting local maildir folders…");
ApplyEnvelopePatches(patches) => {
let mut envelopes_progresses = sub_progresses.lock().unwrap();
let patches_len = patches.values().fold(0, |sum, patch| sum + patch.len());
main_progress.set_length((110 * patches_len / 100) as u64);
main_progress.set_position((5 * patches_len / 100) as u64);
main_progress.set_message("Synchronizing envelopes…");
for (folder, patch) in patches {
let progress = ProgressBar::new(patch.len() as u64)
.with_style(SUB_PROGRESS_STYLE.clone())
.with_prefix(folder.clone())
.with_finish(ProgressFinish::AndClear);
let progress = multi.add(progress);
envelopes_progresses.insert(folder, progress.clone());
}
}
GetRemoteCachedFolders => {
progress.inc(1);
progress.set_message("Getting remote cached folders…");
ApplyEnvelopeHunk(hunk) => {
main_progress.inc(1);
let mut progresses = sub_progresses.lock().unwrap();
if let Some(progress) = progresses.get_mut(hunk.folder()) {
progress.inc(1);
if progress.position() == (progress.length().unwrap() - 1) {
progress.set_style(SUB_PROGRESS_DONE_STYLE.clone())
} else {
progress.set_message(format!("{hunk}"));
}
}
}
GetRemoteFolders => {
progress.inc(1);
progress.set_message("Getting remote folders…");
ApplyEnvelopeCachePatch(_patch) => {
main_progress.set_length(100);
main_progress.set_position(95);
main_progress.set_message("Saving cache database…");
}
BuildFoldersPatch => {
progress.inc(1);
progress.set_message("Building patch…");
}
ProcessFoldersPatch(n) => {
progress.set_length(n as u64);
progress.set_position(0);
progress.set_message("Processing patch…");
}
ProcessFolderHunk(msg) => {
progress.inc(1);
progress.set_message(msg + "");
}
StartEnvelopesSync(folder, n, len) => {
multi.println(format!("[{n:2}/{len}] {folder}")).unwrap();
progress.reset();
}
GetLocalCachedEnvelopes => {
progress.set_length(4);
progress.set_message("Getting local cached envelopes…");
}
GetLocalEnvelopes => {
progress.inc(1);
progress.set_message("Getting local maildir envelopes…");
}
GetRemoteCachedEnvelopes => {
progress.inc(1);
progress.set_message("Getting remote cached envelopes…");
}
GetRemoteEnvelopes => {
progress.inc(1);
progress.set_message("Getting remote envelopes…");
}
BuildEnvelopesPatch => {
progress.inc(1);
progress.set_message("Building patch…");
}
ProcessEnvelopesPatch(n) => {
progress.set_length(n as u64);
progress.set_position(0);
progress.set_message("Processing patch…");
}
ProcessEnvelopeHunk(msg) => {
progress.inc(1);
progress.set_message(msg + "");
ExpungeFolders(folders) => {
let mut progresses = sub_progresses.lock().unwrap();
for progress in progresses.values() {
progress.finish_and_clear()
}
progresses.clear();
main_progress.set_position(100);
main_progress.set_message(format!("Expunging {} folders…", folders.len()));
}
_ => (),
})
})
.sync(backend)?;
progress.finish_and_clear();
.sync()?;
let folders_patch_err = report
.folders_patch
@ -268,18 +258,14 @@ pub fn sync<P: Printer>(
}
}
if !report.envelopes_cache_patch.1.is_empty() {
if let Some(err) = report.envelopes_cache_patch.1 {
printer.print_log("")?;
printer.print_log("Error occured while applying the envelopes cache patch:")?;
for err in report.envelopes_cache_patch.1 {
printer.print_log(format!(" - {err}"))?;
}
printer.print_log(format!(
"Error occured while applying the envelopes cache patch: {err}"
))?;
}
printer.print(format!(
"Account {} successfully synchronized!",
backend.name()
))?;
printer.print("Account successfully synchronized!")?;
}
Ok(())

View file

@ -2,13 +2,15 @@
//!
//! This module gathers all IMAP handlers triggered by the CLI.
use anyhow::{Context, Result};
use anyhow::Result;
use pimalaya_email::ImapBackend;
pub fn notify(imap: &ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.notify(keepalive, folder).context("cannot imap notify")
pub fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.notify(keepalive, folder)?;
Ok(())
}
pub fn watch(imap: &ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.watch(keepalive, folder).context("cannot imap watch")
pub fn watch(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.watch(keepalive, folder)?;
Ok(())
}

View file

@ -4,7 +4,7 @@ use pimalaya_email::{
BackendConfig, ImapAuthConfig, ImapConfig, OAuth2Config, OAuth2Method, OAuth2Scopes,
PasswdConfig,
};
use pimalaya_oauth2::AuthorizationCodeGrant;
use pimalaya_oauth2::{AuthorizationCodeGrant, Client};
use pimalaya_secret::Secret;
use crate::{
@ -84,8 +84,8 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<BackendConfig
let config = match secret {
Some(idx) if SECRETS[idx] == KEYRING => {
Secret::new_keyring(format!("{account_name}-imap-passwd"))
.set(prompt_passwd("IMAP password")?)?;
Secret::new_keyring_entry(format!("{account_name}-imap-passwd"))
.set_keyring_entry_secret(prompt_passwd("IMAP password")?)?;
PasswdConfig::default()
}
Some(idx) if SECRETS[idx] == RAW => PasswdConfig {
@ -125,8 +125,8 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<BackendConfig
let client_secret: String = Password::with_theme(&*THEME)
.with_prompt("IMAP OAuth 2.0 client secret")
.interact()?;
Secret::new_keyring(format!("{account_name}-imap-oauth2-client-secret"))
.set(&client_secret)?;
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))
.set_keyring_entry_secret(&client_secret)?;
config.auth_url = Input::with_theme(&*THEME)
.with_prompt("IMAP OAuth 2.0 authorization URL")
@ -174,35 +174,42 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<BackendConfig
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
let mut builder = AuthorizationCodeGrant::new(
let client = Client::new(
config.client_id.clone(),
client_secret,
config.auth_url.clone(),
config.token_url.clone(),
)?;
)?
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port)
.build()?;
let mut auth_code_grant = AuthorizationCodeGrant::new()
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port);
if config.pkce {
builder = builder.with_pkce();
auth_code_grant = auth_code_grant.with_pkce();
}
for scope in config.scopes.clone() {
builder = builder.with_scope(scope);
auth_code_grant = auth_code_grant.with_scope(scope);
}
let client = builder.get_client()?;
let (redirect_url, csrf_token) = builder.get_redirect_url(&client);
let (redirect_url, csrf_token) = auth_code_grant.get_redirect_url(&client);
println!("{}", redirect_url.to_string());
println!("");
let (access_token, refresh_token) = builder.wait_for_redirection(client, csrf_token)?;
let (access_token, refresh_token) =
auth_code_grant.wait_for_redirection(&client, csrf_token)?;
Secret::new_keyring(format!("{account_name}-imap-oauth2-access-token"))
.set(access_token)?;
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"))
.set_keyring_entry_secret(access_token)?;
if let Some(refresh_token) = &refresh_token {
Secret::new_keyring(format!("{account_name}-imap-oauth2-refresh-token"))
.set(refresh_token)?;
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))
.set_keyring_entry_secret(refresh_token)?;
}
ImapAuthConfig::OAuth2(config)

View file

@ -135,10 +135,10 @@ mod tests {
fn name(&self) -> String {
unimplemented!();
}
fn add_folder(&self, _: &str) -> backend::Result<()> {
fn add_folder(&mut self, _: &str) -> backend::Result<()> {
unimplemented!();
}
fn list_folders(&self) -> backend::Result<Folders> {
fn list_folders(&mut self) -> backend::Result<Folders> {
Ok(Folders::from_iter([
Folder {
delim: "/".into(),
@ -152,23 +152,28 @@ mod tests {
},
]))
}
fn expunge_folder(&self, _: &str) -> backend::Result<()> {
fn expunge_folder(&mut self, _: &str) -> backend::Result<()> {
unimplemented!();
}
fn purge_folder(&self, _: &str) -> backend::Result<()> {
fn purge_folder(&mut self, _: &str) -> backend::Result<()> {
unimplemented!();
}
fn delete_folder(&self, _: &str) -> backend::Result<()> {
fn delete_folder(&mut self, _: &str) -> backend::Result<()> {
unimplemented!();
}
fn get_envelope(&self, _: &str, _: &str) -> backend::Result<Envelope> {
fn get_envelope(&mut self, _: &str, _: &str) -> backend::Result<Envelope> {
unimplemented!();
}
fn list_envelopes(&self, _: &str, _: usize, _: usize) -> backend::Result<Envelopes> {
fn list_envelopes(
&mut self,
_: &str,
_: usize,
_: usize,
) -> backend::Result<Envelopes> {
unimplemented!()
}
fn search_envelopes(
&self,
&mut self,
_: &str,
_: &str,
_: &str,
@ -177,35 +182,38 @@ mod tests {
) -> backend::Result<Envelopes> {
unimplemented!()
}
fn add_email(&self, _: &str, _: &[u8], _: &Flags) -> backend::Result<String> {
fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> backend::Result<String> {
unimplemented!()
}
fn get_emails(&self, _: &str, _: Vec<&str>) -> backend::Result<Emails> {
fn get_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<Emails> {
unimplemented!()
}
fn preview_emails(&self, _: &str, _: Vec<&str>) -> backend::Result<Emails> {
fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<Emails> {
unimplemented!()
}
fn copy_emails(&self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> {
fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> {
unimplemented!()
}
fn move_emails(&self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> {
fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> {
unimplemented!()
}
fn delete_emails(&self, _: &str, _: Vec<&str>) -> backend::Result<()> {
fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<()> {
unimplemented!()
}
fn add_flags(&self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
unimplemented!()
}
fn set_flags(&self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
unimplemented!()
}
fn remove_flags(&self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
fn remove_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> {
unimplemented!()
}
fn as_any(&self) -> &(dyn Any) {
self
fn try_clone(&self) -> backend::Result<Box<dyn Backend>> {
unimplemented!()
}
fn as_any(&self) -> &dyn Any {
unimplemented!()
}
}

View file

@ -1,3 +1,4 @@
pub mod sendmail;
#[cfg(feature = "smtp-sender")]
pub mod smtp;
pub(crate) mod wizard;

View file

@ -4,7 +4,7 @@ use pimalaya_email::{
OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SmtpAuthConfig,
SmtpConfig,
};
use pimalaya_oauth2::AuthorizationCodeGrant;
use pimalaya_oauth2::{AuthorizationCodeGrant, Client};
use pimalaya_secret::Secret;
use crate::{
@ -84,8 +84,8 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<SenderConfig>
let config = match secret {
Some(idx) if SECRETS[idx] == KEYRING => {
Secret::new_keyring(format!("{account_name}-smtp-passwd"))
.set(prompt_passwd("SMTP password")?)?;
Secret::new_keyring_entry(format!("{account_name}-smtp-passwd"))
.set_keyring_entry_secret(prompt_passwd("SMTP password")?)?;
PasswdConfig::default()
}
Some(idx) if SECRETS[idx] == RAW => PasswdConfig {
@ -125,8 +125,8 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<SenderConfig>
let client_secret: String = Input::with_theme(&*THEME)
.with_prompt("SMTP OAuth 2.0 client secret")
.interact()?;
Secret::new_keyring(format!("{account_name}-smtp-oauth2-client-secret"))
.set(&client_secret)?;
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"))
.set_keyring_entry_secret(&client_secret)?;
config.auth_url = Input::with_theme(&*THEME)
.with_prompt("SMTP OAuth 2.0 authorization URL")
@ -174,35 +174,42 @@ pub(crate) fn configure(account_name: &str, email: &str) -> Result<SenderConfig>
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
let mut builder = AuthorizationCodeGrant::new(
let client = Client::new(
config.client_id.clone(),
client_secret,
config.auth_url.clone(),
config.token_url.clone(),
)?;
)?
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port)
.build()?;
let mut auth_code_grant = AuthorizationCodeGrant::new()
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port);
if config.pkce {
builder = builder.with_pkce();
auth_code_grant = auth_code_grant.with_pkce();
}
for scope in config.scopes.clone() {
builder = builder.with_scope(scope);
auth_code_grant = auth_code_grant.with_scope(scope);
}
let client = builder.get_client()?;
let (redirect_url, csrf_token) = builder.get_redirect_url(&client);
let (redirect_url, csrf_token) = auth_code_grant.get_redirect_url(&client);
println!("{}", redirect_url.to_string());
println!("");
let (access_token, refresh_token) = builder.wait_for_redirection(client, csrf_token)?;
let (access_token, refresh_token) =
auth_code_grant.wait_for_redirection(&client, csrf_token)?;
Secret::new_keyring(format!("{account_name}-smtp-oauth2-access-token"))
.set(access_token)?;
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"))
.set_keyring_entry_secret(access_token)?;
if let Some(refresh_token) = &refresh_token {
Secret::new_keyring(format!("{account_name}-smtp-oauth2-refresh-token"))
.set(refresh_token)?;
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))
.set_keyring_entry_secret(refresh_token)?;
}
SmtpAuthConfig::OAuth2(config)

View file

@ -1,11 +1,15 @@
use anyhow::{anyhow, Context, Result};
use clap::Command;
#[cfg(feature = "imap-backend")]
use pimalaya_email::ImapBackend;
use pimalaya_email::{
BackendBuilder, BackendConfig, ImapBackend, SenderBuilder, DEFAULT_INBOX_FOLDER,
BackendBuilder, BackendConfig, BackendSyncBuilder, SenderBuilder, DEFAULT_INBOX_FOLDER,
};
use std::env;
use url::Url;
#[cfg(feature = "imap-backend")]
use himalaya::imap;
use himalaya::{
account, cache, compl,
config::{self, DeserializedConfig},
@ -14,9 +18,6 @@ use himalaya::{
tpl, IdMapper,
};
#[cfg(feature = "imap-backend")]
use himalaya::imap;
fn create_app() -> Command {
let app = Command::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
@ -42,7 +43,8 @@ fn create_app() -> Command {
}
#[allow(clippy::single_match)]
fn main() -> Result<()> {
#[tokio::main]
async fn main() -> Result<()> {
let default_env_filter = env_logger::DEFAULT_FILTER_ENV;
env_logger::init_from_env(env_logger::Env::default().filter_or(default_env_filter, "off"));
@ -52,8 +54,8 @@ fn main() -> Result<()> {
let url = Url::parse(&raw_args[1])?;
let config = DeserializedConfig::from_opt_path(None)?;
let account_config = config.to_account_config(None)?;
let mut backend = BackendBuilder::new().build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let mut backend = BackendBuilder::new(account_config.clone()).build()?;
let mut sender = SenderBuilder::new(account_config.clone()).build()?;
let mut printer = StdoutPrinter::default();
return email::handlers::mailto(
@ -68,7 +70,7 @@ fn main() -> Result<()> {
let app = create_app();
let m = app.get_matches();
// checks completion command before configs
// check completion command before configs
// https://github.com/soywod/himalaya/issues/115
match compl::args::matches(&m)? {
Some(compl::args::Cmd::Generate(shell)) => {
@ -77,7 +79,7 @@ fn main() -> Result<()> {
_ => (),
}
// also checks man command before configs
// check also man command before configs
match man::args::matches(&m)? {
Some(man::args::Cmd::GenerateAll(dir)) => {
return man::handlers::generate(dir, create_app());
@ -85,53 +87,47 @@ fn main() -> Result<()> {
_ => (),
}
// inits config
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
let account_config = config.to_account_config(account::args::parse_arg(&m))?;
let account_name = account_config.name.clone();
let folder = folder::args::parse_source_arg(&m);
let disable_cache = cache::args::parse_disable_cache_flag(&m);
// FIXME: find why account config cannot be borrowed
// let backend_builder =
// BackendBuilder::new(Cow::Borrowed(&account_config)).with_cache_disabled(disable_cache);
let backend_builder =
BackendBuilder::new(account_config.clone()).with_cache_disabled(disable_cache);
let sender_builder = SenderBuilder::new(account_config.clone());
let mut printer = StdoutPrinter::try_from(&m)?;
// checks IMAP commands
#[cfg(feature = "imap-backend")]
if let BackendConfig::Imap(imap_config) = &account_config.backend {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
// FIXME: find a way to downcast `backend` instead of
// recreating an instance.
match imap::args::matches(&m)? {
Some(imap::args::Cmd::Notify(keepalive)) => {
let imap = ImapBackend::new(account_config.clone(), imap_config.clone())?;
return imap::handlers::notify(&imap, &folder, keepalive);
let mut backend =
ImapBackend::new(account_config.clone(), imap_config.clone(), None)?;
return imap::handlers::notify(&mut backend, &folder, keepalive);
}
Some(imap::args::Cmd::Watch(keepalive)) => {
let imap = ImapBackend::new(account_config.clone(), imap_config.clone())?;
return imap::handlers::watch(&imap, &folder, keepalive);
let mut backend =
ImapBackend::new(account_config.clone(), imap_config.clone(), None)?;
return imap::handlers::watch(&mut backend, &folder, keepalive);
}
_ => (),
}
}
// inits services
let disable_cache = cache::args::parse_disable_cache_flag(&m);
let mut printer = StdoutPrinter::try_from(&m)?;
// checks account commands
match account::args::matches(&m)? {
Some(account::args::Cmd::List(max_width)) => {
return account::handlers::list(max_width, &account_config, &config, &mut printer);
}
Some(account::args::Cmd::Sync(folders_strategy, dry_run)) => {
let backend = BackendBuilder::new()
.sessions_pool_size(8)
.disable_cache(true)
.build(&account_config)?;
account::handlers::sync(
&account_config,
&mut printer,
backend.as_ref(),
folders_strategy,
dry_run,
)?;
backend.close()?;
Some(account::args::Cmd::Sync(strategy, dry_run)) => {
let sync_builder = BackendSyncBuilder::new(account_config, backend_builder)?
.with_some_folders_strategy(strategy)
.with_dry_run(dry_run);
account::handlers::sync(&mut printer, sync_builder, dry_run)?;
return Ok(());
}
Some(account::args::Cmd::Configure(reset)) => {
@ -147,15 +143,11 @@ fn main() -> Result<()> {
.ok_or_else(|| anyhow!("the folder argument is missing"))
.context("cannot create folder")?;
let folder = account_config.folder_alias(folder)?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut backend = backend_builder.build()?;
return folder::handlers::create(&mut printer, backend.as_mut(), &folder);
}
Some(folder::args::Cmd::List(max_width)) => {
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut backend = backend_builder.build()?;
return folder::handlers::list(
&account_config,
&mut printer,
@ -165,9 +157,7 @@ fn main() -> Result<()> {
}
Some(folder::args::Cmd::Expunge) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut backend = backend_builder.build()?;
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder);
}
Some(folder::args::Cmd::Delete) => {
@ -175,9 +165,7 @@ fn main() -> Result<()> {
.ok_or_else(|| anyhow!("the folder argument is missing"))
.context("cannot delete folder")?;
let folder = account_config.folder_alias(folder)?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut backend = backend_builder.build()?;
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder);
}
_ => (),
@ -187,10 +175,8 @@ fn main() -> Result<()> {
match email::args::matches(&m)? {
Some(email::args::Cmd::Attachments(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::attachments(
&account_config,
&mut printer,
@ -202,10 +188,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Copy(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::copy(
&account_config,
&mut printer,
@ -218,10 +203,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Delete(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::delete(
&account_config,
&mut printer,
@ -233,11 +217,10 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let mut sender = sender_builder.build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::forward(
&account_config,
&mut printer,
@ -252,10 +235,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::List(max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::list(
&account_config,
&mut printer,
@ -269,10 +251,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Move(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::move_(
&account_config,
&mut printer,
@ -285,10 +266,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Read(ids, text_mime, raw, headers)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::read(
&account_config,
&mut printer,
@ -303,11 +283,10 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let mut sender = sender_builder.build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::reply(
&account_config,
&mut printer,
@ -323,10 +302,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Save(raw_email)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::save(
&account_config,
&mut printer,
@ -338,10 +316,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Search(query, max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::search(
&account_config,
&mut printer,
@ -356,10 +333,9 @@ fn main() -> Result<()> {
}
Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::sort(
&account_config,
&mut printer,
@ -374,10 +350,8 @@ fn main() -> Result<()> {
);
}
Some(email::args::Cmd::Send(raw_email)) => {
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let mut backend = backend_builder.build()?;
let mut sender = sender_builder.build()?;
return email::handlers::send(
&account_config,
&mut printer,
@ -389,10 +363,9 @@ fn main() -> Result<()> {
Some(email::args::Cmd::Flag(m)) => match m {
Some(flag::args::Cmd::Set(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::set(
&mut printer,
&id_mapper,
@ -404,10 +377,9 @@ fn main() -> Result<()> {
}
Some(flag::args::Cmd::Add(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::add(
&mut printer,
&id_mapper,
@ -419,10 +391,9 @@ fn main() -> Result<()> {
}
Some(flag::args::Cmd::Remove(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::remove(
&mut printer,
&id_mapper,
@ -437,10 +408,9 @@ fn main() -> Result<()> {
Some(email::args::Cmd::Tpl(m)) => match m {
Some(tpl::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::forward(
&account_config,
&mut printer,
@ -457,10 +427,9 @@ fn main() -> Result<()> {
}
Some(tpl::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::reply(
&account_config,
&mut printer,
@ -475,10 +444,9 @@ fn main() -> Result<()> {
}
Some(tpl::args::Cmd::Save(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
let mut backend = backend_builder.clone().into_build()?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::save(
&account_config,
&mut printer,
@ -490,10 +458,8 @@ fn main() -> Result<()> {
}
Some(tpl::args::Cmd::Send(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let mut backend = backend_builder.clone().into_build()?;
let mut sender = sender_builder.build()?;
return tpl::handlers::send(
&account_config,
&mut printer,
@ -506,10 +472,8 @@ fn main() -> Result<()> {
_ => (),
},
Some(email::args::Cmd::Write(headers, body)) => {
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config)?;
let mut sender = SenderBuilder::new().build(&account_config)?;
let mut backend = backend_builder.build()?;
let mut sender = sender_builder.build()?;
return email::handlers::write(
&account_config,
&mut printer,