mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
drastically simplified configs
Also started to refactor wizard (WIP).
This commit is contained in:
parent
0ff77b5179
commit
d814ae904a
16 changed files with 458 additions and 416 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -1630,23 +1630,6 @@ version = "0.3.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime-msg-builder"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3981dce6db3e7f9faa1124409a6b94436902ecb2670f374d361789d61eb34ac"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chumsky 0.9.0",
|
||||
"html-escape",
|
||||
"lettre",
|
||||
"log",
|
||||
"regex",
|
||||
"shellexpand",
|
||||
"thiserror",
|
||||
"tree_magic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -2068,7 +2051,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pimalaya-email"
|
||||
version = "0.7.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"advisory-lock",
|
||||
"ammonia",
|
||||
|
@ -2084,11 +2067,11 @@ dependencies = [
|
|||
"maildir",
|
||||
"mailparse",
|
||||
"md5",
|
||||
"mime-msg-builder",
|
||||
"native-tls",
|
||||
"notmuch",
|
||||
"once_cell",
|
||||
"ouroboros",
|
||||
"pimalaya-email-tpl",
|
||||
"pimalaya-oauth2",
|
||||
"pimalaya-process",
|
||||
"pimalaya-secret",
|
||||
|
@ -2107,10 +2090,27 @@ dependencies = [
|
|||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-email-tpl"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chumsky 0.9.0",
|
||||
"html-escape",
|
||||
"lettre",
|
||||
"log",
|
||||
"pimalaya-process",
|
||||
"regex",
|
||||
"shellexpand",
|
||||
"thiserror",
|
||||
"tree_magic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-keyring"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"keyring",
|
||||
"log",
|
||||
|
@ -2120,7 +2120,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pimalaya-oauth2"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"oauth2",
|
||||
|
@ -2132,7 +2132,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pimalaya-process"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"thiserror",
|
||||
|
@ -2141,7 +2141,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pimalaya-secret"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pimalaya-keyring",
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use dirs::{config_dir, home_dir};
|
||||
use log::{debug, trace};
|
||||
use pimalaya_email::{AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat};
|
||||
use pimalaya_email::{AccountConfig, EmailHooks, EmailTextPlainFormat};
|
||||
use pimalaya_process::Cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
use toml;
|
||||
|
@ -33,11 +34,32 @@ pub struct DeserializedConfig {
|
|||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
pub email_reading_verify_cmd: Option<String>,
|
||||
pub email_reading_decrypt_cmd: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_verify_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
pub email_writing_sign_cmd: Option<String>,
|
||||
pub email_writing_encrypt_cmd: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_sign_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||
pub email_sending_save_copy: Option<bool>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksDef",
|
||||
|
@ -54,13 +76,15 @@ impl DeserializedConfig {
|
|||
pub fn from_opt_path(path: Option<&str>) -> Result<Self> {
|
||||
debug!("path: {:?}", path);
|
||||
|
||||
let config: Self = match path.map(|s| s.into()).or_else(Self::path) {
|
||||
Some(path) => {
|
||||
let content = fs::read_to_string(path).context("cannot read config file")?;
|
||||
toml::from_str(&content).context("cannot parse config file")?
|
||||
}
|
||||
None => wizard()?,
|
||||
};
|
||||
// let config: Self = match path.map(|s| s.into()).or_else(Self::path) {
|
||||
// Some(path) => {
|
||||
// let content = fs::read_to_string(path).context("cannot read config file")?;
|
||||
// toml::from_str(&content).context("cannot parse config file")?
|
||||
// }
|
||||
// None => wizard()?,
|
||||
// };
|
||||
|
||||
let config = wizard()?;
|
||||
|
||||
if config.accounts.is_empty() {
|
||||
return Err(anyhow!("config file must contain at least one account"));
|
||||
|
@ -90,17 +114,16 @@ impl DeserializedConfig {
|
|||
.filter(|p| p.exists())
|
||||
}
|
||||
|
||||
pub fn to_configs(&self, account_name: Option<&str>) -> Result<(AccountConfig, BackendConfig)> {
|
||||
pub fn to_account_config(&self, account_name: Option<&str>) -> Result<AccountConfig> {
|
||||
let (account_name, deserialized_account_config) = match account_name {
|
||||
Some("default") | Some("") | None => self
|
||||
.accounts
|
||||
.iter()
|
||||
.find_map(|(name, account)| {
|
||||
if account.is_default() {
|
||||
Some((name.clone(), account))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
account
|
||||
.default
|
||||
.filter(|default| *default == true)
|
||||
.map(|_| (name.clone(), account))
|
||||
})
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => self
|
||||
|
@ -110,16 +133,15 @@ impl DeserializedConfig {
|
|||
.ok_or_else(|| anyhow!(format!("cannot find account {}", name))),
|
||||
}?;
|
||||
|
||||
let (account_config, backend_config) =
|
||||
deserialized_account_config.to_configs(account_name, self);
|
||||
|
||||
Ok((account_config, backend_config))
|
||||
Ok(deserialized_account_config.to_account_config(account_name, self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pimalaya_email::{EmailSender, MaildirConfig, PasswdConfig, SendmailConfig};
|
||||
use pimalaya_email::{
|
||||
BackendConfig, MaildirConfig, PasswdConfig, SenderConfig, SendmailConfig,
|
||||
};
|
||||
use pimalaya_secret::Secret;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
|
@ -132,14 +154,6 @@ mod tests {
|
|||
use std::io::Write;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::account::DeserializedBaseAccountConfig;
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use crate::account::DeserializedImapAccountConfig;
|
||||
use crate::account::DeserializedMaildirAccountConfig;
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use crate::account::DeserializedNotmuchAccountConfig;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn make_config(config: &str) -> Result<DeserializedConfig> {
|
||||
|
@ -159,9 +173,23 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn account_missing_backend_field() {
|
||||
fn account_missing_email_field() {
|
||||
let config = make_config("[account]");
|
||||
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `email`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_missing_backend_field() {
|
||||
let config = make_config(
|
||||
"[account]
|
||||
email = \"test@localhost\"",
|
||||
);
|
||||
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
|
@ -173,6 +201,7 @@ mod tests {
|
|||
fn account_invalid_backend_field() {
|
||||
let config = make_config(
|
||||
"[account]
|
||||
email = \"test@localhost\"
|
||||
backend = \"bad\"",
|
||||
);
|
||||
|
||||
|
@ -183,20 +212,6 @@ mod tests {
|
|||
.contains("unknown variant `bad`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_missing_email_field() {
|
||||
let config = make_config(
|
||||
"[account]
|
||||
backend = \"none\"",
|
||||
);
|
||||
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `email`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imap_account_missing_host_field() {
|
||||
let config = make_config(
|
||||
|
@ -420,6 +435,8 @@ mod tests {
|
|||
#[cfg(feature = "smtp-sender")]
|
||||
#[test]
|
||||
fn account_smtp_sender_minimum_config() {
|
||||
use pimalaya_email::SenderConfig;
|
||||
|
||||
let config = make_config(
|
||||
"[account]
|
||||
email = \"test@localhost\"
|
||||
|
@ -437,9 +454,9 @@ mod tests {
|
|||
DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account".into(),
|
||||
DeserializedAccountConfig::None(DeserializedBaseAccountConfig {
|
||||
DeserializedAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
email_sender: EmailSender::Smtp(SmtpConfig {
|
||||
sender: SenderConfig::Smtp(SmtpConfig {
|
||||
host: "localhost".into(),
|
||||
port: 25,
|
||||
login: "login".into(),
|
||||
|
@ -448,8 +465,8 @@ mod tests {
|
|||
}),
|
||||
..SmtpConfig::default()
|
||||
}),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
})
|
||||
..DeserializedAccountConfig::default()
|
||||
}
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
}
|
||||
|
@ -471,13 +488,13 @@ mod tests {
|
|||
DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account".into(),
|
||||
DeserializedAccountConfig::None(DeserializedBaseAccountConfig {
|
||||
DeserializedAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
email_sender: EmailSender::Sendmail(SendmailConfig {
|
||||
cmd: "echo send".into(),
|
||||
sender: SenderConfig::Sendmail(SendmailConfig {
|
||||
cmd: Cmd::from("echo send")
|
||||
}),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
})
|
||||
..DeserializedAccountConfig::default()
|
||||
}
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
}
|
||||
|
@ -503,12 +520,9 @@ mod tests {
|
|||
DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account".into(),
|
||||
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig {
|
||||
base: DeserializedBaseAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
},
|
||||
backend: ImapConfig {
|
||||
DeserializedAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
backend: BackendConfig::Imap(ImapConfig {
|
||||
host: "localhost".into(),
|
||||
port: 993,
|
||||
login: "login".into(),
|
||||
|
@ -516,8 +530,9 @@ mod tests {
|
|||
passwd: Secret::new_cmd(String::from("echo password"))
|
||||
}),
|
||||
..ImapConfig::default()
|
||||
}
|
||||
})
|
||||
}),
|
||||
..DeserializedAccountConfig::default()
|
||||
}
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
}
|
||||
|
@ -538,15 +553,13 @@ mod tests {
|
|||
DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account".into(),
|
||||
DeserializedAccountConfig::Maildir(DeserializedMaildirAccountConfig {
|
||||
base: DeserializedBaseAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
},
|
||||
backend: MaildirConfig {
|
||||
DeserializedAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
backend: BackendConfig::Maildir(MaildirConfig {
|
||||
root_dir: "/tmp/maildir".into(),
|
||||
}
|
||||
})
|
||||
}),
|
||||
..DeserializedAccountConfig::default()
|
||||
}
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
}
|
||||
|
@ -569,15 +582,13 @@ mod tests {
|
|||
DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account".into(),
|
||||
DeserializedAccountConfig::Notmuch(DeserializedNotmuchAccountConfig {
|
||||
base: DeserializedBaseAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
},
|
||||
backend: NotmuchConfig {
|
||||
DeserializedAccountConfig {
|
||||
email: "test@localhost".into(),
|
||||
backend: BackendConfig::Notmuch(NotmuchConfig {
|
||||
db_path: "/tmp/notmuch.db".into(),
|
||||
}
|
||||
})
|
||||
}),
|
||||
..DeserializedAccountConfig::default()
|
||||
}
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat,
|
||||
folder::sync::Strategy as SyncFoldersStrategy, BackendConfig, EmailHooks, EmailTextPlainFormat,
|
||||
ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
|
||||
SendmailConfig, SmtpAuthConfig, SmtpConfig,
|
||||
SenderConfig, SendmailConfig, SmtpAuthConfig, SmtpConfig,
|
||||
};
|
||||
use pimalaya_keyring::Entry;
|
||||
use pimalaya_process::Cmd;
|
||||
|
@ -29,7 +29,7 @@ pub struct PipelineDef;
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Cmd", from = "SingleCmdOrPipeline")]
|
||||
pub struct SingleCmdOrPipelineDef;
|
||||
pub struct CmdDef;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
|
@ -49,11 +49,36 @@ impl From<SingleCmdOrPipeline> for Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Option<Cmd>", from = "OptionSingleCmdOrPipeline")]
|
||||
pub struct OptionCmdDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionSingleCmdOrPipeline {
|
||||
#[default]
|
||||
None,
|
||||
#[serde(with = "SingleCmdDef")]
|
||||
SingleCmd(Cmd),
|
||||
#[serde(with = "PipelineDef")]
|
||||
Pipeline(Cmd),
|
||||
}
|
||||
|
||||
impl From<OptionSingleCmdOrPipeline> for Option<Cmd> {
|
||||
fn from(cmd: OptionSingleCmdOrPipeline) -> Option<Cmd> {
|
||||
match cmd {
|
||||
OptionSingleCmdOrPipeline::None => None,
|
||||
OptionSingleCmdOrPipeline::SingleCmd(cmd) => Some(cmd),
|
||||
OptionSingleCmdOrPipeline::Pipeline(cmd) => Some(cmd),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Secret", rename_all = "kebab-case")]
|
||||
pub enum SecretDef {
|
||||
Raw(String),
|
||||
#[serde(with = "SingleCmdOrPipelineDef")]
|
||||
#[serde(with = "CmdDef")]
|
||||
Cmd(Cmd),
|
||||
#[serde(with = "EntryDef")]
|
||||
Keyring(Entry),
|
||||
|
@ -68,6 +93,21 @@ pub enum OAuth2MethodDef {
|
|||
OAuthBearer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "BackendConfig", tag = "backend", rename_all = "kebab-case")]
|
||||
pub enum BackendConfigDef {
|
||||
#[default]
|
||||
None,
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[serde(with = "ImapConfigDef")]
|
||||
Imap(ImapConfig),
|
||||
#[serde(with = "MaildirConfigDef")]
|
||||
Maildir(MaildirConfig),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[serde(with = "NotmuchConfigDef")]
|
||||
Notmuch(NotmuchConfig),
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "ImapConfig")]
|
||||
|
@ -106,7 +146,12 @@ pub enum ImapAuthConfigDef {
|
|||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "PasswdConfig")]
|
||||
pub struct ImapPasswdConfigDef {
|
||||
#[serde(rename = "imap-passwd", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "imap-passwd",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub passwd: Secret,
|
||||
}
|
||||
|
||||
|
@ -117,15 +162,30 @@ pub struct ImapOAuth2ConfigDef {
|
|||
pub method: OAuth2Method,
|
||||
#[serde(rename = "imap-oauth2-client-id")]
|
||||
pub client_id: String,
|
||||
#[serde(rename = "imap-oauth2-client-secret", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "imap-oauth2-client-secret",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub client_secret: Secret,
|
||||
#[serde(rename = "imap-oauth2-auth-url")]
|
||||
pub auth_url: String,
|
||||
#[serde(rename = "imap-oauth2-token-url")]
|
||||
pub token_url: String,
|
||||
#[serde(rename = "imap-oauth2-access-token", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "imap-oauth2-access-token",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub access_token: Secret,
|
||||
#[serde(rename = "imap-oauth2-refresh-token", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "imap-oauth2-refresh-token",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub refresh_token: Secret,
|
||||
#[serde(flatten, with = "ImapOAuth2ScopesDef")]
|
||||
pub scopes: OAuth2Scopes,
|
||||
|
@ -172,8 +232,8 @@ pub enum EmailTextPlainFormatDef {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailSender", tag = "sender", rename_all = "kebab-case")]
|
||||
pub enum EmailSenderDef {
|
||||
#[serde(remote = "SenderConfig", tag = "sender", rename_all = "kebab-case")]
|
||||
pub enum SenderConfigDef {
|
||||
#[default]
|
||||
None,
|
||||
#[serde(with = "SmtpConfigDef")]
|
||||
|
@ -213,7 +273,12 @@ pub enum SmtpAuthConfigDef {
|
|||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "PasswdConfig", default)]
|
||||
pub struct SmtpPasswdConfigDef {
|
||||
#[serde(rename = "smtp-passwd", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "smtp-passwd",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub passwd: Secret,
|
||||
}
|
||||
|
||||
|
@ -224,15 +289,30 @@ pub struct SmtpOAuth2ConfigDef {
|
|||
pub method: OAuth2Method,
|
||||
#[serde(rename = "smtp-oauth2-client-id")]
|
||||
pub client_id: String,
|
||||
#[serde(rename = "smtp-oauth2-client-secret", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "smtp-oauth2-client-secret",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub client_secret: Secret,
|
||||
#[serde(rename = "smtp-oauth2-auth-url")]
|
||||
pub auth_url: String,
|
||||
#[serde(rename = "smtp-oauth2-token-url")]
|
||||
pub token_url: String,
|
||||
#[serde(rename = "smtp-oauth2-access-token", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "smtp-oauth2-access-token",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub access_token: Secret,
|
||||
#[serde(rename = "smtp-oauth2-refresh-token", with = "SecretDef", default)]
|
||||
#[serde(
|
||||
rename = "smtp-oauth2-refresh-token",
|
||||
with = "SecretDef",
|
||||
default,
|
||||
skip_serializing_if = "Secret::is_undefined_entry"
|
||||
)]
|
||||
pub refresh_token: Secret,
|
||||
#[serde(flatten, with = "SmtpOAuth2ScopesDef")]
|
||||
pub scopes: OAuth2Scopes,
|
||||
|
@ -249,11 +329,11 @@ pub enum SmtpOAuth2ScopesDef {
|
|||
Scopes(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
||||
pub struct SendmailConfigDef {
|
||||
#[serde(rename = "sendmail-cmd")]
|
||||
cmd: String,
|
||||
#[serde(rename = "sendmail-cmd", with = "CmdDef")]
|
||||
cmd: Cmd,
|
||||
}
|
||||
|
||||
/// Represents the email hooks. Useful for doing extra email
|
||||
|
@ -262,7 +342,12 @@ pub struct SendmailConfigDef {
|
|||
#[serde(remote = "EmailHooks", rename_all = "kebab-case")]
|
||||
pub struct EmailHooksDef {
|
||||
/// Represents the hook called just before sending an email.
|
||||
pub pre_send: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub pre_send: Option<Cmd>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use pimalaya_email::ImapConfig;
|
||||
use pimalaya_email::{BackendConfig, ImapConfig};
|
||||
|
||||
use crate::account::{
|
||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
|
||||
};
|
||||
use crate::account::DeserializedAccountConfig;
|
||||
|
||||
use super::{SECURITY_PROTOCOLS, THEME};
|
||||
use super::{AUTH_MECHANISMS, CMD, KEYRING, RAW, SECRET, SECURITY_PROTOCOLS, THEME};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
pub(crate) fn configure(base: &DeserializedAccountConfig) -> Result<BackendConfig> {
|
||||
// TODO: Validate by checking as valid URI
|
||||
let mut backend = ImapConfig {
|
||||
host: Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter the IMAP host:")
|
||||
.default(format!("imap.{}", base.email.rsplit_once('@').unwrap().1))
|
||||
.interact()?,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
use dialoguer::Password;
|
||||
use pimalaya_email::{ImapAuthConfig, PasswdConfig};
|
||||
use pimalaya_secret::Secret;
|
||||
|
||||
use super::PASSWD;
|
||||
|
||||
let mut imap_config = ImapConfig::default();
|
||||
|
||||
imap_config.host = Input::with_theme(&*THEME)
|
||||
.with_prompt("What is your IMAP host:")
|
||||
.default(format!("imap.{}", base.email.rsplit_once('@').unwrap().1))
|
||||
.interact()?;
|
||||
|
||||
let default_port = match Select::with_theme(&*THEME)
|
||||
.with_prompt("Which security protocol do you want to use?")
|
||||
|
@ -26,35 +30,60 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
|||
.interact_opt()?
|
||||
{
|
||||
Some(idx) if SECURITY_PROTOCOLS[idx] == "SSL/TLS" => {
|
||||
backend.ssl = Some(true);
|
||||
imap_config.ssl = Some(true);
|
||||
993
|
||||
}
|
||||
Some(idx) if SECURITY_PROTOCOLS[idx] == "STARTTLS" => {
|
||||
backend.starttls = Some(true);
|
||||
imap_config.starttls = Some(true);
|
||||
143
|
||||
}
|
||||
_ => 143,
|
||||
};
|
||||
|
||||
backend.port = Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter the IMAP port:")
|
||||
imap_config.port = Input::with_theme(&*THEME)
|
||||
.with_prompt("Which IMAP port would you like to use?")
|
||||
.validate_with(|input: &String| input.parse::<u16>().map(|_| ()))
|
||||
.default(default_port.to_string())
|
||||
.interact()
|
||||
.map(|input| input.parse::<u16>().unwrap())?;
|
||||
|
||||
backend.login = Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter your IMAP login:")
|
||||
imap_config.login = Input::with_theme(&*THEME)
|
||||
.with_prompt("What is your IMAP login?")
|
||||
.default(base.email.clone())
|
||||
.interact()?;
|
||||
|
||||
let auth = Select::with_theme(&*THEME)
|
||||
.with_prompt("Which IMAP authentication mechanism would you like to use?")
|
||||
.items(AUTH_MECHANISMS)
|
||||
.default(0)
|
||||
.interact_opt()?;
|
||||
|
||||
imap_config.auth = match auth {
|
||||
Some(idx) if AUTH_MECHANISMS[idx] == PASSWD => {
|
||||
let secret = Select::with_theme(&*THEME)
|
||||
.with_prompt("How would you like to store your password?")
|
||||
.items(SECRET)
|
||||
.default(0)
|
||||
.interact_opt()?;
|
||||
match secret {
|
||||
Some(idx) if SECRET[idx] == RAW => ImapAuthConfig::Passwd(PasswdConfig {
|
||||
passwd: Secret::new_raw(
|
||||
Password::with_theme(&*THEME)
|
||||
.with_prompt("What is your IMAP password?")
|
||||
.interact()?,
|
||||
),
|
||||
}),
|
||||
_ => ImapAuthConfig::default(),
|
||||
}
|
||||
}
|
||||
_ => ImapAuthConfig::default(),
|
||||
};
|
||||
|
||||
// FIXME: add all variants: password, password command and oauth2
|
||||
// backend.passwd_cmd = Input::with_theme(&*THEME)
|
||||
// .with_prompt("What shell command should we run to get your password?")
|
||||
// .default(format!("pass show {}", &base.email))
|
||||
// .interact()?;
|
||||
|
||||
Ok(DeserializedAccountConfig::Imap(
|
||||
DeserializedImapAccountConfig { base, backend },
|
||||
))
|
||||
Ok(BackendConfig::Imap(imap_config))
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use super::THEME;
|
||||
use crate::account::{
|
||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedMaildirAccountConfig,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use dirs::home_dir;
|
||||
use pimalaya_email::MaildirConfig;
|
||||
use pimalaya_email::{BackendConfig, MaildirConfig};
|
||||
|
||||
use super::THEME;
|
||||
|
||||
pub(crate) fn configure() -> Result<BackendConfig> {
|
||||
let mut maildir_config = MaildirConfig::default();
|
||||
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
let input = if let Some(home) = home_dir() {
|
||||
Input::with_theme(&*THEME)
|
||||
.default(home.join("Mail").display().to_string())
|
||||
|
@ -19,12 +19,7 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
|||
.interact_text()?
|
||||
};
|
||||
|
||||
Ok(DeserializedAccountConfig::Maildir(
|
||||
DeserializedMaildirAccountConfig {
|
||||
base,
|
||||
backend: MaildirConfig {
|
||||
root_dir: input.into(),
|
||||
},
|
||||
},
|
||||
))
|
||||
maildir_config.root_dir = input.into();
|
||||
|
||||
Ok(BackendConfig::Maildir(maildir_config))
|
||||
}
|
||||
|
|
|
@ -8,26 +8,37 @@ mod smtp;
|
|||
mod validators;
|
||||
|
||||
use super::DeserializedConfig;
|
||||
use crate::account::{DeserializedAccountConfig, DeserializedBaseAccountConfig};
|
||||
use crate::account::DeserializedAccountConfig;
|
||||
use anyhow::{anyhow, Result};
|
||||
use console::style;
|
||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select};
|
||||
use log::trace;
|
||||
use once_cell::sync::Lazy;
|
||||
use pimalaya_email::{BackendConfig, SenderConfig};
|
||||
use std::{fs, io, process};
|
||||
|
||||
const BACKENDS: &[&str] = &[
|
||||
"Maildir",
|
||||
#[cfg(feature = "imap-backend")]
|
||||
"IMAP",
|
||||
"Maildir",
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
"Notmuch",
|
||||
"None",
|
||||
];
|
||||
|
||||
const SENDERS: &[&str] = &["SMTP", "Sendmail"];
|
||||
|
||||
const SECURITY_PROTOCOLS: &[&str] = &["SSL/TLS", "STARTTLS", "None"];
|
||||
|
||||
const AUTH_MECHANISMS: &[&str] = &[PASSWD, OAUTH2];
|
||||
const PASSWD: &str = "Password";
|
||||
const OAUTH2: &str = "OAuth 2.0";
|
||||
|
||||
const SECRET: &[&str] = &[RAW, CMD, KEYRING];
|
||||
const RAW: &str = "In clear, in your configuration (not recommanded)";
|
||||
const CMD: &str = "From a shell command";
|
||||
const KEYRING: &str = "From your system's global keyring";
|
||||
|
||||
// A wizard should have pretty colors 💅
|
||||
static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
|
||||
|
||||
|
@ -45,9 +56,10 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
|||
}
|
||||
|
||||
// Determine path to save to
|
||||
let path = dirs::config_dir()
|
||||
.map(|p| p.join("himalaya").join("config.toml"))
|
||||
.ok_or_else(|| anyhow!("The wizard could not determine the config directory. Aborting"))?;
|
||||
// let path = dirs::config_dir()
|
||||
// .map(|p| p.join("himalaya").join("config.toml"))
|
||||
// .ok_or_else(|| anyhow!("The wizard could not determine the config directory. Aborting"))?;
|
||||
let path = std::path::PathBuf::from("/home/soywod/config.wizard.toml");
|
||||
|
||||
let mut config = DeserializedConfig::default();
|
||||
|
||||
|
@ -74,7 +86,7 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
|||
|
||||
// If one acounts is setup, make it the default. If multiple accounts are setup, decide which
|
||||
// will be the default. If no accounts are setup, exit the process
|
||||
let default = match config.accounts.len() {
|
||||
let default_account = match config.accounts.len() {
|
||||
1 => Some(config.accounts.values_mut().next().unwrap()),
|
||||
i if i > 1 => {
|
||||
let accounts = config.accounts.clone();
|
||||
|
@ -97,14 +109,8 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
|||
_ => process::exit(0),
|
||||
};
|
||||
|
||||
match default {
|
||||
Some(DeserializedAccountConfig::None(default)) => default.default = Some(true),
|
||||
Some(DeserializedAccountConfig::Maildir(default)) => default.base.default = Some(true),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
Some(DeserializedAccountConfig::Imap(default)) => default.base.default = Some(true),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
Some(DeserializedAccountConfig::Notmuch(default)) => default.base.default = Some(true),
|
||||
_ => {}
|
||||
if let Some(account) = default_account {
|
||||
account.default = Some(true);
|
||||
}
|
||||
|
||||
// Serialize config to file
|
||||
|
@ -117,18 +123,18 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
|||
}
|
||||
|
||||
fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
|
||||
let mut base = configure_base()?;
|
||||
let sender = Select::with_theme(&*THEME)
|
||||
.with_prompt("Which sender would you like use with your account?")
|
||||
.items(SENDERS)
|
||||
.default(0)
|
||||
.interact_opt()?;
|
||||
let mut config = DeserializedAccountConfig::default();
|
||||
|
||||
base.email_sender = match sender {
|
||||
Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&base),
|
||||
Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(),
|
||||
_ => return Ok(None),
|
||||
}?;
|
||||
config.email = Input::with_theme(&*THEME)
|
||||
.with_prompt("What is your email address?")
|
||||
.validate_with(validators::EmailValidator)
|
||||
.interact()?;
|
||||
|
||||
config.display_name = Some(
|
||||
Input::with_theme(&*THEME)
|
||||
.with_prompt("Which name would you like to display with your email?")
|
||||
.interact()?,
|
||||
);
|
||||
|
||||
let backend = Select::with_theme(&*THEME)
|
||||
.with_prompt("Which backend would you like to configure your account for?")
|
||||
|
@ -136,32 +142,28 @@ fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
|
|||
.default(0)
|
||||
.interact_opt()?;
|
||||
|
||||
match backend {
|
||||
Some(idx) if BACKENDS[idx] == "Maildir" => Ok(Some(maildir::configure(base)?)),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
Some(idx) if BACKENDS[idx] == "IMAP" => Ok(Some(imap::configure(base)?)),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
Some(idx) if BACKENDS[idx] == "Notmuch" => Ok(Some(notmuch::configure(base)?)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
config.backend = match backend {
|
||||
Some(idx) if BACKENDS[idx] == "IMAP" => imap::configure(&config),
|
||||
Some(idx) if BACKENDS[idx] == "Maildir" => maildir::configure(),
|
||||
Some(idx) if BACKENDS[idx] == "Notmuch" => notmuch::configure(),
|
||||
Some(idx) if BACKENDS[idx] == "None" => Ok(BackendConfig::None),
|
||||
_ => return Ok(None),
|
||||
}?;
|
||||
|
||||
fn configure_base() -> Result<DeserializedBaseAccountConfig> {
|
||||
let mut base_account_config = DeserializedBaseAccountConfig {
|
||||
email: Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter your email:")
|
||||
.validate_with(validators::EmailValidator)
|
||||
.interact()?,
|
||||
..Default::default()
|
||||
};
|
||||
let sender = Select::with_theme(&*THEME)
|
||||
.with_prompt("Which sender would you like use with your account?")
|
||||
.items(SENDERS)
|
||||
.default(0)
|
||||
.interact_opt()?;
|
||||
|
||||
base_account_config.display_name = Some(
|
||||
Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter display name:")
|
||||
.interact()?,
|
||||
);
|
||||
config.sender = match sender {
|
||||
Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&config),
|
||||
Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(),
|
||||
Some(idx) if SENDERS[idx] == "None" => Ok(SenderConfig::None),
|
||||
_ => return Ok(None),
|
||||
}?;
|
||||
|
||||
Ok(base_account_config)
|
||||
Ok(Some(config))
|
||||
}
|
||||
|
||||
pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use super::THEME;
|
||||
use crate::account::{
|
||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedNotmuchAccountConfig,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use pimalaya_email::{NotmuchBackend, NotmuchConfig};
|
||||
use pimalaya_email::{BackendConfig, NotmuchBackend, NotmuchConfig};
|
||||
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
let db_path = match NotmuchBackend::get_default_db_path() {
|
||||
use super::THEME;
|
||||
|
||||
pub(crate) fn configure() -> Result<BackendConfig> {
|
||||
let mut notmuch_config = NotmuchConfig::default();
|
||||
|
||||
notmuch_config.db_path = match NotmuchBackend::get_default_db_path() {
|
||||
Ok(db) => db,
|
||||
_ => {
|
||||
let input: String = Input::with_theme(&*THEME)
|
||||
|
@ -17,9 +17,5 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
|||
}
|
||||
};
|
||||
|
||||
let backend = NotmuchConfig { db_path };
|
||||
|
||||
Ok(DeserializedAccountConfig::Notmuch(
|
||||
DeserializedNotmuchAccountConfig { base, backend },
|
||||
))
|
||||
Ok(BackendConfig::Notmuch(notmuch_config))
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use super::THEME;
|
||||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use pimalaya_email::{EmailSender, SendmailConfig};
|
||||
use pimalaya_email::{SenderConfig, SendmailConfig};
|
||||
|
||||
pub(crate) fn configure() -> Result<EmailSender> {
|
||||
Ok(EmailSender::Sendmail(SendmailConfig {
|
||||
cmd: Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter an external command to send a mail: ")
|
||||
.default("/usr/bin/msmtp".to_owned())
|
||||
.interact()?,
|
||||
}))
|
||||
use super::THEME;
|
||||
|
||||
pub(crate) fn configure() -> Result<SenderConfig> {
|
||||
let mut sendmail_config = SendmailConfig::default();
|
||||
|
||||
sendmail_config.cmd = Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter an external command to send an email: ")
|
||||
.default("/usr/bin/msmtp".to_owned())
|
||||
.interact()?
|
||||
.into();
|
||||
|
||||
Ok(SenderConfig::Sendmail(sendmail_config))
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
use super::{SECURITY_PROTOCOLS, THEME};
|
||||
use crate::account::DeserializedBaseAccountConfig;
|
||||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use pimalaya_email::{EmailSender, SmtpConfig};
|
||||
use pimalaya_email::{SenderConfig, SmtpConfig};
|
||||
|
||||
pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSender> {
|
||||
use crate::account::DeserializedAccountConfig;
|
||||
|
||||
use super::{SECURITY_PROTOCOLS, THEME};
|
||||
|
||||
pub(crate) fn configure(config: &DeserializedAccountConfig) -> Result<SenderConfig> {
|
||||
let mut smtp_config = SmtpConfig {
|
||||
host: Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter the SMTP host: ")
|
||||
.default(format!("smtp.{}", base.email.rsplit_once('@').unwrap().1))
|
||||
.default(format!("smtp.{}", config.email.rsplit_once('@').unwrap().1))
|
||||
.interact()?,
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -39,7 +41,7 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
|
|||
|
||||
smtp_config.login = Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter your SMTP login:")
|
||||
.default(base.email.clone())
|
||||
.default(config.email.clone())
|
||||
.interact()?;
|
||||
|
||||
// FIXME: add all variants: password, password command and oauth2
|
||||
|
@ -48,5 +50,5 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
|
|||
// .default(format!("pass show {}", &base.email))
|
||||
// .interact()?;
|
||||
|
||||
Ok(EmailSender::Smtp(smtp_config))
|
||||
Ok(SenderConfig::Smtp(smtp_config))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! accounts from the config file.
|
||||
|
||||
use anyhow::Result;
|
||||
use pimalaya_email::BackendConfig;
|
||||
use serde::Serialize;
|
||||
use std::{collections::hash_map::Iter, ops::Deref};
|
||||
|
||||
|
@ -39,19 +40,19 @@ impl PrintTable for Accounts {
|
|||
impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
|
||||
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
|
||||
let mut accounts: Vec<_> = map
|
||||
.map(|(name, account)| match account {
|
||||
DeserializedAccountConfig::Maildir(config) => {
|
||||
Account::new(name, "maildir", config.base.default.unwrap_or_default())
|
||||
.map(|(name, account)| match &account.backend {
|
||||
BackendConfig::None => Account::new(name, "none", false),
|
||||
BackendConfig::Maildir(_) => {
|
||||
Account::new(name, "maildir", account.default.unwrap_or_default())
|
||||
}
|
||||
#[cfg(feature = "imap-backend")]
|
||||
DeserializedAccountConfig::Imap(config) => {
|
||||
Account::new(name, "imap", config.base.default.unwrap_or_default())
|
||||
BackendConfig::Imap(_) => {
|
||||
Account::new(name, "imap", account.default.unwrap_or_default())
|
||||
}
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
DeserializedAccountConfig::Notmuch(config) => {
|
||||
Account::new(name, "notmuch", config.base.default.unwrap_or_default())
|
||||
BackendConfig::Notmuch(_) => {
|
||||
Account::new(name, "notmuch", account.default.unwrap_or_default())
|
||||
}
|
||||
DeserializedAccountConfig::None(..) => Account::new(name, "none", false),
|
||||
})
|
||||
.collect();
|
||||
accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());
|
||||
|
|
|
@ -5,93 +5,18 @@
|
|||
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
||||
EmailSender, EmailTextPlainFormat, ImapAuthConfig, MaildirConfig,
|
||||
EmailTextPlainFormat, SenderConfig,
|
||||
};
|
||||
use pimalaya_process::Cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use pimalaya_email::ImapConfig;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use pimalaya_email::NotmuchConfig;
|
||||
|
||||
use crate::config::{prelude::*, DeserializedConfig};
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(tag = "backend", rename_all = "kebab-case")]
|
||||
pub enum DeserializedAccountConfig {
|
||||
None(DeserializedBaseAccountConfig),
|
||||
Maildir(DeserializedMaildirAccountConfig),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
Imap(DeserializedImapAccountConfig),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
Notmuch(DeserializedNotmuchAccountConfig),
|
||||
}
|
||||
|
||||
impl DeserializedAccountConfig {
|
||||
pub fn to_configs(
|
||||
&self,
|
||||
name: String,
|
||||
global_config: &DeserializedConfig,
|
||||
) -> (AccountConfig, BackendConfig) {
|
||||
match self {
|
||||
DeserializedAccountConfig::None(config) => (
|
||||
config.to_account_config(name, global_config),
|
||||
BackendConfig::None,
|
||||
),
|
||||
DeserializedAccountConfig::Maildir(config) => (
|
||||
config.base.to_account_config(name, global_config),
|
||||
BackendConfig::Maildir(config.backend.clone()),
|
||||
),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
DeserializedAccountConfig::Imap(config) => {
|
||||
let mut imap_config = config.backend.clone();
|
||||
|
||||
match &mut imap_config.auth {
|
||||
ImapAuthConfig::Passwd(secret) => {
|
||||
secret.replace_undefined_entry_with(format!("{name}-imap-passwd"));
|
||||
}
|
||||
ImapAuthConfig::OAuth2(config) => {
|
||||
config.client_secret.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-client-secret"
|
||||
));
|
||||
config.access_token.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-access-token"
|
||||
));
|
||||
config.refresh_token.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-refresh-token"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let account_config = config.base.to_account_config(name, global_config);
|
||||
(account_config, BackendConfig::Imap(imap_config))
|
||||
}
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
DeserializedAccountConfig::Notmuch(config) => (
|
||||
config.base.to_account_config(name, global_config),
|
||||
BackendConfig::Notmuch(config.backend.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_default(&self) -> bool {
|
||||
match self {
|
||||
DeserializedAccountConfig::None(config) => config.default.unwrap_or_default(),
|
||||
DeserializedAccountConfig::Maildir(config) => config.base.default.unwrap_or_default(),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
DeserializedAccountConfig::Imap(config) => config.base.default.unwrap_or_default(),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
DeserializedAccountConfig::Notmuch(config) => config.base.default.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct DeserializedBaseAccountConfig {
|
||||
#[serde(tag = "backend", rename_all = "kebab-case")]
|
||||
pub struct DeserializedAccountConfig {
|
||||
pub email: String,
|
||||
pub default: Option<bool>,
|
||||
pub display_name: Option<String>,
|
||||
|
@ -106,13 +31,32 @@ pub struct DeserializedBaseAccountConfig {
|
|||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
pub email_reading_verify_cmd: Option<String>,
|
||||
pub email_reading_decrypt_cmd: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_verify_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
pub email_writing_sign_cmd: Option<String>,
|
||||
pub email_writing_encrypt_cmd: Option<String>,
|
||||
#[serde(flatten, with = "EmailSenderDef")]
|
||||
pub email_sender: EmailSender,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_sign_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||
pub email_sending_save_copy: Option<bool>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksDef",
|
||||
|
@ -125,9 +69,14 @@ pub struct DeserializedBaseAccountConfig {
|
|||
pub sync_dir: Option<PathBuf>,
|
||||
#[serde(default, with = "SyncFoldersStrategyDef")]
|
||||
pub sync_folders_strategy: SyncFoldersStrategy,
|
||||
|
||||
#[serde(flatten, with = "BackendConfigDef")]
|
||||
pub backend: BackendConfig,
|
||||
#[serde(flatten, with = "SenderConfigDef")]
|
||||
pub sender: SenderConfig,
|
||||
}
|
||||
|
||||
impl DeserializedBaseAccountConfig {
|
||||
impl DeserializedAccountConfig {
|
||||
pub fn to_account_config(&self, name: String, config: &DeserializedConfig) -> AccountConfig {
|
||||
let mut folder_aliases = config
|
||||
.folder_aliases
|
||||
|
@ -222,39 +171,16 @@ impl DeserializedBaseAccountConfig {
|
|||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.email_writing_headers.as_ref().map(ToOwned::to_owned)),
|
||||
email_sender: self.email_sender.to_owned(),
|
||||
email_sending_save_copy: self.email_sending_save_copy.unwrap_or(true),
|
||||
email_hooks: EmailHooks {
|
||||
pre_send: self.email_hooks.pre_send.clone(),
|
||||
},
|
||||
sync: self.sync,
|
||||
sync_dir: self.sync_dir.clone(),
|
||||
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
||||
|
||||
backend: self.backend.clone(),
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub struct DeserializedImapAccountConfig {
|
||||
#[serde(flatten)]
|
||||
pub base: DeserializedBaseAccountConfig,
|
||||
#[serde(flatten, with = "ImapConfigDef")]
|
||||
pub backend: ImapConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct DeserializedMaildirAccountConfig {
|
||||
#[serde(flatten)]
|
||||
pub base: DeserializedBaseAccountConfig,
|
||||
#[serde(flatten, with = "MaildirConfigDef")]
|
||||
pub backend: MaildirConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
pub struct DeserializedNotmuchAccountConfig {
|
||||
#[serde(flatten)]
|
||||
pub base: DeserializedBaseAccountConfig,
|
||||
#[serde(flatten, with = "NotmuchConfigDef")]
|
||||
pub backend: NotmuchConfig,
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
|||
use log::{info, trace, warn};
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
|
||||
BackendSyncBuilder, BackendSyncProgressEvent, EmailSender, ImapAuthConfig, SmtpAuthConfig,
|
||||
BackendSyncBuilder, BackendSyncProgressEvent, ImapAuthConfig, SenderConfig, SmtpAuthConfig,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -20,16 +20,12 @@ use crate::{
|
|||
};
|
||||
|
||||
/// Configure the current selected account
|
||||
pub fn configure(
|
||||
account_config: &AccountConfig,
|
||||
backend_config: &BackendConfig,
|
||||
reset: bool,
|
||||
) -> Result<()> {
|
||||
pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
|
||||
info!("entering the configure account handler");
|
||||
|
||||
if reset {
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
||||
if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||
let reset = match &imap_config.auth {
|
||||
ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
|
@ -41,7 +37,7 @@ pub fn configure(
|
|||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
||||
if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||
let reset = match &smtp_config.auth {
|
||||
SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
|
@ -54,7 +50,7 @@ pub fn configure(
|
|||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
||||
if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||
match &imap_config.auth {
|
||||
ImapAuthConfig::Passwd(passwd) => {
|
||||
passwd.configure(|| prompt_passwd("Enter your IMAP password:"))
|
||||
|
@ -66,7 +62,7 @@ pub fn configure(
|
|||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
||||
if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||
match &smtp_config.auth {
|
||||
SmtpAuthConfig::Passwd(passwd) => {
|
||||
passwd.configure(|| prompt_passwd("Enter your SMTP password:"))
|
||||
|
@ -296,9 +292,7 @@ mod tests {
|
|||
use termcolor::ColorSpec;
|
||||
|
||||
use crate::{
|
||||
account::{
|
||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
|
||||
},
|
||||
account::DeserializedAccountConfig,
|
||||
printer::{Print, PrintTable, WriteColor},
|
||||
};
|
||||
|
||||
|
@ -370,13 +364,11 @@ mod tests {
|
|||
let deserialized_config = DeserializedConfig {
|
||||
accounts: HashMap::from_iter([(
|
||||
"account-1".into(),
|
||||
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig {
|
||||
base: DeserializedBaseAccountConfig {
|
||||
default: Some(true),
|
||||
..DeserializedBaseAccountConfig::default()
|
||||
},
|
||||
backend: ImapConfig::default(),
|
||||
}),
|
||||
DeserializedAccountConfig {
|
||||
default: Some(true),
|
||||
backend: BackendConfig::Imap(ImapConfig::default()),
|
||||
..DeserializedAccountConfig::default()
|
||||
},
|
||||
)]),
|
||||
..DeserializedConfig::default()
|
||||
};
|
||||
|
|
|
@ -165,9 +165,9 @@ pub fn list<P: Printer>(
|
|||
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
||||
pub fn mailto<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
printer: &mut P,
|
||||
url: &Url,
|
||||
) -> Result<()> {
|
||||
let mut tpl = TplBuilder::default().to(url.path());
|
||||
|
|
|
@ -74,8 +74,8 @@ pub fn save<P: Printer>(
|
|||
})
|
||||
.compile(
|
||||
CompilerBuilder::default()
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||
)?;
|
||||
|
||||
let id = backend.add_email(folder, &email, &Flags::default())?;
|
||||
|
@ -104,8 +104,8 @@ pub fn send<P: Printer>(
|
|||
})
|
||||
.compile(
|
||||
CompilerBuilder::default()
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||
)?;
|
||||
sender.send(&email)?;
|
||||
backend.add_email(folder, &email, &Flags::default())?;
|
||||
|
|
67
src/main.rs
67
src/main.rs
|
@ -51,17 +51,16 @@ fn main() -> Result<()> {
|
|||
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||
let url = Url::parse(&raw_args[1])?;
|
||||
let config = DeserializedConfig::from_opt_path(None)?;
|
||||
let (account_config, backend_config) = config.to_configs(None)?;
|
||||
let mut backend =
|
||||
BackendBuilder::new().build(account_config.clone(), backend_config.clone())?;
|
||||
let mut sender = SenderBuilder::build(&account_config)?;
|
||||
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 printer = StdoutPrinter::default();
|
||||
|
||||
return email::handlers::mailto(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
backend.as_mut(),
|
||||
sender.as_mut(),
|
||||
&mut printer,
|
||||
&url,
|
||||
);
|
||||
}
|
||||
|
@ -88,12 +87,12 @@ fn main() -> Result<()> {
|
|||
|
||||
// inits config
|
||||
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
|
||||
let (account_config, backend_config) = config.to_configs(account::args::parse_arg(&m))?;
|
||||
let account_config = config.to_account_config(account::args::parse_arg(&m))?;
|
||||
let folder = folder::args::parse_source_arg(&m);
|
||||
|
||||
// checks IMAP commands
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = &backend_config {
|
||||
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
|
||||
|
@ -112,7 +111,7 @@ fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
// inits services
|
||||
let mut sender = SenderBuilder::build(&account_config)?;
|
||||
let mut sender = SenderBuilder::new().build(&account_config)?;
|
||||
let mut printer = StdoutPrinter::try_from(&m)?;
|
||||
let disable_cache = cache::args::parse_disable_cache_flag(&m);
|
||||
|
||||
|
@ -125,7 +124,7 @@ fn main() -> Result<()> {
|
|||
let backend = BackendBuilder::new()
|
||||
.sessions_pool_size(8)
|
||||
.disable_cache(true)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
account::handlers::sync(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
|
@ -137,7 +136,7 @@ fn main() -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
Some(account::args::Cmd::Configure(reset)) => {
|
||||
return account::handlers::configure(&account_config, &backend_config, reset);
|
||||
return account::handlers::configure(&account_config, reset);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -151,13 +150,13 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder)?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
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.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return folder::handlers::list(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
|
@ -169,7 +168,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder);
|
||||
}
|
||||
Some(folder::args::Cmd::Delete) => {
|
||||
|
@ -179,7 +178,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder)?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder);
|
||||
}
|
||||
_ => (),
|
||||
|
@ -191,7 +190,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::attachments(
|
||||
&account_config,
|
||||
|
@ -206,7 +205,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::copy(
|
||||
&account_config,
|
||||
|
@ -222,7 +221,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::delete(
|
||||
&account_config,
|
||||
|
@ -237,7 +236,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::forward(
|
||||
&account_config,
|
||||
|
@ -255,7 +254,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::list(
|
||||
&account_config,
|
||||
|
@ -272,7 +271,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::move_(
|
||||
&account_config,
|
||||
|
@ -288,7 +287,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::read(
|
||||
&account_config,
|
||||
|
@ -307,7 +306,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::reply(
|
||||
&account_config,
|
||||
|
@ -326,7 +325,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::save(
|
||||
&account_config,
|
||||
|
@ -341,7 +340,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::search(
|
||||
&account_config,
|
||||
|
@ -359,7 +358,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return email::handlers::sort(
|
||||
&account_config,
|
||||
|
@ -377,7 +376,7 @@ fn main() -> Result<()> {
|
|||
Some(email::args::Cmd::Send(raw_email)) => {
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return email::handlers::send(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
|
@ -391,7 +390,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return flag::handlers::set(
|
||||
&mut printer,
|
||||
|
@ -406,7 +405,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return flag::handlers::add(
|
||||
&mut printer,
|
||||
|
@ -421,7 +420,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return flag::handlers::remove(
|
||||
&mut printer,
|
||||
|
@ -439,7 +438,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return tpl::handlers::forward(
|
||||
&account_config,
|
||||
|
@ -459,7 +458,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return tpl::handlers::reply(
|
||||
&account_config,
|
||||
|
@ -477,7 +476,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||
return tpl::handlers::save(
|
||||
&account_config,
|
||||
|
@ -492,7 +491,7 @@ fn main() -> Result<()> {
|
|||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return tpl::handlers::send(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
|
@ -507,7 +506,7 @@ fn main() -> Result<()> {
|
|||
Some(email::args::Cmd::Write(headers, body)) => {
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(account_config.clone(), backend_config.clone())?;
|
||||
.build(&account_config)?;
|
||||
return email::handlers::write(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
|
|
|
@ -75,8 +75,8 @@ pub fn edit_tpl_with_editor<P: Printer>(
|
|||
printer.print_log("Sending email…")?;
|
||||
let email = tpl.compile(
|
||||
CompilerBuilder::default()
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||
)?;
|
||||
sender.send(&email)?;
|
||||
let sent_folder = config.sent_folder_alias()?;
|
||||
|
@ -98,8 +98,8 @@ pub fn edit_tpl_with_editor<P: Printer>(
|
|||
let draft_folder = config.folder_alias("drafts")?;
|
||||
let email = tpl.compile(
|
||||
CompilerBuilder::default()
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||
)?;
|
||||
backend.add_email(
|
||||
&draft_folder,
|
||||
|
|
Loading…
Reference in a new issue