drastically simplified configs

Also started to refactor wizard (WIP).
This commit is contained in:
Clément DOUIN 2023-05-16 00:11:37 +02:00
parent 0ff77b5179
commit d814ae904a
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
16 changed files with 458 additions and 416 deletions

46
Cargo.lock generated
View file

@ -1630,23 +1630,6 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 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]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -2068,7 +2051,7 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-email" name = "pimalaya-email"
version = "0.7.1" version = "0.7.1"
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a" source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
dependencies = [ dependencies = [
"advisory-lock", "advisory-lock",
"ammonia", "ammonia",
@ -2084,11 +2067,11 @@ dependencies = [
"maildir", "maildir",
"mailparse", "mailparse",
"md5", "md5",
"mime-msg-builder",
"native-tls", "native-tls",
"notmuch", "notmuch",
"once_cell", "once_cell",
"ouroboros", "ouroboros",
"pimalaya-email-tpl",
"pimalaya-oauth2", "pimalaya-oauth2",
"pimalaya-process", "pimalaya-process",
"pimalaya-secret", "pimalaya-secret",
@ -2107,10 +2090,27 @@ dependencies = [
"webpki-roots", "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]] [[package]]
name = "pimalaya-keyring" name = "pimalaya-keyring"
version = "0.0.1" version = "0.0.1"
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a" source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
dependencies = [ dependencies = [
"keyring", "keyring",
"log", "log",
@ -2120,7 +2120,7 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-oauth2" name = "pimalaya-oauth2"
version = "0.0.1" version = "0.0.1"
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a" source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
dependencies = [ dependencies = [
"log", "log",
"oauth2", "oauth2",
@ -2132,7 +2132,7 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-process" name = "pimalaya-process"
version = "0.0.1" version = "0.0.1"
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a" source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
dependencies = [ dependencies = [
"log", "log",
"thiserror", "thiserror",
@ -2141,7 +2141,7 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-secret" name = "pimalaya-secret"
version = "0.0.1" version = "0.0.1"
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a" source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
dependencies = [ dependencies = [
"log", "log",
"pimalaya-keyring", "pimalaya-keyring",

View file

@ -6,7 +6,8 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use dirs::{config_dir, home_dir}; use dirs::{config_dir, home_dir};
use log::{debug, trace}; 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 serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::PathBuf}; use std::{collections::HashMap, fs, path::PathBuf};
use toml; use toml;
@ -33,11 +34,32 @@ pub struct DeserializedConfig {
pub email_reading_headers: Option<Vec<String>>, pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "EmailTextPlainFormatDef")] #[serde(default, with = "EmailTextPlainFormatDef")]
pub email_reading_format: EmailTextPlainFormat, pub email_reading_format: EmailTextPlainFormat,
pub email_reading_verify_cmd: Option<String>, #[serde(
pub email_reading_decrypt_cmd: Option<String>, 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_headers: Option<Vec<String>>,
pub email_writing_sign_cmd: Option<String>, #[serde(
pub email_writing_encrypt_cmd: Option<String>, 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( #[serde(
default, default,
with = "EmailHooksDef", with = "EmailHooksDef",
@ -54,13 +76,15 @@ impl DeserializedConfig {
pub fn from_opt_path(path: Option<&str>) -> Result<Self> { pub fn from_opt_path(path: Option<&str>) -> Result<Self> {
debug!("path: {:?}", path); debug!("path: {:?}", path);
let config: Self = match path.map(|s| s.into()).or_else(Self::path) { // let config: Self = match path.map(|s| s.into()).or_else(Self::path) {
Some(path) => { // Some(path) => {
let content = fs::read_to_string(path).context("cannot read config file")?; // let content = fs::read_to_string(path).context("cannot read config file")?;
toml::from_str(&content).context("cannot parse config file")? // toml::from_str(&content).context("cannot parse config file")?
} // }
None => wizard()?, // None => wizard()?,
}; // };
let config = wizard()?;
if config.accounts.is_empty() { if config.accounts.is_empty() {
return Err(anyhow!("config file must contain at least one account")); return Err(anyhow!("config file must contain at least one account"));
@ -90,17 +114,16 @@ impl DeserializedConfig {
.filter(|p| p.exists()) .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 { let (account_name, deserialized_account_config) = match account_name {
Some("default") | Some("") | None => self Some("default") | Some("") | None => self
.accounts .accounts
.iter() .iter()
.find_map(|(name, account)| { .find_map(|(name, account)| {
if account.is_default() { account
Some((name.clone(), account)) .default
} else { .filter(|default| *default == true)
None .map(|_| (name.clone(), account))
}
}) })
.ok_or_else(|| anyhow!("cannot find default account")), .ok_or_else(|| anyhow!("cannot find default account")),
Some(name) => self Some(name) => self
@ -110,16 +133,15 @@ impl DeserializedConfig {
.ok_or_else(|| anyhow!(format!("cannot find account {}", name))), .ok_or_else(|| anyhow!(format!("cannot find account {}", name))),
}?; }?;
let (account_config, backend_config) = Ok(deserialized_account_config.to_account_config(account_name, self))
deserialized_account_config.to_configs(account_name, self);
Ok((account_config, backend_config))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pimalaya_email::{EmailSender, MaildirConfig, PasswdConfig, SendmailConfig}; use pimalaya_email::{
BackendConfig, MaildirConfig, PasswdConfig, SenderConfig, SendmailConfig,
};
use pimalaya_secret::Secret; use pimalaya_secret::Secret;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
@ -132,14 +154,6 @@ mod tests {
use std::io::Write; use std::io::Write;
use tempfile::NamedTempFile; 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::*; use super::*;
fn make_config(config: &str) -> Result<DeserializedConfig> { fn make_config(config: &str) -> Result<DeserializedConfig> {
@ -159,9 +173,23 @@ mod tests {
} }
#[test] #[test]
fn account_missing_backend_field() { fn account_missing_email_field() {
let config = make_config("[account]"); 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 assert!(config
.unwrap_err() .unwrap_err()
.root_cause() .root_cause()
@ -173,6 +201,7 @@ mod tests {
fn account_invalid_backend_field() { fn account_invalid_backend_field() {
let config = make_config( let config = make_config(
"[account] "[account]
email = \"test@localhost\"
backend = \"bad\"", backend = \"bad\"",
); );
@ -183,20 +212,6 @@ mod tests {
.contains("unknown variant `bad`")); .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] #[test]
fn imap_account_missing_host_field() { fn imap_account_missing_host_field() {
let config = make_config( let config = make_config(
@ -420,6 +435,8 @@ mod tests {
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
#[test] #[test]
fn account_smtp_sender_minimum_config() { fn account_smtp_sender_minimum_config() {
use pimalaya_email::SenderConfig;
let config = make_config( let config = make_config(
"[account] "[account]
email = \"test@localhost\" email = \"test@localhost\"
@ -437,9 +454,9 @@ mod tests {
DeserializedConfig { DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account".into(), "account".into(),
DeserializedAccountConfig::None(DeserializedBaseAccountConfig { DeserializedAccountConfig {
email: "test@localhost".into(), email: "test@localhost".into(),
email_sender: EmailSender::Smtp(SmtpConfig { sender: SenderConfig::Smtp(SmtpConfig {
host: "localhost".into(), host: "localhost".into(),
port: 25, port: 25,
login: "login".into(), login: "login".into(),
@ -448,8 +465,8 @@ mod tests {
}), }),
..SmtpConfig::default() ..SmtpConfig::default()
}), }),
..DeserializedBaseAccountConfig::default() ..DeserializedAccountConfig::default()
}) }
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
} }
@ -471,13 +488,13 @@ mod tests {
DeserializedConfig { DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account".into(), "account".into(),
DeserializedAccountConfig::None(DeserializedBaseAccountConfig { DeserializedAccountConfig {
email: "test@localhost".into(), email: "test@localhost".into(),
email_sender: EmailSender::Sendmail(SendmailConfig { sender: SenderConfig::Sendmail(SendmailConfig {
cmd: "echo send".into(), cmd: Cmd::from("echo send")
}), }),
..DeserializedBaseAccountConfig::default() ..DeserializedAccountConfig::default()
}) }
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
} }
@ -503,12 +520,9 @@ mod tests {
DeserializedConfig { DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account".into(), "account".into(),
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig { DeserializedAccountConfig {
base: DeserializedBaseAccountConfig { email: "test@localhost".into(),
email: "test@localhost".into(), backend: BackendConfig::Imap(ImapConfig {
..DeserializedBaseAccountConfig::default()
},
backend: ImapConfig {
host: "localhost".into(), host: "localhost".into(),
port: 993, port: 993,
login: "login".into(), login: "login".into(),
@ -516,8 +530,9 @@ mod tests {
passwd: Secret::new_cmd(String::from("echo password")) passwd: Secret::new_cmd(String::from("echo password"))
}), }),
..ImapConfig::default() ..ImapConfig::default()
} }),
}) ..DeserializedAccountConfig::default()
}
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
} }
@ -538,15 +553,13 @@ mod tests {
DeserializedConfig { DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account".into(), "account".into(),
DeserializedAccountConfig::Maildir(DeserializedMaildirAccountConfig { DeserializedAccountConfig {
base: DeserializedBaseAccountConfig { email: "test@localhost".into(),
email: "test@localhost".into(), backend: BackendConfig::Maildir(MaildirConfig {
..DeserializedBaseAccountConfig::default()
},
backend: MaildirConfig {
root_dir: "/tmp/maildir".into(), root_dir: "/tmp/maildir".into(),
} }),
}) ..DeserializedAccountConfig::default()
}
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
} }
@ -569,15 +582,13 @@ mod tests {
DeserializedConfig { DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account".into(), "account".into(),
DeserializedAccountConfig::Notmuch(DeserializedNotmuchAccountConfig { DeserializedAccountConfig {
base: DeserializedBaseAccountConfig { email: "test@localhost".into(),
email: "test@localhost".into(), backend: BackendConfig::Notmuch(NotmuchConfig {
..DeserializedBaseAccountConfig::default()
},
backend: NotmuchConfig {
db_path: "/tmp/notmuch.db".into(), db_path: "/tmp/notmuch.db".into(),
} }),
}) ..DeserializedAccountConfig::default()
}
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
} }

View file

@ -1,7 +1,7 @@
use pimalaya_email::{ 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, ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
SendmailConfig, SmtpAuthConfig, SmtpConfig, SenderConfig, SendmailConfig, SmtpAuthConfig, SmtpConfig,
}; };
use pimalaya_keyring::Entry; use pimalaya_keyring::Entry;
use pimalaya_process::Cmd; use pimalaya_process::Cmd;
@ -29,7 +29,7 @@ pub struct PipelineDef;
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "Cmd", from = "SingleCmdOrPipeline")] #[serde(remote = "Cmd", from = "SingleCmdOrPipeline")]
pub struct SingleCmdOrPipelineDef; pub struct CmdDef;
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(untagged)] #[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)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "Secret", rename_all = "kebab-case")] #[serde(remote = "Secret", rename_all = "kebab-case")]
pub enum SecretDef { pub enum SecretDef {
Raw(String), Raw(String),
#[serde(with = "SingleCmdOrPipelineDef")] #[serde(with = "CmdDef")]
Cmd(Cmd), Cmd(Cmd),
#[serde(with = "EntryDef")] #[serde(with = "EntryDef")]
Keyring(Entry), Keyring(Entry),
@ -68,6 +93,21 @@ pub enum OAuth2MethodDef {
OAuthBearer, 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")] #[cfg(feature = "imap-backend")]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "ImapConfig")] #[serde(remote = "ImapConfig")]
@ -106,7 +146,12 @@ pub enum ImapAuthConfigDef {
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "PasswdConfig")] #[serde(remote = "PasswdConfig")]
pub struct ImapPasswdConfigDef { 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, pub passwd: Secret,
} }
@ -117,15 +162,30 @@ pub struct ImapOAuth2ConfigDef {
pub method: OAuth2Method, pub method: OAuth2Method,
#[serde(rename = "imap-oauth2-client-id")] #[serde(rename = "imap-oauth2-client-id")]
pub client_id: String, 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, pub client_secret: Secret,
#[serde(rename = "imap-oauth2-auth-url")] #[serde(rename = "imap-oauth2-auth-url")]
pub auth_url: String, pub auth_url: String,
#[serde(rename = "imap-oauth2-token-url")] #[serde(rename = "imap-oauth2-token-url")]
pub token_url: String, 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, 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, pub refresh_token: Secret,
#[serde(flatten, with = "ImapOAuth2ScopesDef")] #[serde(flatten, with = "ImapOAuth2ScopesDef")]
pub scopes: OAuth2Scopes, pub scopes: OAuth2Scopes,
@ -172,8 +232,8 @@ pub enum EmailTextPlainFormatDef {
} }
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "EmailSender", tag = "sender", rename_all = "kebab-case")] #[serde(remote = "SenderConfig", tag = "sender", rename_all = "kebab-case")]
pub enum EmailSenderDef { pub enum SenderConfigDef {
#[default] #[default]
None, None,
#[serde(with = "SmtpConfigDef")] #[serde(with = "SmtpConfigDef")]
@ -213,7 +273,12 @@ pub enum SmtpAuthConfigDef {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "PasswdConfig", default)] #[serde(remote = "PasswdConfig", default)]
pub struct SmtpPasswdConfigDef { 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, pub passwd: Secret,
} }
@ -224,15 +289,30 @@ pub struct SmtpOAuth2ConfigDef {
pub method: OAuth2Method, pub method: OAuth2Method,
#[serde(rename = "smtp-oauth2-client-id")] #[serde(rename = "smtp-oauth2-client-id")]
pub client_id: String, 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, pub client_secret: Secret,
#[serde(rename = "smtp-oauth2-auth-url")] #[serde(rename = "smtp-oauth2-auth-url")]
pub auth_url: String, pub auth_url: String,
#[serde(rename = "smtp-oauth2-token-url")] #[serde(rename = "smtp-oauth2-token-url")]
pub token_url: String, 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, 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, pub refresh_token: Secret,
#[serde(flatten, with = "SmtpOAuth2ScopesDef")] #[serde(flatten, with = "SmtpOAuth2ScopesDef")]
pub scopes: OAuth2Scopes, pub scopes: OAuth2Scopes,
@ -249,11 +329,11 @@ pub enum SmtpOAuth2ScopesDef {
Scopes(Vec<String>), 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")] #[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
pub struct SendmailConfigDef { pub struct SendmailConfigDef {
#[serde(rename = "sendmail-cmd")] #[serde(rename = "sendmail-cmd", with = "CmdDef")]
cmd: String, cmd: Cmd,
} }
/// Represents the email hooks. Useful for doing extra email /// Represents the email hooks. Useful for doing extra email
@ -262,7 +342,12 @@ pub struct SendmailConfigDef {
#[serde(remote = "EmailHooks", rename_all = "kebab-case")] #[serde(remote = "EmailHooks", rename_all = "kebab-case")]
pub struct EmailHooksDef { pub struct EmailHooksDef {
/// Represents the hook called just before sending an email. /// 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)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]

View file

@ -1,23 +1,27 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::{Input, Select}; use dialoguer::{Input, Select};
use pimalaya_email::ImapConfig; use pimalaya_email::{BackendConfig, ImapConfig};
use crate::account::{ use crate::account::DeserializedAccountConfig;
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
};
use super::{SECURITY_PROTOCOLS, THEME}; use super::{AUTH_MECHANISMS, CMD, KEYRING, RAW, SECRET, SECURITY_PROTOCOLS, THEME};
#[cfg(feature = "imap-backend")] #[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 // TODO: Validate by checking as valid URI
let mut backend = ImapConfig {
host: Input::with_theme(&*THEME) use dialoguer::Password;
.with_prompt("Enter the IMAP host:") use pimalaya_email::{ImapAuthConfig, PasswdConfig};
.default(format!("imap.{}", base.email.rsplit_once('@').unwrap().1)) use pimalaya_secret::Secret;
.interact()?,
..Default::default() 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) let default_port = match Select::with_theme(&*THEME)
.with_prompt("Which security protocol do you want to use?") .with_prompt("Which security protocol do you want to use?")
@ -26,35 +30,60 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
.interact_opt()? .interact_opt()?
{ {
Some(idx) if SECURITY_PROTOCOLS[idx] == "SSL/TLS" => { Some(idx) if SECURITY_PROTOCOLS[idx] == "SSL/TLS" => {
backend.ssl = Some(true); imap_config.ssl = Some(true);
993 993
} }
Some(idx) if SECURITY_PROTOCOLS[idx] == "STARTTLS" => { Some(idx) if SECURITY_PROTOCOLS[idx] == "STARTTLS" => {
backend.starttls = Some(true); imap_config.starttls = Some(true);
143 143
} }
_ => 143, _ => 143,
}; };
backend.port = Input::with_theme(&*THEME) imap_config.port = Input::with_theme(&*THEME)
.with_prompt("Enter the IMAP port:") .with_prompt("Which IMAP port would you like to use?")
.validate_with(|input: &String| input.parse::<u16>().map(|_| ())) .validate_with(|input: &String| input.parse::<u16>().map(|_| ()))
.default(default_port.to_string()) .default(default_port.to_string())
.interact() .interact()
.map(|input| input.parse::<u16>().unwrap())?; .map(|input| input.parse::<u16>().unwrap())?;
backend.login = Input::with_theme(&*THEME) imap_config.login = Input::with_theme(&*THEME)
.with_prompt("Enter your IMAP login:") .with_prompt("What is your IMAP login?")
.default(base.email.clone()) .default(base.email.clone())
.interact()?; .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 // FIXME: add all variants: password, password command and oauth2
// backend.passwd_cmd = Input::with_theme(&*THEME) // backend.passwd_cmd = Input::with_theme(&*THEME)
// .with_prompt("What shell command should we run to get your password?") // .with_prompt("What shell command should we run to get your password?")
// .default(format!("pass show {}", &base.email)) // .default(format!("pass show {}", &base.email))
// .interact()?; // .interact()?;
Ok(DeserializedAccountConfig::Imap( Ok(BackendConfig::Imap(imap_config))
DeserializedImapAccountConfig { base, backend },
))
} }

View file

@ -1,13 +1,13 @@
use super::THEME;
use crate::account::{
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedMaildirAccountConfig,
};
use anyhow::Result; use anyhow::Result;
use dialoguer::Input; use dialoguer::Input;
use dirs::home_dir; 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() { let input = if let Some(home) = home_dir() {
Input::with_theme(&*THEME) Input::with_theme(&*THEME)
.default(home.join("Mail").display().to_string()) .default(home.join("Mail").display().to_string())
@ -19,12 +19,7 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
.interact_text()? .interact_text()?
}; };
Ok(DeserializedAccountConfig::Maildir( maildir_config.root_dir = input.into();
DeserializedMaildirAccountConfig {
base, Ok(BackendConfig::Maildir(maildir_config))
backend: MaildirConfig {
root_dir: input.into(),
},
},
))
} }

View file

@ -8,26 +8,37 @@ mod smtp;
mod validators; mod validators;
use super::DeserializedConfig; use super::DeserializedConfig;
use crate::account::{DeserializedAccountConfig, DeserializedBaseAccountConfig}; use crate::account::DeserializedAccountConfig;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use console::style; use console::style;
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select}; use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select};
use log::trace; use log::trace;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pimalaya_email::{BackendConfig, SenderConfig};
use std::{fs, io, process}; use std::{fs, io, process};
const BACKENDS: &[&str] = &[ const BACKENDS: &[&str] = &[
"Maildir",
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
"IMAP", "IMAP",
"Maildir",
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
"Notmuch", "Notmuch",
"None",
]; ];
const SENDERS: &[&str] = &["SMTP", "Sendmail"]; const SENDERS: &[&str] = &["SMTP", "Sendmail"];
const SECURITY_PROTOCOLS: &[&str] = &["SSL/TLS", "STARTTLS", "None"]; 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 💅 // A wizard should have pretty colors 💅
static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default); static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
@ -45,9 +56,10 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
} }
// Determine path to save to // Determine path to save to
let path = dirs::config_dir() // let path = dirs::config_dir()
.map(|p| p.join("himalaya").join("config.toml")) // .map(|p| p.join("himalaya").join("config.toml"))
.ok_or_else(|| anyhow!("The wizard could not determine the config directory. Aborting"))?; // .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(); 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 // 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 // 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()), 1 => Some(config.accounts.values_mut().next().unwrap()),
i if i > 1 => { i if i > 1 => {
let accounts = config.accounts.clone(); let accounts = config.accounts.clone();
@ -97,14 +109,8 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
_ => process::exit(0), _ => process::exit(0),
}; };
match default { if let Some(account) = default_account {
Some(DeserializedAccountConfig::None(default)) => default.default = Some(true), account.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),
_ => {}
} }
// Serialize config to file // Serialize config to file
@ -117,18 +123,18 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
} }
fn configure_account() -> Result<Option<DeserializedAccountConfig>> { fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
let mut base = configure_base()?; let mut config = DeserializedAccountConfig::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.email_sender = match sender { config.email = Input::with_theme(&*THEME)
Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&base), .with_prompt("What is your email address?")
Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(), .validate_with(validators::EmailValidator)
_ => return Ok(None), .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) let backend = Select::with_theme(&*THEME)
.with_prompt("Which backend would you like to configure your account for?") .with_prompt("Which backend would you like to configure your account for?")
@ -136,32 +142,28 @@ fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
.default(0) .default(0)
.interact_opt()?; .interact_opt()?;
match backend { config.backend = match backend {
Some(idx) if BACKENDS[idx] == "Maildir" => Ok(Some(maildir::configure(base)?)), Some(idx) if BACKENDS[idx] == "IMAP" => imap::configure(&config),
#[cfg(feature = "imap-backend")] Some(idx) if BACKENDS[idx] == "Maildir" => maildir::configure(),
Some(idx) if BACKENDS[idx] == "IMAP" => Ok(Some(imap::configure(base)?)), Some(idx) if BACKENDS[idx] == "Notmuch" => notmuch::configure(),
#[cfg(feature = "notmuch-backend")] Some(idx) if BACKENDS[idx] == "None" => Ok(BackendConfig::None),
Some(idx) if BACKENDS[idx] == "Notmuch" => Ok(Some(notmuch::configure(base)?)), _ => return Ok(None),
_ => Ok(None), }?;
}
}
fn configure_base() -> Result<DeserializedBaseAccountConfig> { let sender = Select::with_theme(&*THEME)
let mut base_account_config = DeserializedBaseAccountConfig { .with_prompt("Which sender would you like use with your account?")
email: Input::with_theme(&*THEME) .items(SENDERS)
.with_prompt("Enter your email:") .default(0)
.validate_with(validators::EmailValidator) .interact_opt()?;
.interact()?,
..Default::default()
};
base_account_config.display_name = Some( config.sender = match sender {
Input::with_theme(&*THEME) Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&config),
.with_prompt("Enter display name:") Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(),
.interact()?, 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> { pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {

View file

@ -1,13 +1,13 @@
use super::THEME;
use crate::account::{
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedNotmuchAccountConfig,
};
use anyhow::Result; use anyhow::Result;
use dialoguer::Input; use dialoguer::Input;
use pimalaya_email::{NotmuchBackend, NotmuchConfig}; use pimalaya_email::{BackendConfig, NotmuchBackend, NotmuchConfig};
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> { use super::THEME;
let db_path = match NotmuchBackend::get_default_db_path() {
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, Ok(db) => db,
_ => { _ => {
let input: String = Input::with_theme(&*THEME) 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(BackendConfig::Notmuch(notmuch_config))
Ok(DeserializedAccountConfig::Notmuch(
DeserializedNotmuchAccountConfig { base, backend },
))
} }

View file

@ -1,13 +1,17 @@
use super::THEME;
use anyhow::Result; use anyhow::Result;
use dialoguer::Input; use dialoguer::Input;
use pimalaya_email::{EmailSender, SendmailConfig}; use pimalaya_email::{SenderConfig, SendmailConfig};
pub(crate) fn configure() -> Result<EmailSender> { use super::THEME;
Ok(EmailSender::Sendmail(SendmailConfig {
cmd: Input::with_theme(&*THEME) pub(crate) fn configure() -> Result<SenderConfig> {
.with_prompt("Enter an external command to send a mail: ") let mut sendmail_config = SendmailConfig::default();
.default("/usr/bin/msmtp".to_owned())
.interact()?, 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))
} }

View file

@ -1,14 +1,16 @@
use super::{SECURITY_PROTOCOLS, THEME};
use crate::account::DeserializedBaseAccountConfig;
use anyhow::Result; use anyhow::Result;
use dialoguer::{Input, Select}; 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 { let mut smtp_config = SmtpConfig {
host: Input::with_theme(&*THEME) host: Input::with_theme(&*THEME)
.with_prompt("Enter the SMTP host: ") .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()?, .interact()?,
..Default::default() ..Default::default()
}; };
@ -39,7 +41,7 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
smtp_config.login = Input::with_theme(&*THEME) smtp_config.login = Input::with_theme(&*THEME)
.with_prompt("Enter your SMTP login:") .with_prompt("Enter your SMTP login:")
.default(base.email.clone()) .default(config.email.clone())
.interact()?; .interact()?;
// FIXME: add all variants: password, password command and oauth2 // 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)) // .default(format!("pass show {}", &base.email))
// .interact()?; // .interact()?;
Ok(EmailSender::Smtp(smtp_config)) Ok(SenderConfig::Smtp(smtp_config))
} }

View file

@ -5,6 +5,7 @@
//! accounts from the config file. //! accounts from the config file.
use anyhow::Result; use anyhow::Result;
use pimalaya_email::BackendConfig;
use serde::Serialize; use serde::Serialize;
use std::{collections::hash_map::Iter, ops::Deref}; use std::{collections::hash_map::Iter, ops::Deref};
@ -39,19 +40,19 @@ impl PrintTable for Accounts {
impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts { impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self { fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
let mut accounts: Vec<_> = map let mut accounts: Vec<_> = map
.map(|(name, account)| match account { .map(|(name, account)| match &account.backend {
DeserializedAccountConfig::Maildir(config) => { BackendConfig::None => Account::new(name, "none", false),
Account::new(name, "maildir", config.base.default.unwrap_or_default()) BackendConfig::Maildir(_) => {
Account::new(name, "maildir", account.default.unwrap_or_default())
} }
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
DeserializedAccountConfig::Imap(config) => { BackendConfig::Imap(_) => {
Account::new(name, "imap", config.base.default.unwrap_or_default()) Account::new(name, "imap", account.default.unwrap_or_default())
} }
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
DeserializedAccountConfig::Notmuch(config) => { BackendConfig::Notmuch(_) => {
Account::new(name, "notmuch", config.base.default.unwrap_or_default()) Account::new(name, "notmuch", account.default.unwrap_or_default())
} }
DeserializedAccountConfig::None(..) => Account::new(name, "none", false),
}) })
.collect(); .collect();
accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap()); accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());

View file

@ -5,93 +5,18 @@
use pimalaya_email::{ use pimalaya_email::{
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks, folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
EmailSender, EmailTextPlainFormat, ImapAuthConfig, MaildirConfig, EmailTextPlainFormat, SenderConfig,
}; };
use pimalaya_process::Cmd;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf}; 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}; use crate::config::{prelude::*, DeserializedConfig};
/// Represents all existing kind of account config. /// 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)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")] #[serde(tag = "backend", rename_all = "kebab-case")]
pub struct DeserializedBaseAccountConfig { pub struct DeserializedAccountConfig {
pub email: String, pub email: String,
pub default: Option<bool>, pub default: Option<bool>,
pub display_name: Option<String>, pub display_name: Option<String>,
@ -106,13 +31,32 @@ pub struct DeserializedBaseAccountConfig {
pub email_reading_headers: Option<Vec<String>>, pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "EmailTextPlainFormatDef")] #[serde(default, with = "EmailTextPlainFormatDef")]
pub email_reading_format: EmailTextPlainFormat, pub email_reading_format: EmailTextPlainFormat,
pub email_reading_verify_cmd: Option<String>, #[serde(
pub email_reading_decrypt_cmd: Option<String>, 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_headers: Option<Vec<String>>,
pub email_writing_sign_cmd: Option<String>, #[serde(
pub email_writing_encrypt_cmd: Option<String>, default,
#[serde(flatten, with = "EmailSenderDef")] with = "OptionCmdDef",
pub email_sender: EmailSender, 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( #[serde(
default, default,
with = "EmailHooksDef", with = "EmailHooksDef",
@ -125,9 +69,14 @@ pub struct DeserializedBaseAccountConfig {
pub sync_dir: Option<PathBuf>, pub sync_dir: Option<PathBuf>,
#[serde(default, with = "SyncFoldersStrategyDef")] #[serde(default, with = "SyncFoldersStrategyDef")]
pub sync_folders_strategy: SyncFoldersStrategy, 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 { pub fn to_account_config(&self, name: String, config: &DeserializedConfig) -> AccountConfig {
let mut folder_aliases = config let mut folder_aliases = config
.folder_aliases .folder_aliases
@ -222,39 +171,16 @@ impl DeserializedBaseAccountConfig {
.as_ref() .as_ref()
.map(ToOwned::to_owned) .map(ToOwned::to_owned)
.or_else(|| config.email_writing_headers.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 { email_hooks: EmailHooks {
pre_send: self.email_hooks.pre_send.clone(), pre_send: self.email_hooks.pre_send.clone(),
}, },
sync: self.sync, sync: self.sync,
sync_dir: self.sync_dir.clone(), sync_dir: self.sync_dir.clone(),
sync_folders_strategy: self.sync_folders_strategy.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,
}

View file

@ -7,7 +7,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use log::{info, trace, warn}; use log::{info, trace, warn};
use pimalaya_email::{ use pimalaya_email::{
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig, folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
BackendSyncBuilder, BackendSyncProgressEvent, EmailSender, ImapAuthConfig, SmtpAuthConfig, BackendSyncBuilder, BackendSyncProgressEvent, ImapAuthConfig, SenderConfig, SmtpAuthConfig,
}; };
use crate::{ use crate::{
@ -20,16 +20,12 @@ use crate::{
}; };
/// Configure the current selected account /// Configure the current selected account
pub fn configure( pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
account_config: &AccountConfig,
backend_config: &BackendConfig,
reset: bool,
) -> Result<()> {
info!("entering the configure account handler"); info!("entering the configure account handler");
if reset { if reset {
#[cfg(feature = "imap-backend")] #[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 { let reset = match &imap_config.auth {
ImapAuthConfig::Passwd(passwd) => passwd.reset(), ImapAuthConfig::Passwd(passwd) => passwd.reset(),
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(), ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
@ -41,7 +37,7 @@ pub fn configure(
} }
#[cfg(feature = "smtp-sender")] #[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 { let reset = match &smtp_config.auth {
SmtpAuthConfig::Passwd(passwd) => passwd.reset(), SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(), SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
@ -54,7 +50,7 @@ pub fn configure(
} }
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
if let BackendConfig::Imap(imap_config) = backend_config { if let BackendConfig::Imap(imap_config) = &config.backend {
match &imap_config.auth { match &imap_config.auth {
ImapAuthConfig::Passwd(passwd) => { ImapAuthConfig::Passwd(passwd) => {
passwd.configure(|| prompt_passwd("Enter your IMAP password:")) passwd.configure(|| prompt_passwd("Enter your IMAP password:"))
@ -66,7 +62,7 @@ pub fn configure(
} }
#[cfg(feature = "smtp-sender")] #[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 { match &smtp_config.auth {
SmtpAuthConfig::Passwd(passwd) => { SmtpAuthConfig::Passwd(passwd) => {
passwd.configure(|| prompt_passwd("Enter your SMTP password:")) passwd.configure(|| prompt_passwd("Enter your SMTP password:"))
@ -296,9 +292,7 @@ mod tests {
use termcolor::ColorSpec; use termcolor::ColorSpec;
use crate::{ use crate::{
account::{ account::DeserializedAccountConfig,
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
},
printer::{Print, PrintTable, WriteColor}, printer::{Print, PrintTable, WriteColor},
}; };
@ -370,13 +364,11 @@ mod tests {
let deserialized_config = DeserializedConfig { let deserialized_config = DeserializedConfig {
accounts: HashMap::from_iter([( accounts: HashMap::from_iter([(
"account-1".into(), "account-1".into(),
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig { DeserializedAccountConfig {
base: DeserializedBaseAccountConfig { default: Some(true),
default: Some(true), backend: BackendConfig::Imap(ImapConfig::default()),
..DeserializedBaseAccountConfig::default() ..DeserializedAccountConfig::default()
}, },
backend: ImapConfig::default(),
}),
)]), )]),
..DeserializedConfig::default() ..DeserializedConfig::default()
}; };

View file

@ -165,9 +165,9 @@ pub fn list<P: Printer>(
/// [mailto]: https://en.wikipedia.org/wiki/Mailto /// [mailto]: https://en.wikipedia.org/wiki/Mailto
pub fn mailto<P: Printer>( pub fn mailto<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
sender: &mut dyn Sender, sender: &mut dyn Sender,
printer: &mut P,
url: &Url, url: &Url,
) -> Result<()> { ) -> Result<()> {
let mut tpl = TplBuilder::default().to(url.path()); let mut tpl = TplBuilder::default().to(url.path());

View file

@ -74,8 +74,8 @@ pub fn save<P: Printer>(
}) })
.compile( .compile(
CompilerBuilder::default() CompilerBuilder::default()
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()), .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
)?; )?;
let id = backend.add_email(folder, &email, &Flags::default())?; let id = backend.add_email(folder, &email, &Flags::default())?;
@ -104,8 +104,8 @@ pub fn send<P: Printer>(
}) })
.compile( .compile(
CompilerBuilder::default() CompilerBuilder::default()
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()), .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
)?; )?;
sender.send(&email)?; sender.send(&email)?;
backend.add_email(folder, &email, &Flags::default())?; backend.add_email(folder, &email, &Flags::default())?;

View file

@ -51,17 +51,16 @@ fn main() -> Result<()> {
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") { if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
let url = Url::parse(&raw_args[1])?; let url = Url::parse(&raw_args[1])?;
let config = DeserializedConfig::from_opt_path(None)?; let config = DeserializedConfig::from_opt_path(None)?;
let (account_config, backend_config) = config.to_configs(None)?; let account_config = config.to_account_config(None)?;
let mut backend = let mut backend = BackendBuilder::new().build(&account_config)?;
BackendBuilder::new().build(account_config.clone(), backend_config.clone())?; let mut sender = SenderBuilder::new().build(&account_config)?;
let mut sender = SenderBuilder::build(&account_config)?;
let mut printer = StdoutPrinter::default(); let mut printer = StdoutPrinter::default();
return email::handlers::mailto( return email::handlers::mailto(
&account_config, &account_config,
&mut printer,
backend.as_mut(), backend.as_mut(),
sender.as_mut(), sender.as_mut(),
&mut printer,
&url, &url,
); );
} }
@ -88,12 +87,12 @@ fn main() -> Result<()> {
// inits config // inits config
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?; 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); let folder = folder::args::parse_source_arg(&m);
// checks IMAP commands // checks IMAP commands
#[cfg(feature = "imap-backend")] #[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))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
// FIXME: find a way to downcast `backend` instead of // FIXME: find a way to downcast `backend` instead of
@ -112,7 +111,7 @@ fn main() -> Result<()> {
} }
// inits services // 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 mut printer = StdoutPrinter::try_from(&m)?;
let disable_cache = cache::args::parse_disable_cache_flag(&m); let disable_cache = cache::args::parse_disable_cache_flag(&m);
@ -125,7 +124,7 @@ fn main() -> Result<()> {
let backend = BackendBuilder::new() let backend = BackendBuilder::new()
.sessions_pool_size(8) .sessions_pool_size(8)
.disable_cache(true) .disable_cache(true)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
account::handlers::sync( account::handlers::sync(
&account_config, &account_config,
&mut printer, &mut printer,
@ -137,7 +136,7 @@ fn main() -> Result<()> {
return Ok(()); return Ok(());
} }
Some(account::args::Cmd::Configure(reset)) => { 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 folder = account_config.folder_alias(folder)?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return folder::handlers::create(&mut printer, backend.as_mut(), &folder); return folder::handlers::create(&mut printer, backend.as_mut(), &folder);
} }
Some(folder::args::Cmd::List(max_width)) => { Some(folder::args::Cmd::List(max_width)) => {
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return folder::handlers::list( return folder::handlers::list(
&account_config, &account_config,
&mut printer, &mut printer,
@ -169,7 +168,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder); return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder);
} }
Some(folder::args::Cmd::Delete) => { Some(folder::args::Cmd::Delete) => {
@ -179,7 +178,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder)?; let folder = account_config.folder_alias(folder)?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder); 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 folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::attachments( return email::handlers::attachments(
&account_config, &account_config,
@ -206,7 +205,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::copy( return email::handlers::copy(
&account_config, &account_config,
@ -222,7 +221,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::delete( return email::handlers::delete(
&account_config, &account_config,
@ -237,7 +236,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::forward( return email::handlers::forward(
&account_config, &account_config,
@ -255,7 +254,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::list( return email::handlers::list(
&account_config, &account_config,
@ -272,7 +271,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::move_( return email::handlers::move_(
&account_config, &account_config,
@ -288,7 +287,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::read( return email::handlers::read(
&account_config, &account_config,
@ -307,7 +306,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::reply( return email::handlers::reply(
&account_config, &account_config,
@ -326,7 +325,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::save( return email::handlers::save(
&account_config, &account_config,
@ -341,7 +340,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::search( return email::handlers::search(
&account_config, &account_config,
@ -359,7 +358,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return email::handlers::sort( return email::handlers::sort(
&account_config, &account_config,
@ -377,7 +376,7 @@ fn main() -> Result<()> {
Some(email::args::Cmd::Send(raw_email)) => { Some(email::args::Cmd::Send(raw_email)) => {
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return email::handlers::send( return email::handlers::send(
&account_config, &account_config,
&mut printer, &mut printer,
@ -391,7 +390,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return flag::handlers::set( return flag::handlers::set(
&mut printer, &mut printer,
@ -406,7 +405,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return flag::handlers::add( return flag::handlers::add(
&mut printer, &mut printer,
@ -421,7 +420,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return flag::handlers::remove( return flag::handlers::remove(
&mut printer, &mut printer,
@ -439,7 +438,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return tpl::handlers::forward( return tpl::handlers::forward(
&account_config, &account_config,
@ -459,7 +458,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return tpl::handlers::reply( return tpl::handlers::reply(
&account_config, &account_config,
@ -477,7 +476,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
return tpl::handlers::save( return tpl::handlers::save(
&account_config, &account_config,
@ -492,7 +491,7 @@ fn main() -> Result<()> {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return tpl::handlers::send( return tpl::handlers::send(
&account_config, &account_config,
&mut printer, &mut printer,
@ -507,7 +506,7 @@ fn main() -> Result<()> {
Some(email::args::Cmd::Write(headers, body)) => { Some(email::args::Cmd::Write(headers, body)) => {
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(account_config.clone(), backend_config.clone())?; .build(&account_config)?;
return email::handlers::write( return email::handlers::write(
&account_config, &account_config,
&mut printer, &mut printer,

View file

@ -75,8 +75,8 @@ pub fn edit_tpl_with_editor<P: Printer>(
printer.print_log("Sending email…")?; printer.print_log("Sending email…")?;
let email = tpl.compile( let email = tpl.compile(
CompilerBuilder::default() CompilerBuilder::default()
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()), .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
)?; )?;
sender.send(&email)?; sender.send(&email)?;
let sent_folder = config.sent_folder_alias()?; 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 draft_folder = config.folder_alias("drafts")?;
let email = tpl.compile( let email = tpl.compile(
CompilerBuilder::default() CompilerBuilder::default()
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()), .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
)?; )?;
backend.add_email( backend.add_email(
&draft_folder, &draft_folder,