diff --git a/Cargo.lock b/Cargo.lock index 8bef91db..e4566407 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,9 +130,6 @@ name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -dependencies = [ - "serde_core", -] [[package]] name = "bounded-static" @@ -305,7 +302,6 @@ version = "7.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" dependencies = [ - "crossterm 0.29.0", "unicode-segmentation", "unicode-width 0.2.2", ] @@ -326,46 +322,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "serde", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags", - "crossterm_winapi", - "document-features", - "parking_lot", - "rustix", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "dirs" version = "6.0.0" @@ -398,15 +354,6 @@ dependencies = [ "syn", ] -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - [[package]] name = "dunce" version = "1.0.5" @@ -609,7 +556,7 @@ dependencies = [ "chrono", "clap", "comfy-table", - "crossterm 0.27.0", + "dirs", "gethostname", "html2text", "io-imap", @@ -995,12 +942,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - [[package]] name = "lock_api" version = "0.4.14" @@ -1066,18 +1007,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "native-tls" version = "0.2.18" @@ -1764,37 +1693,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - [[package]] name = "siphasher" version = "1.0.2" @@ -2194,22 +2092,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" @@ -2219,12 +2101,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-link" version = "0.2.1" @@ -2240,15 +2116,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -2300,21 +2167,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -2354,12 +2206,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -2378,12 +2224,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -2402,12 +2242,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2438,12 +2272,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -2462,12 +2290,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -2486,12 +2308,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -2510,12 +2326,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 946540bc..20fc7a07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,8 @@ pimalaya-toolbox = { version = "0.0.4", default-features = false, features = ["b anyhow = "1" chrono = { version = "0.4", default-features = false } clap = { version = "4.4", features = ["derive", "env", "wrap_help"] } -comfy-table = "7" -crossterm = { version = "0.27", features = ["serde"] } +comfy-table = { version = "7", default-features = false } +dirs = "6" gethostname = "1" html2text = "0.12" io-imap = { version = "0.0.1", default-features = false, optional = true } diff --git a/src/account.rs b/src/account.rs new file mode 100644 index 00000000..43073955 --- /dev/null +++ b/src/account.rs @@ -0,0 +1,48 @@ +use std::{env::temp_dir, path::PathBuf}; + +use crate::config::{AccountConfig, Config}; +use anyhow::Result; +use comfy_table::presets; +use dirs::download_dir; + +#[derive(Debug)] +pub struct Account { + pub backend: B, + + pub email: String, + pub display_name: Option, + pub signature: String, + pub downloads_dir: PathBuf, + + pub table_preset: &'static str, +} + +impl Account { + pub fn new(config: Config, account_config: AccountConfig, backend: B) -> Result { + Ok(Self { + backend, + email: account_config.email, + display_name: account_config.display_name.or(config.display_name), + signature: match account_config.signature.or(config.signature) { + None => String::new(), + Some(ref signature) => { + account_config + .signature_delim + .or(config.signature_delim) + .unwrap_or(String::from("-- \n")) + + signature + } + }, + downloads_dir: match account_config + .downloads_dir + .as_ref() + .and_then(|dir| dir.to_str()) + { + Some(dir) => PathBuf::from(shellexpand::full(dir)?.to_string()), + None => download_dir().unwrap_or_else(temp_dir), + }, + + table_preset: presets::UTF8_FULL_CONDENSED, + }) + } +} diff --git a/src/cli.rs b/src/cli.rs index f308dba1..d09c5d9a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -14,11 +14,11 @@ use pimalaya_toolbox::{ }, }; -use crate::config::Config; #[cfg(feature = "imap")] use crate::imap::command::ImapCommand; #[cfg(feature = "smtp")] use crate::smtp::command::SmtpCommand; +use crate::{account::Account, config::Config}; #[derive(Parser, Debug)] #[command(name = env!("CARGO_PKG_NAME"))] @@ -71,22 +71,28 @@ impl BackendCommand { #[cfg(feature = "imap")] Self::Imap(cmd) => { let config = Config::from_paths_or_default(config_paths)?; - let (account_name, account_config) = config.get_account(account_name)?; - let Some(imap_config) = account_config.imap else { + let (account_name, mut account_config) = config.get_account(account_name)?; + + let Some(imap_config) = account_config.imap.take() else { bail!("IMAP config is missing for account `{account_name}`") }; - cmd.exec(printer, imap_config) + let account = Account::new(config, account_config, imap_config)?; + + cmd.exec(printer, account) } #[cfg(feature = "smtp")] Self::Smtp(cmd) => { let config = Config::from_paths_or_default(config_paths)?; - let (account_name, account_config) = config.get_account(account_name)?; - let Some(smtp_config) = account_config.smtp else { + let (account_name, mut account_config) = config.get_account(account_name)?; + + let Some(smtp_config) = account_config.smtp.take() else { bail!("SMTP config is missing for account `{account_name}`") }; - cmd.exec(printer, smtp_config) + let account = Account::new(config, account_config, smtp_config)?; + + cmd.exec(printer, account) } #[allow(unreachable_patterns)] _ => bail!("No backend available"), diff --git a/src/imap/account.rs b/src/imap/account.rs new file mode 100644 index 00000000..81d73a8d --- /dev/null +++ b/src/imap/account.rs @@ -0,0 +1,3 @@ +use crate::{account::Account, config::ImapConfig}; + +pub type ImapAccount = Account; diff --git a/src/imap/command.rs b/src/imap/command.rs index 5506413e..b417a454 100644 --- a/src/imap/command.rs +++ b/src/imap/command.rs @@ -2,12 +2,9 @@ use anyhow::Result; use clap::Subcommand; use pimalaya_toolbox::terminal::printer::Printer; -use crate::{ - config::ImapConfig, - imap::{ - envelope::command::EnvelopeCommand, flag::command::FlagCommand, - mailbox::command::MailboxCommand, message::command::MessageCommand, - }, +use crate::imap::{ + account::ImapAccount, envelope::command::EnvelopeCommand, flag::command::FlagCommand, + mailbox::command::MailboxCommand, message::command::MessageCommand, }; /// IMAP CLI (requires `imap` cargo feature). @@ -31,12 +28,12 @@ pub enum ImapCommand { } impl ImapCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { match self { - Self::Envelopes(cmd) => cmd.exec(printer, config), - Self::Flags(cmd) => cmd.exec(printer, config), - Self::Mailboxes(cmd) => cmd.exec(printer, config), - Self::Messages(cmd) => cmd.exec(printer, config), + Self::Envelopes(cmd) => cmd.exec(printer, account), + Self::Flags(cmd) => cmd.exec(printer, account), + Self::Mailboxes(cmd) => cmd.exec(printer, account), + Self::Messages(cmd) => cmd.exec(printer, account), } } } diff --git a/src/imap/envelope/command.rs b/src/imap/envelope/command.rs index 20401042..f9b41b11 100644 --- a/src/imap/envelope/command.rs +++ b/src/imap/envelope/command.rs @@ -4,9 +4,12 @@ use pimalaya_toolbox::terminal::printer::Printer; use crate::{ config::ImapConfig, - imap::envelope::{ - get::GetEnvelopeCommand, list::ListEnvelopesCommand, search::SearchEnvelopesCommand, - sort::SortEnvelopesCommand, thread::ThreadEnvelopesCommand, + imap::{ + account::ImapAccount, + envelope::{ + get::GetEnvelopeCommand, list::ListEnvelopesCommand, search::SearchEnvelopesCommand, + sort::SortEnvelopesCommand, thread::ThreadEnvelopesCommand, + }, }, }; @@ -25,13 +28,13 @@ pub enum EnvelopeCommand { } impl EnvelopeCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { match self { - Self::List(cmd) => cmd.exec(printer, config), - Self::Get(cmd) => cmd.exec(printer, config), - Self::Search(cmd) => cmd.exec(printer, config), - Self::Sort(cmd) => cmd.exec(printer, config), - Self::Thread(cmd) => cmd.exec(printer, config), + Self::List(cmd) => cmd.exec(printer, account), + Self::Get(cmd) => cmd.exec(printer, account), + Self::Search(cmd) => cmd.exec(printer, account), + Self::Sort(cmd) => cmd.exec(printer, account), + Self::Thread(cmd) => cmd.exec(printer, account), } } } diff --git a/src/imap/envelope/get.rs b/src/imap/envelope/get.rs index 6cab8931..9518f456 100644 --- a/src/imap/envelope/get.rs +++ b/src/imap/envelope/get.rs @@ -14,13 +14,11 @@ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{ - config::ImapConfig, - imap::{ - envelope::list::{decode_mime, format_addresses}, - mailbox::arg::MailboxNameOptionalFlag, - stream, - }, +use crate::imap::{ + account::ImapAccount, + envelope::list::{decode_mime, format_addresses}, + mailbox::arg::MailboxNameOptionalFlag, + stream, }; /// Get a single message envelope. @@ -43,8 +41,8 @@ pub struct GetEnvelopeCommand { } impl GetEnvelopeCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/envelope/list.rs b/src/imap/envelope/list.rs index c185f4e9..87b22dda 100644 --- a/src/imap/envelope/list.rs +++ b/src/imap/envelope/list.rs @@ -18,7 +18,7 @@ use pimalaya_toolbox::terminal::printer::Printer; use rfc2047_decoder::{Decoder, RecoverStrategy}; use serde::{Serialize, Serializer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalArg, stream}; /// Decode RFC 2047 MIME-encoded string, falling back to original on error. pub fn decode_mime(s: &str) -> String { @@ -52,8 +52,8 @@ pub struct ListEnvelopesCommand { } impl ListEnvelopesCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; @@ -163,7 +163,7 @@ impl fmt::Display for EnvelopesTable { let id_header = if self.uid_mode { "UID" } else { "SEQ" }; table - .load_preset(presets::ASCII_MARKDOWN) + .load_preset(presets::ASCII_FULL) .set_content_arrangement(ContentArrangement::DynamicFullWidth) .set_header(Row::from([ Cell::new(id_header), diff --git a/src/imap/envelope/search.rs b/src/imap/envelope/search.rs index ed0e8624..721194d3 100644 --- a/src/imap/envelope/search.rs +++ b/src/imap/envelope/search.rs @@ -15,7 +15,7 @@ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalArg, stream}; /// Search messages by criteria. /// @@ -57,8 +57,8 @@ pub struct SearchEnvelopesCommand { } impl SearchEnvelopesCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/envelope/sort.rs b/src/imap/envelope/sort.rs index b0beb685..ad3fbfb9 100644 --- a/src/imap/envelope/sort.rs +++ b/src/imap/envelope/sort.rs @@ -14,9 +14,9 @@ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{ - config::ImapConfig, - imap::{envelope::search::parse_query, mailbox::arg::MailboxNameOptionalArg, stream}, +use crate::imap::{ + account::ImapAccount, envelope::search::parse_query, mailbox::arg::MailboxNameOptionalArg, + stream, }; /// Sort messages by criteria. @@ -56,8 +56,8 @@ pub struct SortEnvelopesCommand { } impl SortEnvelopesCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/envelope/thread.rs b/src/imap/envelope/thread.rs index 8ce6cdd8..fcbfcfa8 100644 --- a/src/imap/envelope/thread.rs +++ b/src/imap/envelope/thread.rs @@ -14,13 +14,11 @@ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{ - config::ImapConfig, - imap::{ - envelope::{list::decode_mime, search::parse_query}, - mailbox::arg::MailboxNameOptionalArg, - stream, - }, +use crate::imap::{ + account::ImapAccount, + envelope::{list::decode_mime, search::parse_query}, + mailbox::arg::MailboxNameOptionalArg, + stream, }; /// Thread messages by algorithm. @@ -50,8 +48,8 @@ pub struct ThreadEnvelopesCommand { } impl ThreadEnvelopesCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/flag/add.rs b/src/imap/flag/add.rs index 2e7648fe..1a619146 100644 --- a/src/imap/flag/add.rs +++ b/src/imap/flag/add.rs @@ -10,7 +10,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Add flags to messages. /// @@ -35,8 +35,8 @@ pub struct AddFlagsCommand { } impl AddFlagsCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/flag/command.rs b/src/imap/flag/command.rs index 024c5d35..85a72e0b 100644 --- a/src/imap/flag/command.rs +++ b/src/imap/flag/command.rs @@ -4,9 +4,12 @@ use pimalaya_toolbox::terminal::printer::Printer; use crate::{ config::ImapConfig, - imap::flag::{ - add::AddFlagsCommand, list::ListFlagsCommand, remove::RemoveFlagsCommand, - set::SetFlagsCommand, + imap::{ + account::ImapAccount, + flag::{ + add::AddFlagsCommand, list::ListFlagsCommand, remove::RemoveFlagsCommand, + set::SetFlagsCommand, + }, }, }; @@ -24,12 +27,12 @@ pub enum FlagCommand { } impl FlagCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { match self { - Self::List(cmd) => cmd.exec(printer, config), - Self::Add(cmd) => cmd.exec(printer, config), - Self::Set(cmd) => cmd.exec(printer, config), - Self::Remove(cmd) => cmd.exec(printer, config), + Self::List(cmd) => cmd.exec(printer, account), + Self::Add(cmd) => cmd.exec(printer, account), + Self::Set(cmd) => cmd.exec(printer, account), + Self::Remove(cmd) => cmd.exec(printer, account), } } } diff --git a/src/imap/flag/list.rs b/src/imap/flag/list.rs index d95c6076..212a1f9d 100644 --- a/src/imap/flag/list.rs +++ b/src/imap/flag/list.rs @@ -11,7 +11,7 @@ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalArg, stream}; /// List available flags for a mailbox. /// @@ -25,8 +25,8 @@ pub struct ListFlagsCommand { } impl ListFlagsCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/flag/remove.rs b/src/imap/flag/remove.rs index 929bb731..d71f6699 100644 --- a/src/imap/flag/remove.rs +++ b/src/imap/flag/remove.rs @@ -10,7 +10,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Remove flags from messages. /// @@ -35,8 +35,8 @@ pub struct RemoveFlagsCommand { } impl RemoveFlagsCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/flag/set.rs b/src/imap/flag/set.rs index 083204d8..cb3e2a4e 100644 --- a/src/imap/flag/set.rs +++ b/src/imap/flag/set.rs @@ -10,7 +10,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Set flags on messages (replacing existing flags). /// @@ -35,8 +35,8 @@ pub struct SetFlagsCommand { } impl SetFlagsCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/close.rs b/src/imap/mailbox/close.rs index b32fc17c..6541b6f8 100644 --- a/src/imap/mailbox/close.rs +++ b/src/imap/mailbox/close.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::close::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::stream}; +use crate::imap::{account::ImapAccount, stream}; /// Close the current, selected mailbox. /// @@ -19,8 +19,8 @@ use crate::{config::ImapConfig, imap::stream}; pub struct CloseMailboxCommand; impl CloseMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mut arg = None; let mut close_coroutine = ImapClose::new(context); diff --git a/src/imap/mailbox/command.rs b/src/imap/mailbox/command.rs index 822ffded..5448b379 100644 --- a/src/imap/mailbox/command.rs +++ b/src/imap/mailbox/command.rs @@ -4,12 +4,15 @@ use pimalaya_toolbox::terminal::printer::Printer; use crate::{ config::ImapConfig, - imap::mailbox::{ - close::CloseMailboxCommand, create::CreateMailboxCommand, delete::DeleteMailboxCommand, - expunge::ExpungeMailboxCommand, list::ListMailboxesCommand, purge::PurgeMailboxCommand, - rename::RenameMailboxCommand, select::SelectMailboxCommand, status::StatusMailboxCommand, - subscribe::SubscribeMailboxCommand, unselect::UnselectMailboxCommand, - unsubscribe::UnsubscribeMailboxCommand, + imap::{ + account::ImapAccount, + mailbox::{ + close::CloseMailboxCommand, create::CreateMailboxCommand, delete::DeleteMailboxCommand, + expunge::ExpungeMailboxCommand, list::ListMailboxesCommand, purge::PurgeMailboxCommand, + rename::RenameMailboxCommand, select::SelectMailboxCommand, + status::StatusMailboxCommand, subscribe::SubscribeMailboxCommand, + unselect::UnselectMailboxCommand, unsubscribe::UnsubscribeMailboxCommand, + }, }, }; @@ -37,20 +40,20 @@ pub enum MailboxCommand { } impl MailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { match self { - Self::Close(cmd) => cmd.exec(printer, config), - Self::Create(cmd) => cmd.exec(printer, config), - Self::Delete(cmd) => cmd.exec(printer, config), - Self::Expunge(cmd) => cmd.exec(printer, config), - Self::List(cmd) => cmd.exec(printer, config), - Self::Purge(cmd) => cmd.exec(printer, config), - Self::Rename(cmd) => cmd.exec(printer, config), - Self::Select(cmd) => cmd.exec(printer, config), - Self::Status(cmd) => cmd.exec(printer, config), - Self::Subscribe(cmd) => cmd.exec(printer, config), - Self::Unselect(cmd) => cmd.exec(printer, config), - Self::Unsubscribe(cmd) => cmd.exec(printer, config), + Self::Close(cmd) => cmd.exec(printer, account), + Self::Create(cmd) => cmd.exec(printer, account), + Self::Delete(cmd) => cmd.exec(printer, account), + Self::Expunge(cmd) => cmd.exec(printer, account), + Self::List(cmd) => cmd.exec(printer, account), + Self::Purge(cmd) => cmd.exec(printer, account), + Self::Rename(cmd) => cmd.exec(printer, account), + Self::Select(cmd) => cmd.exec(printer, account), + Self::Status(cmd) => cmd.exec(printer, account), + Self::Subscribe(cmd) => cmd.exec(printer, account), + Self::Unselect(cmd) => cmd.exec(printer, account), + Self::Unsubscribe(cmd) => cmd.exec(printer, account), } } } diff --git a/src/imap/mailbox/create.rs b/src/imap/mailbox/create.rs index 62ebab0e..b6aa8e9a 100644 --- a/src/imap/mailbox/create.rs +++ b/src/imap/mailbox/create.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::create::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Create the given mailbox. /// @@ -17,8 +17,8 @@ pub struct CreateMailboxCommand { } impl CreateMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/delete.rs b/src/imap/mailbox/delete.rs index 480a5fcb..905a4126 100644 --- a/src/imap/mailbox/delete.rs +++ b/src/imap/mailbox/delete.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::delete::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Delete the given mailbox. /// @@ -17,8 +17,8 @@ pub struct DeleteMailboxCommand { } impl DeleteMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/expunge.rs b/src/imap/mailbox/expunge.rs index 1b69509c..92eb828c 100644 --- a/src/imap/mailbox/expunge.rs +++ b/src/imap/mailbox/expunge.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::{expunge::*, select::*}; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Expunge the given mailbox. /// @@ -26,8 +26,8 @@ pub struct ExpungeMailboxCommand { } impl ExpungeMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (mut context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (mut context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/list.rs b/src/imap/mailbox/list.rs index 532e0b73..a50566c8 100644 --- a/src/imap/mailbox/list.rs +++ b/src/imap/mailbox/list.rs @@ -1,15 +1,17 @@ -use std::{fmt, ops::Deref}; +use std::fmt; use anyhow::{bail, Result}; use clap::Parser; -use comfy_table::{presets, Cell, ContentArrangement, Row, Table}; -use crossterm::style::Color; -use io_imap::coroutines::{list::*, lsub::*}; +use comfy_table::{Cell, ContentArrangement, Row, Table}; +use io_imap::{ + coroutines::{list::*, lsub::*}, + types::{core::QuotedChar, flag::FlagNameAttribute, mailbox::Mailbox}, +}; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{config::ImapConfig, imap::stream}; +use crate::imap::{account::ImapAccount, stream}; /// List mailboxes. /// @@ -32,8 +34,8 @@ pub struct ListMailboxesCommand { } impl ListMailboxesCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let reference = self.reference.try_into()?; let pattern = self.pattern.try_into()?; @@ -62,124 +64,19 @@ impl ListMailboxesCommand { } }; - let table = MailboxesTable::from(mailboxes); + let table = MailboxesTable { + rows: mailboxes.into_iter().map(From::from).collect(), + preset: account.table_preset, + }; - printer.out(table)?; - Ok(()) - } -} - -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct ListMailboxesTableConfig { - pub preset: Option, - pub name_color: Option, - pub desc_color: Option, -} - -impl ListMailboxesTableConfig { - pub fn preset(&self) -> &str { - self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN) - } - - pub fn name_color(&self) -> comfy_table::Color { - map_color(self.name_color.unwrap_or(Color::Blue)) - } - - pub fn desc_color(&self) -> comfy_table::Color { - map_color(self.desc_color.unwrap_or(Color::Green)) - } -} - -#[derive(Clone, Debug, Serialize)] -pub struct Mailbox { - pub name: String, - pub delimiter: String, - pub attributes: Vec, -} - -#[derive(Clone, Debug, Default, Serialize)] -pub struct Mailboxes(Vec); - -impl> From for Mailboxes { - fn from(mboxes: T) -> Self { - Self(mboxes.into_iter().collect()) - } -} - -impl Deref for Mailboxes { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 + printer.out(table) } } +#[derive(Clone, Debug, Default)] pub struct MailboxesTable { - mailboxes: Mailboxes, - width: Option, - config: ListMailboxesTableConfig, -} - -impl MailboxesTable { - pub fn with_some_width(mut self, width: Option) -> Self { - self.width = width; - self - } - - pub fn with_some_preset(mut self, preset: Option) -> Self { - self.config.preset = preset; - self - } - - pub fn with_some_name_color(mut self, color: Option) -> Self { - self.config.name_color = color; - self - } - - pub fn with_some_desc_color(mut self, color: Option) -> Self { - self.config.desc_color = color; - self - } -} - -impl - From< - Vec<( - io_imap::types::mailbox::Mailbox<'static>, - Option, - Vec>, - )>, - > for MailboxesTable -{ - fn from( - mailboxes: Vec<( - io_imap::types::mailbox::Mailbox<'static>, - Option, - Vec>, - )>, - ) -> Self { - Self { - mailboxes: mailboxes - .into_iter() - .map(|(mbox, delim, attrs)| Mailbox { - name: match mbox { - io_imap::types::mailbox::Mailbox::Inbox => "Inbox".into(), - io_imap::types::mailbox::Mailbox::Other(mbox) => { - String::from_utf8_lossy(mbox.inner().as_ref()).to_string() - } - }, - delimiter: match delim { - Some(delim) => delim.inner().to_string(), - None => String::new(), - }, - attributes: attrs.into_iter().map(|attr| attr.to_string()).collect(), - }) - .into(), - width: None, - config: Default::default(), - } - } + pub rows: Vec, + pub preset: &'static str, } impl fmt::Display for MailboxesTable { @@ -187,28 +84,24 @@ impl fmt::Display for MailboxesTable { let mut table = Table::new(); table - .load_preset(self.config.preset()) + .load_preset(self.preset) .set_content_arrangement(ContentArrangement::DynamicFullWidth) .set_header(Row::from([ Cell::new("NAME"), Cell::new("DELIMITER"), Cell::new("ATTRIBUTES"), ])) - .add_rows(self.mailboxes.iter().map(|mbox| { + .add_rows(self.rows.iter().map(|mbox| { let mut row = Row::new(); row.max_height(1); - row.add_cell(Cell::new(&mbox.name).fg(self.config.name_color())); - row.add_cell(Cell::new(&mbox.delimiter).fg(self.config.desc_color())); - row.add_cell(Cell::new(&mbox.attributes.join(", ")).fg(self.config.desc_color())); + row.add_cell(Cell::new(&mbox.name)); + row.add_cell(Cell::new(&mbox.delimiter)); + row.add_cell(Cell::new(&mbox.attributes.join(", "))); row })); - if let Some(width) = self.width { - table.set_width(width); - } - writeln!(f)?; write!(f, "{table}")?; writeln!(f)?; @@ -218,30 +111,41 @@ impl fmt::Display for MailboxesTable { impl Serialize for MailboxesTable { fn serialize(&self, serializer: S) -> Result { - self.mailboxes.serialize(serializer) + self.rows.serialize(serializer) } } -fn map_color(color: Color) -> comfy_table::Color { - match color { - Color::Reset => comfy_table::Color::Reset, - Color::Black => comfy_table::Color::Black, - Color::DarkGrey => comfy_table::Color::DarkGrey, - Color::Red => comfy_table::Color::Red, - Color::DarkRed => comfy_table::Color::DarkRed, - Color::Green => comfy_table::Color::Green, - Color::DarkGreen => comfy_table::Color::DarkGreen, - Color::Yellow => comfy_table::Color::Yellow, - Color::DarkYellow => comfy_table::Color::DarkYellow, - Color::Blue => comfy_table::Color::Blue, - Color::DarkBlue => comfy_table::Color::DarkBlue, - Color::Magenta => comfy_table::Color::Magenta, - Color::DarkMagenta => comfy_table::Color::DarkMagenta, - Color::Cyan => comfy_table::Color::Cyan, - Color::DarkCyan => comfy_table::Color::DarkCyan, - Color::White => comfy_table::Color::White, - Color::Grey => comfy_table::Color::Grey, - Color::Rgb { r, g, b } => comfy_table::Color::Rgb { r, g, b }, - Color::AnsiValue(n) => comfy_table::Color::AnsiValue(n), +#[derive(Clone, Debug, Serialize)] +pub struct MailboxRow { + pub name: String, + pub delimiter: String, + pub attributes: Vec, +} + +impl + From<( + Mailbox<'static>, + Option, + Vec>, + )> for MailboxRow +{ + fn from( + (mbox, delim, attrs): ( + Mailbox<'static>, + Option, + Vec>, + ), + ) -> Self { + Self { + name: match mbox { + Mailbox::Inbox => "Inbox".into(), + Mailbox::Other(mbox) => String::from_utf8_lossy(mbox.inner().as_ref()).to_string(), + }, + delimiter: match delim { + Some(delim) => delim.inner().to_string(), + None => String::new(), + }, + attributes: attrs.into_iter().map(|attr| attr.to_string()).collect(), + } } } diff --git a/src/imap/mailbox/purge.rs b/src/imap/mailbox/purge.rs index d9642526..034039a5 100644 --- a/src/imap/mailbox/purge.rs +++ b/src/imap/mailbox/purge.rs @@ -7,7 +7,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Shortcut for marking as deleted all envelopes then expunging the /// given mailbox. @@ -30,8 +30,8 @@ pub struct PurgeMailboxCommand { } impl PurgeMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (mut context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (mut context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/rename.rs b/src/imap/mailbox/rename.rs index 0b27ead3..7635647b 100644 --- a/src/imap/mailbox/rename.rs +++ b/src/imap/mailbox/rename.rs @@ -4,12 +4,10 @@ use io_imap::coroutines::rename::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{ - config::ImapConfig, - imap::{ - mailbox::arg::{MailboxNameArg, TargetMailboxNameArg}, - stream, - }, +use crate::imap::{ + account::ImapAccount, + mailbox::arg::{MailboxNameArg, TargetMailboxNameArg}, + stream, }; /// Rename the given mailbox. @@ -25,8 +23,8 @@ pub struct RenameMailboxCommand { } impl RenameMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let from = self.from.name.try_into()?; let to = self.to.name.try_into()?; diff --git a/src/imap/mailbox/select.rs b/src/imap/mailbox/select.rs index 05ce2671..9aa8e6ba 100644 --- a/src/imap/mailbox/select.rs +++ b/src/imap/mailbox/select.rs @@ -4,10 +4,7 @@ use io_imap::coroutines::select::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{ - config::ImapConfig, - imap::{mailbox::arg::MailboxNameArg, stream}, -}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Select the given mailbox. /// @@ -24,8 +21,8 @@ pub struct SelectMailboxCommand { } impl SelectMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/status.rs b/src/imap/mailbox/status.rs index 0bb320a5..c82b48f8 100644 --- a/src/imap/mailbox/status.rs +++ b/src/imap/mailbox/status.rs @@ -3,13 +3,15 @@ use std::fmt; use anyhow::{bail, Result}; use clap::Parser; use comfy_table::{presets, Cell, ContentArrangement, Row, Table}; -use io_imap::coroutines::status::*; -use io_imap::types::status::{StatusDataItem, StatusDataItemName}; +use io_imap::{ + coroutines::status::*, + types::status::{StatusDataItem, StatusDataItemName}, +}; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::Printer; use serde::{Serialize, Serializer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Get the status of the given mailbox. /// @@ -22,8 +24,8 @@ pub struct StatusMailboxCommand { } impl StatusMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; let item_names = vec![ diff --git a/src/imap/mailbox/subscribe.rs b/src/imap/mailbox/subscribe.rs index 2f3d7686..147e7b26 100644 --- a/src/imap/mailbox/subscribe.rs +++ b/src/imap/mailbox/subscribe.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::subscribe::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Subscribe to the given mailbox. /// @@ -17,8 +17,8 @@ pub struct SubscribeMailboxCommand { } impl SubscribeMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/mailbox/unselect.rs b/src/imap/mailbox/unselect.rs index 9bdcbfb6..9a08021b 100644 --- a/src/imap/mailbox/unselect.rs +++ b/src/imap/mailbox/unselect.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::unselect::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::stream}; +use crate::imap::{account::ImapAccount, stream}; /// Unselect a current, selected mailbox. /// @@ -18,8 +18,8 @@ use crate::{config::ImapConfig, imap::stream}; pub struct UnselectMailboxCommand; impl UnselectMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mut arg = None; let mut unselect_coroutine = ImapUnselect::new(context); diff --git a/src/imap/mailbox/unsubscribe.rs b/src/imap/mailbox/unsubscribe.rs index 5d1bb357..4741838b 100644 --- a/src/imap/mailbox/unsubscribe.rs +++ b/src/imap/mailbox/unsubscribe.rs @@ -4,7 +4,7 @@ use io_imap::coroutines::unsubscribe::*; use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameArg, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream}; /// Unsubscribe from the given mailbox. /// @@ -17,8 +17,8 @@ pub struct UnsubscribeMailboxCommand { } impl UnsubscribeMailboxCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/command.rs b/src/imap/message/command.rs index 608773c0..00350979 100644 --- a/src/imap/message/command.rs +++ b/src/imap/message/command.rs @@ -2,9 +2,9 @@ use anyhow::Result; use clap::Subcommand; use pimalaya_toolbox::terminal::printer::Printer; -use crate::{ - config::ImapConfig, - imap::message::{ +use crate::imap::{ + account::ImapAccount, + message::{ copy::CopyMessageCommand, delete::DeleteMessageCommand, export::ExportMessageCommand, get::GetMessageCommand, r#move::MoveMessageCommand, read::ReadMessageCommand, save::SaveMessageCommand, @@ -28,15 +28,15 @@ pub enum MessageCommand { } impl MessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { match self { - Self::Save(cmd) => cmd.exec(printer, config), - Self::Get(cmd) => cmd.exec(printer, config), - Self::Read(cmd) => cmd.exec(printer, config), - Self::Export(cmd) => cmd.exec(printer, config), - Self::Copy(cmd) => cmd.exec(printer, config), - Self::Move(cmd) => cmd.exec(printer, config), - Self::Delete(cmd) => cmd.exec(printer, config), + Self::Save(cmd) => cmd.exec(printer, account), + Self::Get(cmd) => cmd.exec(printer, account), + Self::Read(cmd) => cmd.exec(printer, account), + Self::Export(cmd) => cmd.exec(printer, account), + Self::Copy(cmd) => cmd.exec(printer, account), + Self::Move(cmd) => cmd.exec(printer, account), + Self::Delete(cmd) => cmd.exec(printer, account), } } } diff --git a/src/imap/message/copy.rs b/src/imap/message/copy.rs index e17285f2..ab7bfc75 100644 --- a/src/imap/message/copy.rs +++ b/src/imap/message/copy.rs @@ -7,7 +7,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Copy messages to another mailbox. /// @@ -32,8 +32,8 @@ pub struct CopyMessageCommand { } impl CopyMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/delete.rs b/src/imap/message/delete.rs index e4de340e..ad0d0d72 100644 --- a/src/imap/message/delete.rs +++ b/src/imap/message/delete.rs @@ -7,7 +7,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Delete messages from a mailbox. /// @@ -28,8 +28,8 @@ pub struct DeleteMessageCommand { } impl DeleteMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/export.rs b/src/imap/message/export.rs index 73ce6415..d573ceb2 100644 --- a/src/imap/message/export.rs +++ b/src/imap/message/export.rs @@ -15,7 +15,7 @@ use io_stream::runtimes::std::handle; use mail_parser::{MessageParser, MimeHeaders}; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Export type for message export. #[derive(Debug, Clone, clap::ValueEnum)] @@ -61,8 +61,8 @@ pub struct ExportMessageCommand { } impl ExportMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/get.rs b/src/imap/message/get.rs index 711cbf31..c4cbd476 100644 --- a/src/imap/message/get.rs +++ b/src/imap/message/get.rs @@ -12,7 +12,7 @@ use mail_parser::{Addr, Address, ContentType, MessageParser, MimeHeaders}; use pimalaya_toolbox::terminal::printer::Printer; use serde::Serialize; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Get a message and display its structure. /// @@ -33,8 +33,8 @@ pub struct GetMessageCommand { } impl GetMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/move.rs b/src/imap/message/move.rs index 56b94493..a794cb1d 100644 --- a/src/imap/message/move.rs +++ b/src/imap/message/move.rs @@ -7,7 +7,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Move messages to another mailbox. /// @@ -33,8 +33,8 @@ pub struct MoveMessageCommand { } impl MoveMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/read.rs b/src/imap/message/read.rs index 1121ad06..af46f7b8 100644 --- a/src/imap/message/read.rs +++ b/src/imap/message/read.rs @@ -11,7 +11,7 @@ use mail_parser::MessageParser; use pimalaya_toolbox::terminal::printer::Printer; use serde::Serialize; -use crate::{config::ImapConfig, imap::mailbox::arg::MailboxNameOptionalFlag, imap::stream}; +use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream}; /// Read message content. /// @@ -40,8 +40,8 @@ pub struct ReadMessageCommand { } impl ReadMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let mailbox = self.mailbox.name.try_into()?; diff --git a/src/imap/message/save.rs b/src/imap/message/save.rs index 1a82ab77..9f984edf 100644 --- a/src/imap/message/save.rs +++ b/src/imap/message/save.rs @@ -1,4 +1,4 @@ -use std::io::{self, Read}; +use std::io::{stdin, Read}; use anyhow::{bail, Result}; use clap::Parser; @@ -12,7 +12,7 @@ use io_imap::{ use io_stream::runtimes::std::handle; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::ImapConfig, imap::stream}; +use crate::imap::{account::ImapAccount, stream}; /// Save a message to a mailbox. /// @@ -35,12 +35,12 @@ pub struct SaveMessageCommand { } impl SaveMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> { - let (mut context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> { + let (mut context, mut stream) = stream::connect(account.backend)?; // Read message from stdin let mut message = Vec::new(); - io::stdin().read_to_end(&mut message)?; + stdin().read_to_end(&mut message)?; if message.is_empty() { bail!("No message provided on stdin"); diff --git a/src/imap/mod.rs b/src/imap/mod.rs index 5498e1d1..a32c162e 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -1,3 +1,4 @@ +pub mod account; pub mod command; pub mod envelope; pub mod flag; diff --git a/src/main.rs b/src/main.rs index 2b28fc21..45999692 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod account; mod cli; mod config; #[cfg(feature = "imap")] diff --git a/src/smtp/account.rs b/src/smtp/account.rs new file mode 100644 index 00000000..bf8c3f6d --- /dev/null +++ b/src/smtp/account.rs @@ -0,0 +1,3 @@ +use crate::{account::Account, config::SmtpConfig}; + +pub type SmtpAccount = Account; diff --git a/src/smtp/command.rs b/src/smtp/command.rs index 3633fcb4..bfb78f3d 100644 --- a/src/smtp/command.rs +++ b/src/smtp/command.rs @@ -2,7 +2,7 @@ use anyhow::Result; use clap::Subcommand; use pimalaya_toolbox::terminal::printer::Printer; -use crate::{config::SmtpConfig, smtp::message::command::MessageCommand}; +use crate::smtp::{account::SmtpAccount, message::command::MessageCommand}; /// SMTP CLI (requires `smtp` cargo feature). /// @@ -18,9 +18,9 @@ pub enum SmtpCommand { } impl SmtpCommand { - pub fn exec(self, printer: &mut impl Printer, config: SmtpConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> { match self { - Self::Messages(cmd) => cmd.exec(printer, config), + Self::Messages(cmd) => cmd.exec(printer, account), } } } diff --git a/src/smtp/message/command.rs b/src/smtp/message/command.rs index 57d56f0e..9f655424 100644 --- a/src/smtp/message/command.rs +++ b/src/smtp/message/command.rs @@ -2,7 +2,7 @@ use anyhow::Result; use clap::Subcommand; use pimalaya_toolbox::terminal::printer::Printer; -use crate::{config::SmtpConfig, smtp::message::send::SendMessageCommand}; +use crate::smtp::{account::SmtpAccount, message::send::SendMessageCommand}; /// Manage messages. /// @@ -15,9 +15,9 @@ pub enum MessageCommand { } impl MessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: SmtpConfig) -> Result<()> { + pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> { match self { - Self::Send(cmd) => cmd.exec(printer, config), + Self::Send(cmd) => cmd.exec(printer, account), } } } diff --git a/src/smtp/message/send.rs b/src/smtp/message/send.rs index 9cd3beb3..0e842920 100644 --- a/src/smtp/message/send.rs +++ b/src/smtp/message/send.rs @@ -13,7 +13,7 @@ use io_stream::runtimes::std::handle; use mail_parser::{Addr, Address, HeaderName, HeaderValue, MessageParser}; use pimalaya_toolbox::terminal::printer::{Message, Printer}; -use crate::{config::SmtpConfig, smtp::stream}; +use crate::smtp::{account::SmtpAccount, stream}; /// Send a message to a mailbox. /// @@ -28,8 +28,8 @@ pub struct SendMessageCommand { } impl SendMessageCommand { - pub fn exec(self, printer: &mut impl Printer, config: SmtpConfig) -> Result<()> { - let (context, mut stream) = stream::connect(config)?; + pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> { + let (context, mut stream) = stream::connect(account.backend)?; let message = if stdin().is_terminal() || printer.is_json() { self.message diff --git a/src/smtp/mod.rs b/src/smtp/mod.rs index 10877122..1d207ba4 100644 --- a/src/smtp/mod.rs +++ b/src/smtp/mod.rs @@ -1,3 +1,4 @@ +pub mod account; pub mod command; pub mod message; pub mod stream;