fix wizard serialization issues

This commit is contained in:
Clément DOUIN
2024-01-12 10:16:43 +01:00
parent a15e2c0442
commit 1246be8a5b
8 changed files with 383 additions and 37 deletions
+2 -2
View File
@@ -45,10 +45,10 @@ pub struct TomlAccountConfig {
pub flag: Option<FlagConfig>,
pub message: Option<MessageConfig>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "imap")]
pub imap: Option<ImapConfig>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "notmuch")]
pub notmuch: Option<NotmuchConfig>,
#[cfg(feature = "smtp")]
+359 -10
View File
@@ -94,16 +94,21 @@ pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
let path = expand::path(path);
println!("Writing the configuration to {path:?}");
let toml = pretty_serialize(&config)?;
fs::create_dir_all(path.parent().unwrap_or(&path))?;
fs::write(path, toml)?;
let mut doc = toml::to_string(&config)?.parse::<Document>()?;
println!("Exiting the wizard…");
Ok(config)
}
fn pretty_serialize(config: &TomlConfig) -> Result<String> {
let mut doc: Document = 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");
if let Some(item) = get_table_mut(item, "folder") {
set_tables_dotted(item, ["add", "list", "expunge", "purge", "delete"]);
set_tables_dotted(item, ["alias", "add", "list", "expunge", "purge", "delete"]);
}
set_table_dotted(item, "envelope");
@@ -124,7 +129,9 @@ pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
);
}
#[cfg(feature = "maildir")]
set_table_dotted(item, "maildir");
#[cfg(feature = "imap")]
{
set_table_dotted(item, "imap");
@@ -132,9 +139,10 @@ pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
set_tables_dotted(item, ["passwd", "oauth2"]);
}
}
#[cfg(feature = "notmuch")]
set_table_dotted(item, "notmuch");
set_table_dotted(item, "sendmail");
#[cfg(feature = "smtp")]
{
set_table_dotted(item, "smtp");
@@ -143,14 +151,22 @@ pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
}
}
#[cfg(feature = "sendmail")]
set_table_dotted(item, "sendmail");
#[cfg(feature = "account-sync")]
{
set_table_dotted(item, "sync");
if let Some(item) = get_table_mut(item, "sync") {
set_tables_dotted(item, ["strategy"]);
}
}
#[cfg(feature = "pgp")]
set_table_dotted(item, "pgp");
});
fs::create_dir_all(path.parent().unwrap_or(&path))?;
fs::write(path, doc.to_string())?;
Ok(config)
Ok(doc.to_string())
}
fn get_table_mut<'a>(item: &'a mut Item, key: &'a str) -> Option<&'a mut Item> {
@@ -168,3 +184,336 @@ fn set_tables_dotted<'a>(item: &'a mut Item, keys: impl IntoIterator<Item = &'a
set_table_dotted(item, key)
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
use crate::{account::config::TomlAccountConfig, config::TomlConfig};
fn assert_eq(config: TomlAccountConfig, expected_toml: &str) {
let config = TomlConfig {
accounts: HashMap::from_iter([("test".into(), config)]),
..Default::default()
};
let toml = super::pretty_serialize(&config).expect("serialize error");
assert_eq!(toml, expected_toml);
let expected_config = toml::from_str(&toml).expect("deserialize error");
assert_eq!(config, expected_config);
}
#[test]
fn pretty_serialize_default() {
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
..Default::default()
},
r#"[test]
email = "test@localhost"
"#,
)
}
#[cfg(feature = "account-sync")]
#[test]
fn pretty_serialize_sync_all() {
use email::{account::sync::config::SyncConfig, folder::sync::FolderSyncStrategy};
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
sync: Some(SyncConfig {
enable: Some(false),
dir: Some("/tmp/test".into()),
strategy: Some(FolderSyncStrategy::All),
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
sync.enable = false
sync.dir = "/tmp/test"
sync.strategy = "all"
"#,
);
}
#[cfg(feature = "account-sync")]
#[test]
fn pretty_serialize_sync_include() {
use std::collections::HashSet;
use email::{account::sync::config::SyncConfig, folder::sync::FolderSyncStrategy};
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
sync: Some(SyncConfig {
enable: Some(true),
dir: Some("/tmp/test".into()),
strategy: Some(FolderSyncStrategy::Include(HashSet::from_iter([
"test".into()
]))),
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
sync.enable = true
sync.dir = "/tmp/test"
sync.strategy.include = ["test"]
"#,
);
}
#[cfg(feature = "imap")]
#[test]
fn pretty_serialize_imap_passwd_cmd() {
use email::{
account::config::passwd::PasswdConfig,
imap::config::{ImapAuthConfig, ImapConfig},
};
use secret::Secret;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
imap: Some(ImapConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_cmd("pass show test"))),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
imap.host = "localhost"
imap.port = 143
imap.login = "test@localhost"
imap.passwd.cmd = "pass show test"
"#,
);
}
#[cfg(feature = "imap")]
#[test]
fn pretty_serialize_imap_passwd_cmds() {
use email::{
account::config::passwd::PasswdConfig,
imap::config::{ImapAuthConfig, ImapConfig},
};
use secret::Secret;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
imap: Some(ImapConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_cmd(vec![
"pass show test",
"tr -d '[:blank:]'",
]))),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
imap.host = "localhost"
imap.port = 143
imap.login = "test@localhost"
imap.passwd.cmd = ["pass show test", "tr -d '[:blank:]'"]
"#,
);
}
#[cfg(feature = "imap")]
#[test]
fn pretty_serialize_imap_oauth2() {
use email::{
account::config::oauth2::OAuth2Config,
imap::config::{ImapAuthConfig, ImapConfig},
};
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
imap: Some(ImapConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: ImapAuthConfig::OAuth2(OAuth2Config {
client_id: "client-id".into(),
auth_url: "auth-url".into(),
token_url: "token-url".into(),
..Default::default()
}),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
imap.host = "localhost"
imap.port = 143
imap.login = "test@localhost"
imap.oauth2.method = "xoauth2"
imap.oauth2.client-id = "client-id"
imap.oauth2.auth-url = "auth-url"
imap.oauth2.token-url = "token-url"
imap.oauth2.pkce = false
imap.oauth2.scopes = []
"#,
);
}
#[cfg(feature = "maildir")]
#[test]
fn pretty_serialize_maildir() {
use email::maildir::config::MaildirConfig;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
maildir: Some(MaildirConfig {
root_dir: "/tmp/test".into(),
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
maildir.root-dir = "/tmp/test"
"#,
);
}
#[cfg(feature = "smtp")]
#[test]
fn pretty_serialize_smtp_passwd_cmd() {
use email::{
account::config::passwd::PasswdConfig,
smtp::config::{SmtpAuthConfig, SmtpConfig},
};
use secret::Secret;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
smtp: Some(SmtpConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_cmd("pass show test"))),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
smtp.host = "localhost"
smtp.port = 143
smtp.login = "test@localhost"
smtp.passwd.cmd = "pass show test"
"#,
);
}
#[cfg(feature = "smtp")]
#[test]
fn pretty_serialize_smtp_passwd_cmds() {
use email::{
account::config::passwd::PasswdConfig,
smtp::config::{SmtpAuthConfig, SmtpConfig},
};
use secret::Secret;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
smtp: Some(SmtpConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_cmd(vec![
"pass show test",
"tr -d '[:blank:]'",
]))),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
smtp.host = "localhost"
smtp.port = 143
smtp.login = "test@localhost"
smtp.passwd.cmd = ["pass show test", "tr -d '[:blank:]'"]
"#,
);
}
#[cfg(feature = "smtp")]
#[test]
fn pretty_serialize_smtp_oauth2() {
use email::{
account::config::oauth2::OAuth2Config,
smtp::config::{SmtpAuthConfig, SmtpConfig},
};
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
smtp: Some(SmtpConfig {
host: "localhost".into(),
port: 143,
login: "test@localhost".into(),
auth: SmtpAuthConfig::OAuth2(OAuth2Config {
client_id: "client-id".into(),
auth_url: "auth-url".into(),
token_url: "token-url".into(),
..Default::default()
}),
..Default::default()
}),
..Default::default()
},
r#"[test]
email = "test@localhost"
smtp.host = "localhost"
smtp.port = 143
smtp.login = "test@localhost"
smtp.oauth2.method = "xoauth2"
smtp.oauth2.client-id = "client-id"
smtp.oauth2.auth-url = "auth-url"
smtp.oauth2.token-url = "token-url"
smtp.oauth2.pkce = false
smtp.oauth2.scopes = []
"#,
);
}
#[cfg(feature = "pgp")]
#[test]
fn pretty_serialize_pgp_cmds() {
use email::account::config::pgp::PgpConfig;
assert_eq(
TomlAccountConfig {
email: "test@localhost".into(),
pgp: Some(PgpConfig::Cmds(Default::default())),
..Default::default()
},
r#"[test]
email = "test@localhost"
pgp.backend = "cmds"
"#,
);
}
}
+7 -5
View File
@@ -135,6 +135,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
let auth = if oauth2_enabled {
let mut config = OAuth2Config::default();
let redirect_host = OAuth2Config::LOCALHOST.to_owned();
let redirect_port = OAuth2Config::get_first_available_port()?;
let method_idx = Select::with_theme(&*THEME)
.with_prompt("IMAP OAuth 2.0 mechanism")
@@ -245,13 +247,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
config.auth_url.clone(),
config.token_url.clone(),
)?
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port)
.with_redirect_host(redirect_host.to_owned())
.with_redirect_port(redirect_port)
.build()?;
let mut auth_code_grant = AuthorizationCodeGrant::new()
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port);
.with_redirect_host(redirect_host.to_owned())
.with_redirect_port(redirect_port);
if config.pkce {
auth_code_grant = auth_code_grant.with_pkce();
@@ -312,7 +314,7 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
_ => Default::default(),
};
ImapAuthConfig::Passwd(PasswdConfig { passwd: secret })
ImapAuthConfig::Passwd(PasswdConfig(secret))
};
let config = ImapConfig {
+7 -5
View File
@@ -135,6 +135,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
let auth = if oauth2_enabled {
let mut config = OAuth2Config::default();
let redirect_host = OAuth2Config::LOCALHOST;
let redirect_port = OAuth2Config::get_first_available_port()?;
let method_idx = Select::with_theme(&*THEME)
.with_prompt("SMTP OAuth 2.0 mechanism")
@@ -245,13 +247,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
config.auth_url.clone(),
config.token_url.clone(),
)?
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port)
.with_redirect_host(redirect_host.to_owned())
.with_redirect_port(redirect_port)
.build()?;
let mut auth_code_grant = AuthorizationCodeGrant::new()
.with_redirect_host(config.redirect_host.clone())
.with_redirect_port(config.redirect_port);
.with_redirect_host(redirect_host.to_owned())
.with_redirect_port(redirect_port);
if config.pkce {
auth_code_grant = auth_code_grant.with_pkce();
@@ -312,7 +314,7 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
_ => Default::default(),
};
SmtpAuthConfig::Passwd(PasswdConfig { passwd: secret })
SmtpAuthConfig::Passwd(PasswdConfig(secret))
};
let config = SmtpConfig {