fix config and oauth2

This commit is contained in:
Clément DOUIN 2023-12-04 16:25:56 +01:00
parent c54ada730b
commit ea9c28b9d7
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
9 changed files with 453 additions and 177 deletions

View file

@ -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<Option<BackendConfig>> {
@ -52,7 +52,7 @@ pub(crate) async fn configure_sender(
email: &str,
) -> Result<Option<BackendConfig>> {
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()?

View file

@ -44,11 +44,19 @@ pub struct TomlConfig {
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "OptionEmailTextPlainFormatDef")]
#[serde(
default,
with = "OptionEmailTextPlainFormatDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_writing_headers: Option<Vec<String>>,
pub email_sending_save_copy: Option<bool>,
#[serde(default, with = "OptionEmailHooksDef")]
#[serde(
default,
with = "OptionEmailHooksDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>,
#[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.

View file

@ -61,27 +61,38 @@ pub enum CmdDef {
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Option<Cmd>", from = "OptionCmd")]
#[serde(remote = "Option<Cmd>", 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<OptionCmd> for Option<Cmd> {
fn from(cmd: OptionCmd) -> Option<Cmd> {
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<OptionCmd> for Option<Cmd> {
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<ImapConfig>", from = "OptionImapConfig")]
#[serde(
remote = "Option<ImapConfig>",
from = "OptionImapConfig",
into = "OptionImapConfig"
)]
pub struct OptionImapConfigDef;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
@ -129,6 +144,21 @@ impl From<OptionImapConfig> for Option<ImapConfig> {
}
}
impl Into<OptionImapConfig> for Option<ImapConfig> {
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<MaildirConfig>", from = "OptionMaildirConfig")]
#[serde(
remote = "Option<MaildirConfig>",
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<OptionMaildirConfig> for Option<MaildirConfig> {
fn from(config: OptionMaildirConfig) -> Option<MaildirConfig> {
match config {
OptionMaildirConfig::None => None,
OptionMaildirConfig::Some(config) => Some(config),
if config.is_none {
None
} else {
Some(config.inner)
}
}
}
impl Into<OptionMaildirConfig> for Option<MaildirConfig> {
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<NotmuchConfig>", from = "OptionNotmuchConfig")]
#[serde(
remote = "Option<NotmuchConfig>",
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<OptionNotmuchConfig> for Option<NotmuchConfig> {
fn from(config: OptionNotmuchConfig) -> Option<NotmuchConfig> {
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<OptionNotmuchConfig> for Option<NotmuchConfig> {
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<EmailTextPlainFormat>",
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<OptionEmailTextPlainFormat> for Option<EmailTextPlainFormat> {
fn from(fmt: OptionEmailTextPlainFormat) -> Option<EmailTextPlainFormat> {
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<OptionEmailTextPlainFormat> for Option<EmailTextPlainFormat> {
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<SmtpConfig>", from = "OptionSmtpConfig")]
#[serde(
remote = "Option<SmtpConfig>",
from = "OptionSmtpConfig",
into = "OptionSmtpConfig"
)]
pub struct OptionSmtpConfigDef;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
@ -352,6 +437,21 @@ impl From<OptionSmtpConfig> for Option<SmtpConfig> {
}
}
impl Into<OptionSmtpConfig> for Option<SmtpConfig> {
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<SendmailConfig>", from = "OptionSendmailConfig")]
#[serde(
remote = "Option<SendmailConfig>",
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<OptionSendmailConfig> for Option<SendmailConfig> {
fn from(config: OptionSendmailConfig) -> Option<SendmailConfig> {
match config {
OptionSendmailConfig::None => None,
OptionSendmailConfig::Some(config) => Some(config),
if config.is_none {
None
} else {
Some(config.inner)
}
}
}
impl Into<OptionSendmailConfig> for Option<SendmailConfig> {
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<EmailHooks>", from = "OptionEmailHooks")]
#[serde(
remote = "Option<EmailHooks>",
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<OptionEmailHooks> for Option<EmailHooks> {
fn from(fmt: OptionEmailHooks) -> Option<EmailHooks> {
match fmt {
OptionEmailHooks::None => None,
OptionEmailHooks::Some(hooks) => Some(hooks),
fn from(hooks: OptionEmailHooks) -> Option<EmailHooks> {
if hooks.is_none {
None
} else {
Some(hooks.inner)
}
}
}
impl Into<OptionEmailHooks> for Option<EmailHooks> {
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<FolderSyncStrategy>",
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<OptionFolderSyncStrategy> for Option<FolderSyncStrategy> {
fn from(config: OptionFolderSyncStrategy) -> Option<FolderSyncStrategy> {
match config {
OptionFolderSyncStrategy::None => None,
OptionFolderSyncStrategy::Some(config) => Some(config),
fn from(option: OptionFolderSyncStrategy) -> Option<FolderSyncStrategy> {
if option.is_some {
Some(option.inner)
} else {
None
}
}
}
impl Into<OptionFolderSyncStrategy> for Option<FolderSyncStrategy> {
fn into(self) -> OptionFolderSyncStrategy {
match self {
Some(strategy) => OptionFolderSyncStrategy {
is_some: true,
inner: strategy,
},
None => OptionFolderSyncStrategy {
is_some: false,
inner: Default::default(),
},
}
}
}

View file

@ -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<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
pub(crate) async fn configure() -> Result<TomlConfig> {
pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
wizard_log!("Configuring your first account:");
let mut config = TomlConfig::default();
@ -89,25 +92,86 @@ pub(crate) async fn configure() -> Result<TomlConfig> {
.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::<Document>()?;
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<Item = &'a str>) {
for key in keys {
set_table_dotted(item, key)
}
}
pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {
Password::with_theme(&*THEME)
.with_prompt(prompt)

View file

@ -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<bool>,
@ -48,16 +48,28 @@ pub struct TomlAccountConfig {
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "OptionEmailTextPlainFormatDef")]
#[serde(
default,
with = "OptionEmailTextPlainFormatDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_writing_headers: Option<Vec<String>>,
pub email_sending_save_copy: Option<bool>,
#[serde(default, with = "OptionEmailHooksDef")]
#[serde(
default,
with = "OptionEmailHooksDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>,
pub sync: Option<bool>,
pub sync_dir: Option<PathBuf>,
#[serde(default, with = "OptionFolderSyncStrategyDef")]
#[serde(
default,
with = "OptionFolderSyncStrategyDef",
skip_serializing_if = "Option::is_none"
)]
pub sync_folders_strategy: Option<FolderSyncStrategy>,
pub backend: Option<BackendKind>,
@ -68,25 +80,49 @@ pub struct TomlAccountConfig {
pub message: Option<MessageConfig>,
#[cfg(feature = "imap-backend")]
#[serde(default, with = "OptionImapConfigDef")]
#[serde(
default,
with = "OptionImapConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub imap: Option<ImapConfig>,
#[serde(default, with = "OptionMaildirConfigDef")]
#[serde(
default,
with = "OptionMaildirConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "notmuch-backend")]
#[serde(default, with = "OptionNotmuchConfigDef")]
#[serde(
default,
with = "OptionNotmuchConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub notmuch: Option<NotmuchConfig>,
#[cfg(feature = "smtp-sender")]
#[serde(default, with = "OptionSmtpConfigDef")]
#[serde(
default,
with = "OptionSmtpConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub smtp: Option<SmtpConfig>,
#[serde(default, with = "OptionSendmailConfigDef")]
#[serde(
default,
with = "OptionSendmailConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub sendmail: Option<SendmailConfig>,
#[cfg(feature = "pgp")]
#[serde(default, with = "OptionPgpConfigDef")]
#[serde(
default,
with = "OptionPgpConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub pgp: Option<PgpConfig>,
}

View file

@ -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<ProgressStyle> = 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<ProgressStyle> = 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!",

View file

@ -10,7 +10,7 @@ pub struct MessageConfig {
pub peek: Option<MessagePeekConfig>,
pub get: Option<MessageGetConfig>,
pub copy: Option<MessageCopyConfig>,
#[serde(rename = "move")]
#[serde(default, rename = "move", skip_serializing_if = "Option::is_none")]
pub move_: Option<MessageMoveConfig>,
}

View file

@ -105,7 +105,7 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
ImapAuthConfig::Passwd(config)
}
Some(idx) if AUTH_MECHANISMS[idx] == OAUTH2 => {
let mut config = OAuth2Config::default();
let mut config = OAuth2Config::new()?;
let method = Select::with_theme(&*THEME)
.with_prompt("IMAP OAuth 2.0 mechanism")

View file

@ -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;
}
_ => (),
}