diff --git a/Cargo.lock b/Cargo.lock index 8200cd9..406fb25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/src/config/config.rs b/src/config/config.rs index 14e6b92..6fcc7ec 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -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>, #[serde(default, with = "EmailTextPlainFormatDef")] pub email_reading_format: EmailTextPlainFormat, - pub email_reading_verify_cmd: Option, - pub email_reading_decrypt_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_reading_verify_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_reading_decrypt_cmd: Option, pub email_writing_headers: Option>, - pub email_writing_sign_cmd: Option, - pub email_writing_encrypt_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_writing_sign_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_writing_encrypt_cmd: Option, + pub email_sending_save_copy: Option, #[serde( default, with = "EmailHooksDef", @@ -54,13 +76,15 @@ impl DeserializedConfig { pub fn from_opt_path(path: Option<&str>) -> Result { 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 { 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 { @@ -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() } diff --git a/src/config/prelude.rs b/src/config/prelude.rs index 919efd2..2d6c286 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -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 for Cmd { } } +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde(remote = "Option", 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 for Option { + fn from(cmd: OptionSingleCmdOrPipeline) -> Option { + 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), } -#[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, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub pre_send: Option, } #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] diff --git a/src/config/wizard/imap.rs b/src/config/wizard/imap.rs index 3cb2c8a..b758b0b 100644 --- a/src/config/wizard/imap.rs +++ b/src/config/wizard/imap.rs @@ -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 { +pub(crate) fn configure(base: &DeserializedAccountConfig) -> Result { // 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 { - 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::().map(|_| ())) .default(default_port.to_string()) .interact() .map(|input| input.parse::().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)) } diff --git a/src/config/wizard/maildir.rs b/src/config/wizard/maildir.rs index 037deb2..b12b309 100644 --- a/src/config/wizard/maildir.rs +++ b/src/config/wizard/maildir.rs @@ -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 { + let mut maildir_config = MaildirConfig::default(); -pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result { 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 = Lazy::new(ColorfulTheme::default); @@ -45,9 +56,10 @@ pub(crate) fn wizard() -> Result { } // 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 { // 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 { _ => 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 { } fn configure_account() -> Result> { - 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> { .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 { - 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 { diff --git a/src/config/wizard/notmuch.rs b/src/config/wizard/notmuch.rs index df9cc6f..29fff9c 100644 --- a/src/config/wizard/notmuch.rs +++ b/src/config/wizard/notmuch.rs @@ -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 { - let db_path = match NotmuchBackend::get_default_db_path() { +use super::THEME; + +pub(crate) fn configure() -> Result { + 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 Result { - 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 { + 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)) } diff --git a/src/config/wizard/smtp.rs b/src/config/wizard/smtp.rs index 342723f..7e0e868 100644 --- a/src/config/wizard/smtp.rs +++ b/src/config/wizard/smtp.rs @@ -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 { +use crate::account::DeserializedAccountConfig; + +use super::{SECURITY_PROTOCOLS, THEME}; + +pub(crate) fn configure(config: &DeserializedAccountConfig) -> Result { 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 Result> 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()); diff --git a/src/domain/account/config.rs b/src/domain/account/config.rs index 0e4f1c9..792a433 100644 --- a/src/domain/account/config.rs +++ b/src/domain/account/config.rs @@ -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, pub display_name: Option, @@ -106,13 +31,32 @@ pub struct DeserializedBaseAccountConfig { pub email_reading_headers: Option>, #[serde(default, with = "EmailTextPlainFormatDef")] pub email_reading_format: EmailTextPlainFormat, - pub email_reading_verify_cmd: Option, - pub email_reading_decrypt_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_reading_verify_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_reading_decrypt_cmd: Option, pub email_writing_headers: Option>, - pub email_writing_sign_cmd: Option, - pub email_writing_encrypt_cmd: Option, - #[serde(flatten, with = "EmailSenderDef")] - pub email_sender: EmailSender, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_writing_sign_cmd: Option, + #[serde( + default, + with = "OptionCmdDef", + skip_serializing_if = "Option::is_none" + )] + pub email_writing_encrypt_cmd: Option, + pub email_sending_save_copy: Option, #[serde( default, with = "EmailHooksDef", @@ -125,9 +69,14 @@ pub struct DeserializedBaseAccountConfig { pub sync_dir: Option, #[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, -} diff --git a/src/domain/account/handlers.rs b/src/domain/account/handlers.rs index 5fe4fe1..9caeccc 100644 --- a/src/domain/account/handlers.rs +++ b/src/domain/account/handlers.rs @@ -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() }; diff --git a/src/domain/email/handlers.rs b/src/domain/email/handlers.rs index 13f4e10..09bf429 100644 --- a/src/domain/email/handlers.rs +++ b/src/domain/email/handlers.rs @@ -165,9 +165,9 @@ pub fn list( /// [mailto]: https://en.wikipedia.org/wiki/Mailto pub fn mailto( 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()); diff --git a/src/domain/tpl/handlers.rs b/src/domain/tpl/handlers.rs index d74561e..3e2d78e 100644 --- a/src/domain/tpl/handlers.rs +++ b/src/domain/tpl/handlers.rs @@ -74,8 +74,8 @@ pub fn save( }) .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( }) .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())?; diff --git a/src/main.rs b/src/main.rs index 3553aca..a1dba41 100644 --- a/src/main.rs +++ b/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, diff --git a/src/ui/editor.rs b/src/ui/editor.rs index 795332a..1808fd6 100644 --- a/src/ui/editor.rs +++ b/src/ui/editor.rs @@ -75,8 +75,8 @@ pub fn edit_tpl_with_editor( 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( 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,