mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
refactor builders and sync
This commit is contained in:
parent
ab1e8b7e45
commit
c254f64569
14 changed files with 405 additions and 364 deletions
65
Cargo.lock
generated
65
Cargo.lock
generated
|
@ -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"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -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"
|
||||
|
|
24
flake.lock
24
flake.lock
|
@ -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": {
|
||||
|
|
21
src/cache/id_mapper.rs
vendored
21
src/cache/id_mapper.rs
vendored
|
@ -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);
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod sendmail;
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
pub mod smtp;
|
||||
pub(crate) mod wizard;
|
||||
|
|
|
@ -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)
|
||||
|
|
224
src/main.rs
224
src/main.rs
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue