make use of pimalaya_tui::config::TomlConfig

This commit is contained in:
Clément DOUIN 2024-09-01 13:46:56 +02:00
parent 6f5f943875
commit b92d7b4a08
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
46 changed files with 205 additions and 308 deletions

82
Cargo.lock generated
View file

@ -262,7 +262,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -297,7 +297,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -403,7 +403,7 @@ dependencies = [
"regex", "regex",
"rustc-hash", "rustc-hash",
"shlex", "shlex",
"syn 2.0.76", "syn 2.0.77",
"which", "which",
] ]
@ -483,7 +483,7 @@ checksum = "e0af050e27e5d57aa14975f97fe47a134c46a390f91819f23a625319a7111bfa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -692,7 +692,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -952,7 +952,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -1284,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f24a09fd651027f8764f8a12c12358715cb9bab622ab3125ede3dd6ae047c95" checksum = "0f24a09fd651027f8764f8a12c12358715cb9bab622ab3125ede3dd6ae047c95"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -1314,7 +1314,7 @@ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -1335,7 +1335,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -1344,15 +1344,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.9" version = "0.3.9"
@ -1603,7 +1594,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -1895,7 +1886,6 @@ dependencies = [
"dirs 4.0.0", "dirs 4.0.0",
"email-lib", "email-lib",
"email_address", "email_address",
"erased-serde",
"mail-builder", "mail-builder",
"md5", "md5",
"mml-lib", "mml-lib",
@ -2245,9 +2235,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.4.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -2574,7 +2564,7 @@ dependencies = [
"smtp-proto", "smtp-proto",
"tokio", "tokio",
"tokio-rustls 0.26.0", "tokio-rustls 0.26.0",
"webpki-roots 0.26.3", "webpki-roots 0.26.5",
] ]
[[package]] [[package]]
@ -2858,7 +2848,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -3201,8 +3191,10 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-tui" name = "pimalaya-tui"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pimalaya/tui#80660dfaf9daafbaa716c711e510bf3cfd04cd69" source = "git+https://github.com/pimalaya/tui#0b2403886edcc57491e56da86537541e9b126cbc"
dependencies = [ dependencies = [
"clap",
"color-eyre",
"crossterm 0.25.0", "crossterm 0.25.0",
"dirs 4.0.0", "dirs 4.0.0",
"email-lib", "email-lib",
@ -3210,8 +3202,16 @@ dependencies = [
"inquire", "inquire",
"oauth-lib", "oauth-lib",
"secret-lib", "secret-lib",
"serde",
"serde-toml-merge",
"serde_json",
"shellexpand-utils", "shellexpand-utils",
"thiserror", "thiserror",
"toml",
"toml_edit 0.22.20",
"tracing",
"tracing-error",
"tracing-subscriber",
] ]
[[package]] [[package]]
@ -3231,7 +3231,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -3331,7 +3331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -3970,7 +3970,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4003,7 +4003,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4249,7 +4249,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4271,9 +4271,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.76" version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4366,7 +4366,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4420,7 +4420,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4548,7 +4548,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -4800,7 +4800,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4834,7 +4834,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4863,9 +4863,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.26.3" version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
dependencies = [ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
@ -5242,7 +5242,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]
@ -5262,7 +5262,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.76", "syn 2.0.77",
] ]
[[package]] [[package]]

View file

@ -59,14 +59,13 @@ crossterm = { version = "0.27", features = ["serde"] }
dirs = "4" dirs = "4"
email-lib = { version = "=0.25.0", default-features = false, features = ["derive", "thread", "tracing"] } email-lib = { version = "=0.25.0", default-features = false, features = ["derive", "thread", "tracing"] }
email_address = { version = "0.2", optional = true } email_address = { version = "0.2", optional = true }
erased-serde = "0.3"
mail-builder = "0.3" mail-builder = "0.3"
md5 = "0.7" md5 = "0.7"
mml-lib = { version = "=1.0.14", default-features = false, features = ["derive"] } mml-lib = { version = "=1.0.14", default-features = false, features = ["derive"] }
oauth-lib = { version = "=0.1.1", optional = true } oauth-lib = { version = "=0.1.1", optional = true }
once_cell = "1.16" once_cell = "1.16"
petgraph = "0.6" petgraph = "0.6"
pimalaya-tui = { version = "=0.1.0", default-features = false, features = ["email", "path"] } pimalaya-tui = { version = "=0.1.0", default-features = false, features = ["email", "path", "cli", "config", "tracing"] }
process-lib = { version = "=0.4.2", features = ["derive"] } process-lib = { version = "=0.4.2", features = ["derive"] }
secret-lib = { version = "=0.4.6", default-features = false, features = ["command", "derive"], optional = true } secret-lib = { version = "=0.4.6", default-features = false, features = ["command", "derive"], optional = true }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }

