diff --git a/src/backend/wizard.rs b/src/backend/wizard.rs index 6d9ddca..4234889 100644 --- a/src/backend/wizard.rs +++ b/src/backend/wizard.rs @@ -12,17 +12,17 @@ use crate::{config::wizard::THEME, maildir, sendmail}; use super::{config::BackendConfig, BackendKind}; const DEFAULT_BACKEND_KINDS: &[BackendKind] = &[ - BackendKind::Maildir, #[cfg(feature = "imap-backend")] BackendKind::Imap, + BackendKind::Maildir, #[cfg(feature = "notmuch-backend")] BackendKind::Notmuch, ]; const SEND_MESSAGE_BACKEND_KINDS: &[BackendKind] = &[ - BackendKind::Sendmail, #[cfg(feature = "smtp-sender")] BackendKind::Smtp, + BackendKind::Sendmail, ]; pub(crate) async fn configure(account_name: &str, email: &str) -> Result> { @@ -52,7 +52,7 @@ pub(crate) async fn configure_sender( email: &str, ) -> Result> { let kind = Select::with_theme(&*THEME) - .with_prompt("Default email backend") + .with_prompt("Backend for sending messages") .items(SEND_MESSAGE_BACKEND_KINDS) .default(0) .interact_opt()? diff --git a/src/config/config.rs b/src/config/config.rs index 574dad1..cdc4670 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -44,11 +44,19 @@ pub struct TomlConfig { pub email_listing_datetime_fmt: Option, pub email_listing_datetime_local_tz: Option, pub email_reading_headers: Option>, - #[serde(default, with = "OptionEmailTextPlainFormatDef")] + #[serde( + default, + with = "OptionEmailTextPlainFormatDef", + skip_serializing_if = "Option::is_none" + )] pub email_reading_format: Option, pub email_writing_headers: Option>, pub email_sending_save_copy: Option, - #[serde(default, with = "OptionEmailHooksDef")] + #[serde( + default, + with = "OptionEmailHooksDef", + skip_serializing_if = "Option::is_none" + )] pub email_hooks: Option, #[serde(flatten)] @@ -78,7 +86,7 @@ impl TomlConfig { let confirm = Confirm::new() .with_prompt(wizard_prompt!( - "Would you like to create it with the wizard?" + "Would you like to create one with the wizard?" )) .default(true) .interact_opt()? @@ -88,7 +96,7 @@ impl TomlConfig { process::exit(0); } - wizard::configure().await + wizard::configure(path).await } /// Read and parse the TOML configuration from default paths. diff --git a/src/config/prelude.rs b/src/config/prelude.rs index f61982b..df1aaa7 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -61,27 +61,38 @@ pub enum CmdDef { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionCmd")] +#[serde(remote = "Option", from = "OptionCmd", into = "OptionCmd")] pub struct OptionCmdDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionCmd { - #[default] - #[serde(skip_serializing)] - None, - #[serde(with = "SingleCmdDef")] - SingleCmd(SingleCmd), - #[serde(with = "PipelineDef")] - Pipeline(Pipeline), +pub struct OptionCmd { + #[serde(default, skip)] + is_some: bool, + #[serde(flatten, with = "CmdDef")] + inner: Cmd, } impl From for Option { fn from(cmd: OptionCmd) -> Option { - match cmd { - OptionCmd::None => None, - OptionCmd::SingleCmd(cmd) => Some(Cmd::SingleCmd(cmd)), - OptionCmd::Pipeline(pipeline) => Some(Cmd::Pipeline(pipeline)), + if cmd.is_some { + Some(cmd.inner) + } else { + None + } + } +} + +impl Into for Option { + fn into(self) -> OptionCmd { + match self { + Some(cmd) => OptionCmd { + is_some: true, + inner: cmd, + }, + None => OptionCmd { + is_some: false, + inner: Default::default(), + }, } } } @@ -108,7 +119,11 @@ pub enum OAuth2MethodDef { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionImapConfig")] +#[serde( + remote = "Option", + from = "OptionImapConfig", + into = "OptionImapConfig" +)] pub struct OptionImapConfigDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -129,6 +144,21 @@ impl From for Option { } } +impl Into for Option { + fn into(self) -> OptionImapConfig { + match self { + Some(config) => OptionImapConfig { + is_none: false, + inner: config, + }, + None => OptionImapConfig { + is_none: true, + inner: Default::default(), + }, + } + } +} + #[cfg(feature = "imap-backend")] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(remote = "ImapConfig", rename_all = "kebab-case")] @@ -226,23 +256,42 @@ pub enum ImapOAuth2ScopesDef { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionMaildirConfig")] +#[serde( + remote = "Option", + from = "OptionMaildirConfig", + into = "OptionMaildirConfig" +)] pub struct OptionMaildirConfigDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionMaildirConfig { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "MaildirConfigDef")] MaildirConfig), +pub struct OptionMaildirConfig { + #[serde(default, skip)] + is_none: bool, + #[serde(flatten, with = "MaildirConfigDef")] + inner: MaildirConfig, } impl From for Option { fn from(config: OptionMaildirConfig) -> Option { - match config { - OptionMaildirConfig::None => None, - OptionMaildirConfig::Some(config) => Some(config), + if config.is_none { + None + } else { + Some(config.inner) + } + } +} + +impl Into for Option { + fn into(self) -> OptionMaildirConfig { + match self { + Some(config) => OptionMaildirConfig { + is_none: false, + inner: config, + }, + None => OptionMaildirConfig { + is_none: true, + inner: Default::default(), + }, } } } @@ -256,25 +305,45 @@ pub struct MaildirConfigDef { #[cfg(feature = "notmuch-backend")] #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionNotmuchConfig")] +#[serde( + remote = "Option", + from = "OptionNotmuchConfig", + into = "OptionNotmuchConfig" +)] pub struct OptionNotmuchConfigDef; #[cfg(feature = "notmuch-backend")] #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionNotmuchConfig { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "NotmuchConfigDef")] NotmuchConfig), +pub struct OptionNotmuchConfig { + #[serde(default, skip)] + is_none: bool, + #[serde(flatten, with = "NotmuchConfigDef")] + inner: NotmuchConfig, } #[cfg(feature = "notmuch-backend")] impl From for Option { fn from(config: OptionNotmuchConfig) -> Option { - match config { - OptionNotmuchConfig::None => None, - OptionNotmuchConfig::Some(config) => Some(config), + if config.is_none { + None + } else { + Some(config.inner) + } + } +} + +#[cfg(feature = "notmuch-backend")] +impl Into for Option { + fn into(self) -> OptionNotmuchConfig { + match self { + Some(config) => OptionNotmuchConfig { + is_none: false, + inner: config, + }, + None => OptionNotmuchConfig { + is_none: true, + inner: Default::default(), + }, } } } @@ -290,28 +359,40 @@ pub struct NotmuchConfigDef { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde( remote = "Option", - from = "OptionEmailTextPlainFormat" + from = "OptionEmailTextPlainFormat", + into = "OptionEmailTextPlainFormat" )] pub struct OptionEmailTextPlainFormatDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionEmailTextPlainFormat { - #[serde(skip_serializing)] - None, - #[default] - Auto, - Flowed, - Fixed(usize), +pub struct OptionEmailTextPlainFormat { + #[serde(default, skip)] + is_none: bool, + #[serde(flatten, with = "EmailTextPlainFormatDef")] + inner: EmailTextPlainFormat, } impl From for Option { fn from(fmt: OptionEmailTextPlainFormat) -> Option { - match fmt { - OptionEmailTextPlainFormat::None => None, - OptionEmailTextPlainFormat::Auto => Some(EmailTextPlainFormat::Auto), - OptionEmailTextPlainFormat::Flowed => Some(EmailTextPlainFormat::Flowed), - OptionEmailTextPlainFormat::Fixed(size) => Some(EmailTextPlainFormat::Fixed(size)), + if fmt.is_none { + None + } else { + Some(fmt.inner) + } + } +} + +impl Into for Option { + fn into(self) -> OptionEmailTextPlainFormat { + match self { + Some(config) => OptionEmailTextPlainFormat { + is_none: false, + inner: config, + }, + None => OptionEmailTextPlainFormat { + is_none: true, + inner: Default::default(), + }, } } } @@ -331,7 +412,11 @@ pub enum EmailTextPlainFormatDef { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionSmtpConfig")] +#[serde( + remote = "Option", + from = "OptionSmtpConfig", + into = "OptionSmtpConfig" +)] pub struct OptionSmtpConfigDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -352,6 +437,21 @@ impl From for Option { } } +impl Into for Option { + fn into(self) -> OptionSmtpConfig { + match self { + Some(config) => OptionSmtpConfig { + is_none: false, + inner: config, + }, + None => OptionSmtpConfig { + is_none: true, + inner: Default::default(), + }, + } + } +} + #[cfg(feature = "smtp-sender")] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(remote = "SmtpConfig")] @@ -434,23 +534,42 @@ pub enum SmtpOAuth2ScopesDef { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionSendmailConfig")] +#[serde( + remote = "Option", + from = "OptionSendmailConfig", + into = "OptionSendmailConfig" +)] pub struct OptionSendmailConfigDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionSendmailConfig { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "SendmailConfigDef")] SendmailConfig), +pub struct OptionSendmailConfig { + #[serde(default, skip)] + is_none: bool, + #[serde(flatten, with = "SendmailConfigDef")] + inner: SendmailConfig, } impl From for Option { fn from(config: OptionSendmailConfig) -> Option { - match config { - OptionSendmailConfig::None => None, - OptionSendmailConfig::Some(config) => Some(config), + if config.is_none { + None + } else { + Some(config.inner) + } + } +} + +impl Into for Option { + fn into(self) -> OptionSendmailConfig { + match self { + Some(config) => OptionSendmailConfig { + is_none: false, + inner: config, + }, + None => OptionSendmailConfig { + is_none: true, + inner: Default::default(), + }, } } } @@ -467,23 +586,46 @@ fn sendmail_default_cmd() -> Cmd { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "Option", from = "OptionEmailHooks")] +#[serde( + remote = "Option", + from = "OptionEmailHooks", + into = "OptionEmailHooks" +)] pub struct OptionEmailHooksDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionEmailHooks { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "EmailHooksDef")] EmailHooks), +pub struct OptionEmailHooks { + #[serde(default, skip)] + is_none: bool, + #[serde( + flatten, + skip_serializing_if = "EmailHooks::is_empty", + with = "EmailHooksDef" + )] + inner: EmailHooks, } impl From for Option { - fn from(fmt: OptionEmailHooks) -> Option { - match fmt { - OptionEmailHooks::None => None, - OptionEmailHooks::Some(hooks) => Some(hooks), + fn from(hooks: OptionEmailHooks) -> Option { + if hooks.is_none { + None + } else { + Some(hooks.inner) + } + } +} + +impl Into for Option { + fn into(self) -> OptionEmailHooks { + match self { + Some(hooks) => OptionEmailHooks { + is_none: false, + inner: hooks, + }, + None => OptionEmailHooks { + is_none: true, + inner: Default::default(), + }, } } } @@ -501,24 +643,44 @@ pub struct EmailHooksDef { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde( remote = "Option", - from = "OptionFolderSyncStrategy" + from = "OptionFolderSyncStrategy", + into = "OptionFolderSyncStrategy" )] pub struct OptionFolderSyncStrategyDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionFolderSyncStrategy { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "FolderSyncStrategyDef")] FolderSyncStrategy), +pub struct OptionFolderSyncStrategy { + #[serde(default, skip)] + is_some: bool, + #[serde( + flatten, + skip_serializing_if = "FolderSyncStrategy::is_default", + with = "FolderSyncStrategyDef" + )] + inner: FolderSyncStrategy, } impl From for Option { - fn from(config: OptionFolderSyncStrategy) -> Option { - match config { - OptionFolderSyncStrategy::None => None, - OptionFolderSyncStrategy::Some(config) => Some(config), + fn from(option: OptionFolderSyncStrategy) -> Option { + if option.is_some { + Some(option.inner) + } else { + None + } + } +} + +impl Into for Option { + fn into(self) -> OptionFolderSyncStrategy { + match self { + Some(strategy) => OptionFolderSyncStrategy { + is_some: true, + inner: strategy, + }, + None => OptionFolderSyncStrategy { + is_some: false, + inner: Default::default(), + }, } } } diff --git a/src/config/wizard.rs b/src/config/wizard.rs index f6b359e..ffb3a23 100644 --- a/src/config/wizard.rs +++ b/src/config/wizard.rs @@ -1,10 +1,13 @@ -use super::TomlConfig; -use crate::account; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select}; use once_cell::sync::Lazy; -use shellexpand_utils::{shellexpand_path, try_shellexpand_path}; -use std::{env, fs, io, process}; +use shellexpand_utils::shellexpand_path; +use std::{fs, io, path::PathBuf, process}; +use toml_edit::{Document, Item}; + +use crate::account; + +use super::TomlConfig; #[macro_export] macro_rules! wizard_warn { @@ -31,7 +34,7 @@ macro_rules! wizard_log { pub(crate) static THEME: Lazy = Lazy::new(ColorfulTheme::default); -pub(crate) async fn configure() -> Result { +pub(crate) async fn configure(path: PathBuf) -> Result { wizard_log!("Configuring your first account:"); let mut config = TomlConfig::default(); @@ -89,25 +92,86 @@ pub(crate) async fn configure() -> Result { .with_prompt(wizard_prompt!( "Where would you like to save your configuration?" )) - .default( - dirs::config_dir() - .map(|p| p.join("himalaya").join("config.toml")) - .unwrap_or_else(|| env::temp_dir().join("himalaya").join("config.toml")) - .to_string_lossy() - .to_string(), - ) - .validate_with(|path: &String| try_shellexpand_path(path).map(|_| ())) + .default(path.to_string_lossy().to_string()) .interact()?; let path = shellexpand_path(&path); println!("Writing the configuration to {path:?}…"); + let mut doc = toml::to_string(&config)?.parse::()?; + + doc.iter_mut().for_each(|(_, item)| { + set_table_dotted(item, "folder-aliases"); + set_table_dotted(item, "sync-folders-strategy"); + + set_table_dotted(item, "folder"); + get_table_mut(item, "folder").map(|item| { + set_tables_dotted(item, ["add", "list", "expunge", "purge", "delete"]); + }); + + set_table_dotted(item, "envelope"); + get_table_mut(item, "envelope").map(|item| { + set_tables_dotted(item, ["list", "get"]); + }); + + set_table_dotted(item, "flag"); + get_table_mut(item, "flag").map(|item| { + set_tables_dotted(item, ["add", "set", "remove"]); + }); + + set_table_dotted(item, "message"); + get_table_mut(item, "message").map(|item| { + set_tables_dotted( + item, + ["add", "send", "peek", "get", "copy", "move", "delete"], + ); + }); + + set_table_dotted(item, "maildir"); + #[cfg(feature = "imap-backend")] + { + set_table_dotted(item, "imap"); + get_table_mut(item, "imap").map(|item| { + set_tables_dotted(item, ["passwd", "oauth2"]); + }); + } + #[cfg(feature = "notmuch-backend")] + set_table_dotted(item, "notmuch"); + set_table_dotted(item, "sendmail"); + #[cfg(feature = "smtp-sender")] + { + set_table_dotted(item, "smtp"); + get_table_mut(item, "smtp").map(|item| { + set_tables_dotted(item, ["passwd", "oauth2"]); + }); + } + + #[cfg(feature = "pgp")] + set_table_dotted(item, "pgp"); + }); + fs::create_dir_all(path.parent().unwrap_or(&path))?; - fs::write(path, toml::to_string(&config)?)?; + fs::write(path, doc.to_string())?; Ok(config) } +fn get_table_mut<'a>(item: &'a mut Item, key: &'a str) -> Option<&'a mut Item> { + item.get_mut(key).filter(|item| item.is_table()) +} + +fn set_table_dotted(item: &mut Item, key: &str) { + get_table_mut(item, key) + .and_then(|item| item.as_table_mut()) + .map(|table| table.set_dotted(true)); +} + +fn set_tables_dotted<'a>(item: &'a mut Item, keys: impl IntoIterator) { + for key in keys { + set_table_dotted(item, key) + } +} + pub(crate) fn prompt_passwd(prompt: &str) -> io::Result { Password::with_theme(&*THEME) .with_prompt(prompt) diff --git a/src/domain/account/config.rs b/src/domain/account/config.rs index d514396..01186ae 100644 --- a/src/domain/account/config.rs +++ b/src/domain/account/config.rs @@ -31,7 +31,7 @@ use crate::{ /// Represents all existing kind of account config. #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] -#[serde(tag = "backend", rename_all = "kebab-case")] +#[serde(rename_all = "kebab-case")] pub struct TomlAccountConfig { pub default: Option, @@ -48,16 +48,28 @@ pub struct TomlAccountConfig { pub email_listing_datetime_fmt: Option, pub email_listing_datetime_local_tz: Option, pub email_reading_headers: Option>, - #[serde(default, with = "OptionEmailTextPlainFormatDef")] + #[serde( + default, + with = "OptionEmailTextPlainFormatDef", + skip_serializing_if = "Option::is_none" + )] pub email_reading_format: Option, pub email_writing_headers: Option>, pub email_sending_save_copy: Option, - #[serde(default, with = "OptionEmailHooksDef")] + #[serde( + default, + with = "OptionEmailHooksDef", + skip_serializing_if = "Option::is_none" + )] pub email_hooks: Option, pub sync: Option, pub sync_dir: Option, - #[serde(default, with = "OptionFolderSyncStrategyDef")] + #[serde( + default, + with = "OptionFolderSyncStrategyDef", + skip_serializing_if = "Option::is_none" + )] pub sync_folders_strategy: Option, pub backend: Option, @@ -68,25 +80,49 @@ pub struct TomlAccountConfig { pub message: Option, #[cfg(feature = "imap-backend")] - #[serde(default, with = "OptionImapConfigDef")] + #[serde( + default, + with = "OptionImapConfigDef", + skip_serializing_if = "Option::is_none" + )] pub imap: Option, - #[serde(default, with = "OptionMaildirConfigDef")] + #[serde( + default, + with = "OptionMaildirConfigDef", + skip_serializing_if = "Option::is_none" + )] pub maildir: Option, #[cfg(feature = "notmuch-backend")] - #[serde(default, with = "OptionNotmuchConfigDef")] + #[serde( + default, + with = "OptionNotmuchConfigDef", + skip_serializing_if = "Option::is_none" + )] pub notmuch: Option, #[cfg(feature = "smtp-sender")] - #[serde(default, with = "OptionSmtpConfigDef")] + #[serde( + default, + with = "OptionSmtpConfigDef", + skip_serializing_if = "Option::is_none" + )] pub smtp: Option, - #[serde(default, with = "OptionSendmailConfigDef")] + #[serde( + default, + with = "OptionSendmailConfigDef", + skip_serializing_if = "Option::is_none" + )] pub sendmail: Option, #[cfg(feature = "pgp")] - #[serde(default, with = "OptionPgpConfigDef")] + #[serde( + default, + with = "OptionPgpConfigDef", + skip_serializing_if = "Option::is_none" + )] pub pgp: Option, } diff --git a/src/domain/account/handlers.rs b/src/domain/account/handlers.rs index 29d5a5b..0905563 100644 --- a/src/domain/account/handlers.rs +++ b/src/domain/account/handlers.rs @@ -2,7 +2,7 @@ //! //! This module gathers all account actions triggered by the CLI. -use anyhow::{Context, Result}; +use anyhow::Result; use email::account::{ sync::{AccountSyncBuilder, AccountSyncProgressEvent}, AccountConfig, @@ -12,7 +12,7 @@ use email::imap::ImapAuthConfig; #[cfg(feature = "smtp-sender")] use email::smtp::SmtpAuthConfig; use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle}; -use log::{info, trace, warn}; +use log::{debug, info, trace, warn}; use once_cell::sync::Lazy; use std::{collections::HashMap, sync::Mutex}; @@ -26,6 +26,8 @@ use crate::{ Accounts, }; +use super::TomlAccountConfig; + const MAIN_PROGRESS_STYLE: Lazy = Lazy::new(|| { ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap() }); @@ -42,71 +44,75 @@ const SUB_PROGRESS_DONE_STYLE: Lazy = Lazy::new(|| { }); /// Configure the current selected account -pub async fn configure(config: &AccountConfig, reset: bool) -> Result<()> { +pub async fn configure(config: &TomlAccountConfig, reset: bool) -> Result<()> { info!("entering the configure account handler"); - // if reset { - // #[cfg(feature = "imap-backend")] - // if let BackendConfig::Imap(imap_config) = &config.backend { - // let reset = match &imap_config.auth { - // ImapAuthConfig::Passwd(passwd) => passwd.reset(), - // ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(), - // }; - // if let Err(err) = reset { - // warn!("error while resetting imap secrets, skipping it"); - // warn!("{err}"); - // } - // } + if reset { + #[cfg(feature = "imap-backend")] + if let Some(ref config) = config.imap { + let reset = match &config.auth { + ImapAuthConfig::Passwd(config) => config.reset(), + ImapAuthConfig::OAuth2(config) => config.reset(), + }; + if let Err(err) = reset { + warn!("error while resetting imap secrets: {err}"); + debug!("error while resetting imap secrets: {err:?}"); + } + } - // #[cfg(feature = "smtp-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(), - // }; - // if let Err(err) = reset { - // warn!("error while resetting smtp secrets, skipping it"); - // warn!("{err}"); - // } - // } + #[cfg(feature = "smtp-sender")] + if let Some(ref config) = config.smtp { + let reset = match &config.auth { + SmtpAuthConfig::Passwd(config) => config.reset(), + SmtpAuthConfig::OAuth2(config) => config.reset(), + }; + if let Err(err) = reset { + warn!("error while resetting smtp secrets: {err}"); + debug!("error while resetting smtp secrets: {err:?}"); + } + } - // #[cfg(feature = "pgp")] - // config.pgp.reset().await?; - // } + #[cfg(feature = "pgp")] + if let Some(ref config) = config.pgp { + config.pgp.reset().await?; + } + } - // #[cfg(feature = "imap-backend")] - // if let BackendConfig::Imap(imap_config) = &config.backend { - // match &imap_config.auth { - // ImapAuthConfig::Passwd(passwd) => { - // passwd.configure(|| prompt_passwd("IMAP password")).await - // } - // ImapAuthConfig::OAuth2(oauth2) => { - // oauth2 - // .configure(|| prompt_secret("IMAP OAuth 2.0 client secret")) - // .await - // } - // }?; - // } + #[cfg(feature = "imap-backend")] + if let Some(ref config) = config.imap { + match &config.auth { + ImapAuthConfig::Passwd(config) => { + config.configure(|| prompt_passwd("IMAP password")).await + } + ImapAuthConfig::OAuth2(config) => { + config + .configure(|| prompt_secret("IMAP OAuth 2.0 client secret")) + .await + } + }?; + } - // #[cfg(feature = "smtp-sender")] - // if let SenderConfig::Smtp(smtp_config) = &config.sender { - // match &smtp_config.auth { - // SmtpAuthConfig::Passwd(passwd) => { - // passwd.configure(|| prompt_passwd("SMTP password")).await - // } - // SmtpAuthConfig::OAuth2(oauth2) => { - // oauth2 - // .configure(|| prompt_secret("SMTP OAuth 2.0 client secret")) - // .await - // } - // }?; - // } + #[cfg(feature = "smtp-sender")] + if let Some(ref config) = config.smtp { + match &config.auth { + SmtpAuthConfig::Passwd(config) => { + config.configure(|| prompt_passwd("SMTP password")).await + } + SmtpAuthConfig::OAuth2(config) => { + config + .configure(|| prompt_secret("SMTP OAuth 2.0 client secret")) + .await + } + }?; + } - // #[cfg(feature = "pgp")] - // config - // .pgp - // .configure(&config.email, || prompt_passwd("PGP secret key password")) - // .await?; + #[cfg(feature = "pgp")] + if let Some(ref config) = config.pgp { + config + .pgp + .configure(&config.email, || prompt_passwd("PGP secret key password")) + .await?; + } println!( "Account successfully {}configured!", diff --git a/src/domain/email/message/config.rs b/src/domain/email/message/config.rs index 0ab96a4..0e3f080 100644 --- a/src/domain/email/message/config.rs +++ b/src/domain/email/message/config.rs @@ -10,7 +10,7 @@ pub struct MessageConfig { pub peek: Option, pub get: Option, pub copy: Option, - #[serde(rename = "move")] + #[serde(default, rename = "move", skip_serializing_if = "Option::is_none")] pub move_: Option, } diff --git a/src/imap/wizard.rs b/src/imap/wizard.rs index 7ea750e..00d6017 100644 --- a/src/imap/wizard.rs +++ b/src/imap/wizard.rs @@ -105,7 +105,7 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result { - let mut config = OAuth2Config::default(); + let mut config = OAuth2Config::new()?; let method = Select::with_theme(&*THEME) .with_prompt("IMAP OAuth 2.0 mechanism") diff --git a/src/main.rs b/src/main.rs index 59df0cb..46a9128 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,10 +131,10 @@ async fn main() -> Result<()> { return account::handlers::sync(&mut printer, sync_builder, dry_run).await; } Some(account::args::Cmd::Configure(reset)) => { - let (_, account_config) = toml_config + let (toml_account_config, _) = toml_config .clone() .into_account_configs(some_account_name, disable_cache)?; - return account::handlers::configure(&account_config, reset).await; + return account::handlers::configure(&toml_account_config, reset).await; } _ => (), }