View file

@ -4,7 +4,7 @@ use email::backend::context::BackendContextBuilder;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::OptionalAccountNameArg, backend, config::TomlConfig, printer::Printer, account::arg::name::OptionalAccountNameArg, backend, config::Config, printer::Printer,
}; };
/// Check up the given account. /// Check up the given account.
@ -19,7 +19,7 @@ pub struct AccountCheckUpCommand {
} }
impl AccountCheckUpCommand { impl AccountCheckUpCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing check up account command"); info!("executing check up account command");
let account = self.account.name.as_ref().map(String::as_str); let account = self.account.name.as_ref().map(String::as_str);

View file

@ -10,7 +10,7 @@ use tracing::info;
#[cfg(any(feature = "imap", feature = "smtp"))] #[cfg(any(feature = "imap", feature = "smtp"))]
use tracing::{debug, warn}; use tracing::{debug, warn};
use crate::{account::arg::name::AccountNameArg, config::TomlConfig, printer::Printer}; use crate::{account::arg::name::AccountNameArg, config::Config, printer::Printer};
/// Configure an account. /// Configure an account.
/// ///
@ -31,7 +31,7 @@ pub struct AccountConfigureCommand {
} }
impl AccountConfigureCommand { impl AccountConfigureCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing configure account command"); info!("executing configure account command");
let account = &self.account.name; let account = &self.account.name;

View file

@ -4,7 +4,7 @@ use tracing::info;
use crate::{ use crate::{
account::{Accounts, AccountsTable}, account::{Accounts, AccountsTable},
config::TomlConfig, config::Config,
printer::Printer, printer::Printer,
}; };
@ -24,7 +24,7 @@ pub struct AccountListCommand {
} }
impl AccountListCommand { impl AccountListCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing list accounts command"); info!("executing list accounts command");
let accounts = Accounts::from(config.accounts.iter()); let accounts = Accounts::from(config.accounts.iter());

View file

@ -5,7 +5,7 @@ mod list;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result; use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{ use self::{
check_up::AccountCheckUpCommand, configure::AccountConfigureCommand, list::AccountListCommand, check_up::AccountCheckUpCommand, configure::AccountConfigureCommand, list::AccountListCommand,
@ -30,7 +30,7 @@ pub enum AccountSubcommand {
impl AccountSubcommand { impl AccountSubcommand {
#[allow(unused)] #[allow(unused)]
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::CheckUp(cmd) => cmd.execute(printer, config).await, Self::CheckUp(cmd) => cmd.execute(printer, config).await,
Self::Configure(cmd) => cmd.execute(printer, config).await, Self::Configure(cmd) => cmd.execute(printer, config).await,

View file

@ -32,12 +32,11 @@ pub async fn configure(
let config = wizard::maildir::start(account_name)?; let config = wizard::maildir::start(account_name)?;
Ok(BackendConfig::Maildir(config)) Ok(BackendConfig::Maildir(config))
} }
// TODO #[cfg(feature = "notmuch")]
// #[cfg(feature = "notmuch")] BackendKind::Notmuch => {
// BackendKind::Notmuch => { let config = wizard::notmuch::start()?;
// let config = wizard::notmuch::start()?; Ok(BackendConfig::Notmuch(config))
// Ok(BackendConfig::Notmuch(config)) }
// }
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -5,7 +5,7 @@ use std::path::PathBuf;
use crate::{ use crate::{
account::command::AccountSubcommand, account::command::AccountSubcommand,
completion::command::CompletionGenerateCommand, completion::command::CompletionGenerateCommand,
config::{self, TomlConfig}, config::{self, Config},
envelope::command::EnvelopeSubcommand, envelope::command::EnvelopeSubcommand,
flag::command::FlagSubcommand, flag::command::FlagSubcommand,
folder::command::FolderSubcommand, folder::command::FolderSubcommand,
@ -111,31 +111,31 @@ impl HimalayaCommand {
pub async fn execute(self, printer: &mut impl Printer, config_paths: &[PathBuf]) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config_paths: &[PathBuf]) -> Result<()> {
match self { match self {
Self::Account(cmd) => { Self::Account(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Folder(cmd) => { Self::Folder(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Envelope(cmd) => { Self::Envelope(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Flag(cmd) => { Self::Flag(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Message(cmd) => { Self::Message(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Attachment(cmd) => { Self::Attachment(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Template(cmd) => { Self::Template(cmd) => {
let config = TomlConfig::from_paths_or_default(config_paths).await?; let config = Config::from_paths_or_default(config_paths).await?;
cmd.execute(printer, &config).await cmd.execute(printer, &config).await
} }
Self::Manual(cmd) => cmd.execute(printer).await, Self::Manual(cmd) => cmd.execute(printer).await,

View file

@ -1,32 +1,24 @@
#[cfg(feature = "wizard")] #[cfg(feature = "wizard")]
pub mod wizard; pub mod wizard;
use std::{collections::HashMap, fs, path::PathBuf, sync::Arc}; use std::{collections::HashMap, path::PathBuf, sync::Arc};
use color_eyre::{ use color_eyre::{eyre::eyre, Result};
eyre::{bail, eyre, Context},
Result,
};
use crossterm::style::Color; use crossterm::style::Color;
use dirs::{config_dir, home_dir};
use email::{ use email::{
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig, account::config::AccountConfig, envelope::config::EnvelopeConfig, folder::config::FolderConfig,
folder::config::FolderConfig, message::config::MessageConfig, message::config::MessageConfig,
}; };
#[cfg(feature = "wizard")] use pimalaya_tui::config::TomlConfig;
use pimalaya_tui::{print, prompt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_toml_merge::merge;
use shellexpand_utils::{canonicalize, expand}; use shellexpand_utils::{canonicalize, expand};
use toml::{self, Value};
use tracing::debug;
use crate::account::config::{ListAccountsTableConfig, TomlAccountConfig}; use crate::account::config::{ListAccountsTableConfig, TomlAccountConfig};
/// Represents the user config file. /// Represents the user config file.
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)] #[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct TomlConfig { pub struct Config {
#[serde(alias = "name")] #[serde(alias = "name")]
pub display_name: Option<String>, pub display_name: Option<String>,
pub signature: Option<String>, pub signature: Option<String>,
@ -36,83 +28,9 @@ pub struct TomlConfig {
pub account: Option<AccountsConfig>, pub account: Option<AccountsConfig>,
} }
impl TomlConfig { impl TomlConfig<AccountConfig> for Config {}
pub fn account_list_table_preset(&self) -> Option<String> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.preset.clone())
}
pub fn account_list_table_name_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.name_color)
}
pub fn account_list_table_backends_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.backends_color)
}
pub fn account_list_table_default_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.default_color)
}
/// Read and parse the TOML configuration at the given paths.
///
/// Returns an error if a configuration file cannot be read or if
/// a content cannot be parsed.
fn from_paths(paths: &[PathBuf]) -> Result<Self> {
match paths.len() {
0 => {
// should never happen
bail!("cannot read config file from empty paths");
}
1 => {
let path = &paths[0];
let content = &(fs::read_to_string(path)
.context(format!("cannot read config file at {path:?}"))?);
toml::from_str(content).context(format!("cannot parse config file at {path:?}"))
}
_ => {
let path = &paths[0];
let mut merged_content = fs::read_to_string(path)
.context(format!("cannot read config file at {path:?}"))?
.parse::<Value>()?;
for path in &paths[1..] {
match fs::read_to_string(path) {
Ok(content) => {
merged_content = merge(merged_content, content.parse()?).unwrap();
}
Err(err) => {
debug!("skipping subconfig file at {path:?}: {err}");
continue;
}
}
}
merged_content
.try_into()
.context(format!("cannot parse merged config file at {path:?}"))
}
}
}
impl Config {
/// Create and save a TOML configuration using the wizard. /// Create and save a TOML configuration using the wizard.
/// ///
/// If the user accepts the confirmation, the wizard starts and /// If the user accepts the confirmation, the wizard starts and
@ -122,25 +40,18 @@ impl TomlConfig {
/// NOTE: the wizard can only be used with interactive shells. /// NOTE: the wizard can only be used with interactive shells.
#[cfg(feature = "wizard")] #[cfg(feature = "wizard")]
async fn from_wizard(path: &PathBuf) -> Result<Self> { async fn from_wizard(path: &PathBuf) -> Result<Self> {
print::warn(format!("Cannot find existing configuration at {path:?}.")); Self::confirm_from_wizard(path)?;
wizard::configure(path).await
if !prompt::bool("Would you like to create one with the wizard? ", true)? {
std::process::exit(0);
}
return wizard::configure(path).await;
}
#[cfg(not(feature = "wizard"))]
async fn from_wizard(path: &PathBuf) -> Result<Self> {
bail!("Cannot find existing configuration at {path:?}.");
} }
/// Read and parse the TOML configuration from default paths. /// Read and parse the TOML configuration from default paths.
pub async fn from_default_paths() -> Result<Self> { pub async fn from_default_paths() -> Result<Self> {
match Self::first_valid_default_path() { match Self::first_valid_default_path() {
Some(path) => Self::from_paths(&[path]), Some(path) => Self::from_paths(&[path]),
#[cfg(feature = "wizard")]
None => Self::from_wizard(&Self::default_path()?).await, None => Self::from_wizard(&Self::default_path()?).await,
#[cfg(not(feature = "wizard"))]
None => color_eyre::eyre::bail!("cannot find config file from default paths"),
} }
} }
@ -164,36 +75,6 @@ impl TomlConfig {
} }
} }
/// Get the default configuration path.
///
/// Returns an error if the XDG configuration directory cannot be
/// found.
pub fn default_path() -> Result<PathBuf> {
Ok(config_dir()
.ok_or(eyre!("cannot get XDG config directory"))?
.join("himalaya")
.join("config.toml"))
}
/// Get the first default configuration path that points to a
/// valid file.
///
/// Tries paths in this order:
///
/// - `$XDG_CONFIG_DIR/himalaya/config.toml` (or equivalent to
/// `$XDG_CONFIG_DIR` in other OSes.)
/// - `$HOME/.config/himalaya/config.toml`
/// - `$HOME/.himalayarc`
pub fn first_valid_default_path() -> Option<PathBuf> {
Self::default_path()
.ok()
.filter(|p| p.exists())
.or_else(|| home_dir().map(|p| p.join(".config").join("himalaya").join("config.toml")))
.filter(|p| p.exists())
.or_else(|| home_dir().map(|p| p.join(".himalayarc")))
.filter(|p| p.exists())
}
pub fn into_toml_account_config( pub fn into_toml_account_config(
&self, &self,
account_name: Option<&str>, account_name: Option<&str>,
@ -241,7 +122,7 @@ impl TomlConfig {
) -> Result<(Arc<TomlAccountConfig>, Arc<AccountConfig>)> { ) -> Result<(Arc<TomlAccountConfig>, Arc<AccountConfig>)> {
let (account_name, toml_account_config) = self.into_toml_account_config(account_name)?; let (account_name, toml_account_config) = self.into_toml_account_config(account_name)?;
let config = Config { let config = email::config::Config {
display_name: self.display_name, display_name: self.display_name,
signature: self.signature, signature: self.signature,
signature_delim: self.signature_delim, signature_delim: self.signature_delim,
@ -286,6 +167,38 @@ impl TomlConfig {
Ok((Arc::new(toml_account_config), Arc::new(account_config))) Ok((Arc::new(toml_account_config), Arc::new(account_config)))
} }
pub fn account_list_table_preset(&self) -> Option<String> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.preset.clone())
}
pub fn account_list_table_name_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.name_color)
}
pub fn account_list_table_backends_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.backends_color)
}
pub fn account_list_table_default_color(&self) -> Option<Color> {
self.account
.as_ref()
.and_then(|account| account.list.as_ref())
.and_then(|list| list.table.as_ref())
.and_then(|table| table.default_color)
}
} }
/// Parse a configuration file path as [`PathBuf`]. /// Parse a configuration file path as [`PathBuf`].

View file

@ -6,12 +6,12 @@ use toml_edit::{DocumentMut, Table};
use crate::account; use crate::account;
use super::TomlConfig; use super::Config;
pub async fn configure(path: &PathBuf) -> Result<TomlConfig> { pub async fn configure(path: &PathBuf) -> Result<Config> {
print::section("Configuring your default account"); print::section("Configuring your default account");
let mut config = TomlConfig::default(); let mut config = Config::default();
let (account_name, account_config) = account::wizard::configure().await?; let (account_name, account_config) = account::wizard::configure().await?;
config.accounts.insert(account_name, account_config); config.accounts.insert(account_name, account_config);
@ -27,7 +27,7 @@ pub async fn configure(path: &PathBuf) -> Result<TomlConfig> {
Ok(config) Ok(config)
} }
fn pretty_serialize(config: &TomlConfig) -> Result<String> { fn pretty_serialize(config: &Config) -> Result<String> {
let mut doc: DocumentMut = toml::to_string(&config)?.parse()?; let mut doc: DocumentMut = toml::to_string(&config)?.parse()?;
doc.iter_mut().for_each(|(_, item)| { doc.iter_mut().for_each(|(_, item)| {

View file

@ -9,7 +9,7 @@ use std::process::exit;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
envelope::EnvelopesTable, folder::arg::name::FolderNameOptionalFlag, printer::Printer, envelope::EnvelopesTable, folder::arg::name::FolderNameOptionalFlag, printer::Printer,
}; };
@ -132,7 +132,7 @@ impl Default for ListEnvelopesCommand {
} }
impl ListEnvelopesCommand { impl ListEnvelopesCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing list envelopes command"); info!("executing list envelopes command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -4,7 +4,7 @@ pub mod thread;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result; use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{list::ListEnvelopesCommand, thread::ThreadEnvelopesCommand}; use self::{list::ListEnvelopesCommand, thread::ThreadEnvelopesCommand};
@ -25,7 +25,7 @@ pub enum EnvelopeSubcommand {
impl EnvelopeSubcommand { impl EnvelopeSubcommand {
#[allow(unused)] #[allow(unused)]
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::List(cmd) => cmd.execute(printer, config).await, Self::List(cmd) => cmd.execute(printer, config).await,
Self::Thread(cmd) => cmd.execute(printer, config).await, Self::Thread(cmd) => cmd.execute(printer, config).await,

View file

@ -9,8 +9,8 @@ use std::process::exit;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config, envelope::EnvelopesTree,
envelope::EnvelopesTree, folder::arg::name::FolderNameOptionalFlag, printer::Printer, folder::arg::name::FolderNameOptionalFlag, printer::Printer,
}; };
/// Thread all envelopes. /// Thread all envelopes.
@ -34,7 +34,7 @@ pub struct ThreadEnvelopesCommand {
} }
impl ThreadEnvelopesCommand { impl ThreadEnvelopesCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing thread envelopes command"); info!("executing thread envelopes command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
@ -29,7 +29,7 @@ pub struct FlagAddCommand {
} }
impl FlagAddCommand { impl FlagAddCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing add flag(s) command"); info!("executing add flag(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -2,10 +2,10 @@ mod add;
mod remove; mod remove;
mod set; mod set;
use color_eyre::Result;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{add::FlagAddCommand, remove::FlagRemoveCommand, set::FlagSetCommand}; use self::{add::FlagAddCommand, remove::FlagRemoveCommand, set::FlagSetCommand};
@ -32,7 +32,7 @@ pub enum FlagSubcommand {
impl FlagSubcommand { impl FlagSubcommand {
#[allow(unused)] #[allow(unused)]
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::Add(cmd) => cmd.execute(printer, config).await, Self::Add(cmd) => cmd.execute(printer, config).await,
Self::Set(cmd) => cmd.execute(printer, config).await, Self::Set(cmd) => cmd.execute(printer, config).await,

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
@ -29,7 +29,7 @@ pub struct FlagRemoveCommand {
} }
impl FlagRemoveCommand { impl FlagRemoveCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing remove flag(s) command"); info!("executing remove flag(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
@ -29,7 +29,7 @@ pub struct FlagSetCommand {
} }
impl FlagSetCommand { impl FlagSetCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing set flag(s) command"); info!("executing set flag(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
}; };
@ -28,7 +28,7 @@ pub struct AttachmentDownloadCommand {
} }
impl AttachmentDownloadCommand { impl AttachmentDownloadCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing download attachment(s) command"); info!("executing download attachment(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -1,9 +1,9 @@
mod download; mod download;
use color_eyre::Result;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::download::AttachmentDownloadCommand; use self::download::AttachmentDownloadCommand;
@ -19,7 +19,7 @@ pub enum AttachmentSubcommand {
} }
impl AttachmentSubcommand { impl AttachmentSubcommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::Download(cmd) => cmd.execute(printer, config).await, Self::Download(cmd) => cmd.execute(printer, config).await,
} }

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, envelope::arg::ids::EnvelopeIdsArgs,
folder::arg::name::{SourceFolderNameOptionalFlag, TargetFolderNameArg}, folder::arg::name::{SourceFolderNameOptionalFlag, TargetFolderNameArg},
printer::Printer, printer::Printer,
@ -29,7 +29,7 @@ pub struct MessageCopyCommand {
} }
impl MessageCopyCommand { impl MessageCopyCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing copy message(s) command"); info!("executing copy message(s) command");
let source = &self.source_folder.name; let source = &self.source_folder.name;

View file

@ -4,7 +4,7 @@ use email::backend::feature::BackendFeatureSource;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
}; };
@ -28,7 +28,7 @@ pub struct MessageDeleteCommand {
} }
impl MessageDeleteCommand { impl MessageDeleteCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing delete message(s) command"); info!("executing delete message(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdArg, envelope::arg::ids::EnvelopeIdArg,
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs},
@ -39,7 +39,7 @@ pub struct MessageForwardCommand {
} }
impl MessageForwardCommand { impl MessageForwardCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing forward message command"); info!("executing forward message command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use url::Url; use url::Url;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, printer::Printer, account::arg::name::AccountNameFlag, backend::Backend, config::Config, printer::Printer,
ui::editor, ui::editor,
}; };
@ -34,7 +34,7 @@ impl MessageMailtoCommand {
}) })
} }
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing mailto message command"); info!("executing mailto message command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -13,7 +13,7 @@ pub mod write;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result; use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{ use self::{
copy::MessageCopyCommand, delete::MessageDeleteCommand, forward::MessageForwardCommand, copy::MessageCopyCommand, delete::MessageDeleteCommand, forward::MessageForwardCommand,
@ -67,7 +67,7 @@ pub enum MessageSubcommand {
impl MessageSubcommand { impl MessageSubcommand {
#[allow(unused)] #[allow(unused)]
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::Read(cmd) => cmd.execute(printer, config).await, Self::Read(cmd) => cmd.execute(printer, config).await,
Self::Thread(cmd) => cmd.execute(printer, config).await, Self::Thread(cmd) => cmd.execute(printer, config).await,

View file

@ -7,7 +7,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, envelope::arg::ids::EnvelopeIdsArgs,
folder::arg::name::{SourceFolderNameOptionalFlag, TargetFolderNameArg}, folder::arg::name::{SourceFolderNameOptionalFlag, TargetFolderNameArg},
printer::Printer, printer::Printer,
@ -30,7 +30,7 @@ pub struct MessageMoveCommand {
} }
impl MessageMoveCommand { impl MessageMoveCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing move message(s) command"); info!("executing move message(s) command");
let source = &self.source_folder.name; let source = &self.source_folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
#[allow(unused)] #[allow(unused)]
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
}; };
@ -73,7 +73,7 @@ pub struct MessageReadCommand {
} }
impl MessageReadCommand { impl MessageReadCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing read message(s) command"); info!("executing read message(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdArg, envelope::arg::ids::EnvelopeIdArg,
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg},
@ -42,7 +42,7 @@ pub struct MessageReplyCommand {
} }
impl MessageReplyCommand { impl MessageReplyCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing reply message command"); info!("executing reply message command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
#[allow(unused)] #[allow(unused)]
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
folder::arg::name::FolderNameOptionalFlag, message::arg::MessageRawArg, printer::Printer, folder::arg::name::FolderNameOptionalFlag, message::arg::MessageRawArg, printer::Printer,
}; };
@ -26,7 +26,7 @@ pub struct MessageSaveCommand {
} }
impl MessageSaveCommand { impl MessageSaveCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing save message command"); info!("executing save message command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -5,7 +5,7 @@ use std::io::{self, BufRead, IsTerminal};
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
message::arg::MessageRawArg, printer::Printer, message::arg::MessageRawArg, printer::Printer,
}; };
@ -23,7 +23,7 @@ pub struct MessageSendCommand {
} }
impl MessageSendCommand { impl MessageSendCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing send message command"); info!("executing send message command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -7,7 +7,7 @@ use tracing::info;
use crate::envelope::arg::ids::EnvelopeIdArg; use crate::envelope::arg::ids::EnvelopeIdArg;
#[allow(unused)] #[allow(unused)]
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
}; };
@ -74,7 +74,7 @@ pub struct MessageThreadCommand {
} }
impl MessageThreadCommand { impl MessageThreadCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing thread message(s) command"); info!("executing thread message(s) command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs},
printer::Printer, printer::Printer,
ui::editor, ui::editor,
@ -31,7 +31,7 @@ pub struct MessageWriteCommand {
} }
impl MessageWriteCommand { impl MessageWriteCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing write message command"); info!("executing write message command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdArg, envelope::arg::ids::EnvelopeIdArg,
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs},
@ -37,7 +37,7 @@ pub struct TemplateForwardCommand {
} }
impl TemplateForwardCommand { impl TemplateForwardCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing forward template command"); info!("executing forward template command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -4,10 +4,10 @@ mod save;
mod send; mod send;
mod write; mod write;
use color_eyre::Result;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{ use self::{
forward::TemplateForwardCommand, reply::TemplateReplyCommand, save::TemplateSaveCommand, forward::TemplateForwardCommand, reply::TemplateReplyCommand, save::TemplateSaveCommand,
@ -43,7 +43,7 @@ pub enum TemplateSubcommand {
} }
impl TemplateSubcommand { impl TemplateSubcommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::Write(cmd) => cmd.execute(printer, config).await, Self::Write(cmd) => cmd.execute(printer, config).await,
Self::Reply(cmd) => cmd.execute(printer, config).await, Self::Reply(cmd) => cmd.execute(printer, config).await,

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
envelope::arg::ids::EnvelopeIdArg, envelope::arg::ids::EnvelopeIdArg,
folder::arg::name::FolderNameOptionalFlag, folder::arg::name::FolderNameOptionalFlag,
message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg},
@ -41,7 +41,7 @@ pub struct TemplateReplyCommand {
} }
impl TemplateReplyCommand { impl TemplateReplyCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing reply template command"); info!("executing reply template command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use std::io::{self, BufRead, IsTerminal};
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
email::template::arg::TemplateRawArg, folder::arg::name::FolderNameOptionalFlag, email::template::arg::TemplateRawArg, folder::arg::name::FolderNameOptionalFlag,
printer::Printer, printer::Printer,
}; };
@ -30,7 +30,7 @@ pub struct TemplateSaveCommand {
} }
impl TemplateSaveCommand { impl TemplateSaveCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing save template command"); info!("executing save template command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use std::io::{self, BufRead, IsTerminal};
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
email::template::arg::TemplateRawArg, printer::Printer, email::template::arg::TemplateRawArg, printer::Printer,
}; };
@ -26,7 +26,7 @@ pub struct TemplateSendCommand {
} }
impl TemplateSendCommand { impl TemplateSendCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing send template command"); info!("executing send template command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -4,7 +4,7 @@ use email::message::Message;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, config::TomlConfig, account::arg::name::AccountNameFlag, config::Config,
email::template::arg::body::TemplateRawBodyArg, message::arg::header::HeaderRawArgs, email::template::arg::body::TemplateRawBodyArg, message::arg::header::HeaderRawArgs,
printer::Printer, printer::Printer,
}; };
@ -26,7 +26,7 @@ pub struct TemplateWriteCommand {
} }
impl TemplateWriteCommand { impl TemplateWriteCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing write template command"); info!("executing write template command");
let (_, account_config) = config let (_, account_config) = config

View file

@ -4,7 +4,7 @@ use email::{backend::feature::BackendFeatureSource, folder::add::AddFolder};
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
folder::arg::name::FolderNameArg, printer::Printer, folder::arg::name::FolderNameArg, printer::Printer,
}; };
@ -22,7 +22,7 @@ pub struct AddFolderCommand {
} }
impl AddFolderCommand { impl AddFolderCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing create folder command"); info!("executing create folder command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -7,7 +7,7 @@ use pimalaya_tui::prompt;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
folder::arg::name::FolderNameArg, printer::Printer, folder::arg::name::FolderNameArg, printer::Printer,
}; };
@ -25,7 +25,7 @@ pub struct FolderDeleteCommand {
} }
impl FolderDeleteCommand { impl FolderDeleteCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing delete folder command"); info!("executing delete folder command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -4,7 +4,7 @@ use email::{backend::feature::BackendFeatureSource, folder::expunge::ExpungeFold
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
folder::arg::name::FolderNameArg, printer::Printer, folder::arg::name::FolderNameArg, printer::Printer,
}; };
@ -23,7 +23,7 @@ pub struct FolderExpungeCommand {
} }
impl FolderExpungeCommand { impl FolderExpungeCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing expunge folder command"); info!("executing expunge folder command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -6,7 +6,7 @@ use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, account::arg::name::AccountNameFlag,
backend::Backend, backend::Backend,
config::TomlConfig, config::Config,
folder::{Folders, FoldersTable}, folder::{Folders, FoldersTable},
printer::Printer, printer::Printer,
}; };
@ -29,7 +29,7 @@ pub struct FolderListCommand {
} }
impl FolderListCommand { impl FolderListCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing list folders command"); info!("executing list folders command");
let (toml_account_config, account_config) = config let (toml_account_config, account_config) = config

View file

@ -4,10 +4,10 @@ mod expunge;
mod list; mod list;
mod purge; mod purge;
use color_eyre::Result;
use clap::Subcommand; use clap::Subcommand;
use color_eyre::Result;
use crate::{config::TomlConfig, printer::Printer}; use crate::{config::Config, printer::Printer};
use self::{ use self::{
add::AddFolderCommand, delete::FolderDeleteCommand, expunge::FolderExpungeCommand, add::AddFolderCommand, delete::FolderDeleteCommand, expunge::FolderExpungeCommand,
@ -38,7 +38,7 @@ pub enum FolderSubcommand {
impl FolderSubcommand { impl FolderSubcommand {
#[allow(unused)] #[allow(unused)]
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
match self { match self {
Self::Add(cmd) => cmd.execute(printer, config).await, Self::Add(cmd) => cmd.execute(printer, config).await,
Self::List(cmd) => cmd.execute(printer, config).await, Self::List(cmd) => cmd.execute(printer, config).await,

View file

@ -7,7 +7,7 @@ use pimalaya_tui::prompt;
use tracing::info; use tracing::info;
use crate::{ use crate::{
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, account::arg::name::AccountNameFlag, backend::Backend, config::Config,
folder::arg::name::FolderNameArg, printer::Printer, folder::arg::name::FolderNameArg, printer::Printer,
}; };
@ -25,7 +25,7 @@ pub struct FolderPurgeCommand {
} }
impl FolderPurgeCommand { impl FolderPurgeCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
info!("executing purge folder command"); info!("executing purge folder command");
let folder = &self.folder.name; let folder = &self.folder.name;

View file

@ -1,24 +1,14 @@
use clap::Parser; use clap::Parser;
use color_eyre::{Result, Section}; use color_eyre::Result;
use himalaya::{ use himalaya::{
cli::Cli, config::TomlConfig, envelope::command::list::ListEnvelopesCommand, cli::Cli, config::Config, envelope::command::list::ListEnvelopesCommand,
message::command::mailto::MessageMailtoCommand, printer::StdoutPrinter, message::command::mailto::MessageMailtoCommand, printer::StdoutPrinter,
}; };
use std::env; use pimalaya_tui::cli::tracing;
use tracing::level_filters::LevelFilter;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
if env::var("RUST_LOG").is_err() { let tracing = tracing::install()?;
if std::env::args().any(|arg| arg == "--debug") {
env::set_var("RUST_LOG", "debug");
}
if std::env::args().any(|arg| arg == "--trace") {
env::set_var("RUST_LOG", "trace");
}
}
let filter = himalaya::tracing::install()?;
// if the first argument starts by "mailto:", execute straight the // if the first argument starts by "mailto:", execute straight the
// mailto message command // mailto message command
@ -28,7 +18,7 @@ async fn main() -> Result<()> {
if let Some(ref url) = mailto { if let Some(ref url) = mailto {
let mut printer = StdoutPrinter::default(); let mut printer = StdoutPrinter::default();
let config = TomlConfig::from_default_paths().await?; let config = Config::from_default_paths().await?;
return MessageMailtoCommand::new(url)? return MessageMailtoCommand::new(url)?
.execute(&mut printer, &config) .execute(&mut printer, &config)
@ -37,23 +27,15 @@ async fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
let mut printer = StdoutPrinter::new(cli.output); let mut printer = StdoutPrinter::new(cli.output);
let mut res = match cli.command { let res = match cli.command {
Some(cmd) => cmd.execute(&mut printer, cli.config_paths.as_ref()).await, Some(cmd) => cmd.execute(&mut printer, cli.config_paths.as_ref()).await,
None => { None => {
let config = TomlConfig::from_paths_or_default(cli.config_paths.as_ref()).await?; let config = Config::from_paths_or_default(cli.config_paths.as_ref()).await?;
ListEnvelopesCommand::default() ListEnvelopesCommand::default()
.execute(&mut printer, &config) .execute(&mut printer, &config)
.await .await
} }
}; };
if filter < LevelFilter::DEBUG { tracing.with_debug_and_trace_notes(res)
res = res.note("Run with --debug to enable logs with spantrace.");
};
if filter < LevelFilter::TRACE {
res = res.note("Run with --trace to enable verbose logs with backtrace.")
};
res
} }

View file

@ -1,5 +1,8 @@
use clap::ValueEnum; use clap::ValueEnum;
use color_eyre::{eyre::eyre, eyre::Error, Result}; use color_eyre::{
eyre::{bail, Error},
Result,
};
use serde::Serialize; use serde::Serialize;
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
@ -18,7 +21,7 @@ impl FromStr for OutputFmt {
match fmt { match fmt {
fmt if fmt.eq_ignore_ascii_case("json") => Ok(Self::Json), fmt if fmt.eq_ignore_ascii_case("json") => Ok(Self::Json),
fmt if fmt.eq_ignore_ascii_case("plain") => Ok(Self::Plain), fmt if fmt.eq_ignore_ascii_case("plain") => Ok(Self::Plain),
unknown => Err(eyre!("cannot parse output format {}", unknown)), unknown => bail!("cannot parse output format {unknown}"),
} }
} }
} }

View file

@ -1,13 +1,14 @@
use color_eyre::{eyre::Context, Result};
use std::{ use std::{
fmt, fmt,
io::{self, Write}, io::{stderr, stdout, Stderr, Stdout, Write},
}; };
use color_eyre::{eyre::Context, Result};
use crate::output::OutputFmt; use crate::output::OutputFmt;
pub trait PrintTable { pub trait PrintTable {
fn print(&self, writer: &mut dyn io::Write, table_max_width: Option<u16>) -> Result<()>; fn print(&self, writer: &mut dyn Write, table_max_width: Option<u16>) -> Result<()>;
} }
pub trait Printer { pub trait Printer {
@ -23,16 +24,16 @@ pub trait Printer {
} }
pub struct StdoutPrinter { pub struct StdoutPrinter {
stdout: io::Stdout, stdout: Stdout,
stderr: io::Stderr, stderr: Stderr,
output: OutputFmt, output: OutputFmt,
} }
impl StdoutPrinter { impl StdoutPrinter {
pub fn new(output: OutputFmt) -> Self { pub fn new(output: OutputFmt) -> Self {
Self { Self {
stdout: io::stdout(), stdout: stdout(),
stderr: io::stderr(), stderr: stderr(),
output, output,
} }
} }