From cac8280c8c21fe759d81c2bbfbd397b21d543a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Mon, 3 Jul 2023 23:08:01 +0200 Subject: [PATCH] use tokio async runtime last fixes before merge --- Cargo.lock | 31 +++- Cargo.toml | 9 +- src/cache/id_mapper.rs | 6 +- src/config/config.rs | 17 +- src/config/prelude.rs | 15 +- src/domain/account/accounts.rs | 2 +- src/domain/account/args.rs | 2 +- src/domain/account/config.rs | 11 +- src/domain/account/handlers.rs | 52 ++++-- src/domain/backend/imap/handlers.rs | 10 +- src/domain/backend/imap/wizard.rs | 4 +- src/domain/backend/maildir/wizard.rs | 2 +- src/domain/backend/wizard.rs | 2 +- src/domain/email/handlers.rs | 124 +++++++------ src/domain/envelope/envelopes.rs | 36 ++-- src/domain/flag/args.rs | 2 +- src/domain/flag/flag.rs | 17 +- src/domain/flag/flags.rs | 4 +- src/domain/flag/handlers.rs | 14 +- src/domain/folder/folder.rs | 8 +- src/domain/folder/folders.rs | 7 +- src/domain/folder/handlers.rs | 129 ++++++++++---- src/domain/sender/sendmail/wizard.rs | 2 +- src/domain/sender/smtp/wizard.rs | 4 +- src/domain/sender/wizard.rs | 2 +- src/domain/tpl/handlers.rs | 63 ++++--- src/main.rs | 256 +++++++++++++++++---------- src/printer/print.rs | 2 +- src/printer/print_table.rs | 2 +- src/ui/editor.rs | 32 ++-- src/ui/table/table.rs | 3 +- 31 files changed, 538 insertions(+), 332 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6bde53..1225a9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1218,6 +1218,7 @@ name = "himalaya" version = "0.8.1" dependencies = [ "anyhow", + "async-trait", "atty", "chrono", "clap", @@ -2199,12 +2200,12 @@ dependencies = [ [[package]] name = "pimalaya-email" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8375dc804686e9e03a8b6f87d80b9038b5a734efb4b77f81b90b07b8ac1d4499" +version = "0.13.0" +source = "git+https://git.sr.ht/~soywod/pimalaya#5250a4c906228da7f2d1ca1d58468a760c69c720" dependencies = [ "advisory-lock", "ammonia", + "async-trait", "chrono", "convert_case", "dirs", @@ -2244,10 +2245,11 @@ dependencies = [ [[package]] name = "pimalaya-email-tpl" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a838dd91468bf79997ead555d6a5b93f066e363259925ec931d49591b1ebb79" +checksum = "c536455f778ed7aa0948b755281e8e6a61650a857a3f610ff7e60b097a681838" dependencies = [ + "async-recursion", "chumsky 0.9.0", "log", "mail-builder", @@ -2285,19 +2287,20 @@ dependencies = [ [[package]] name = "pimalaya-process" -version = "0.0.2" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8d2853bf2f0efbe397ce22bb4e6d53a7464f6dcc0abe7e2936f6f4e8e2726a" +checksum = "1a9d1e1ab6334d4e4d06613cd65f3fed34e5d2d7b1488f3d1a0d2fdffdba5d1c" dependencies = [ "log", "thiserror", + "tokio", ] [[package]] name = "pimalaya-secret" -version = "0.0.2" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9338dc84e5ec9fc25f3a36d82ed68ffe4888ad728ca1aa42228e27648ddff01f" +checksum = "0f975cafe977327bb215cf71e953dd1f1bfa45d3cf4ab5e806493d917e0a0cd1" dependencies = [ "log", "pimalaya-keyring", @@ -2925,6 +2928,15 @@ dependencies = [ "dirs", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -3181,6 +3193,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.42.0", diff --git a/Cargo.toml b/Cargo.toml index 66c021c..b370d5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ notmuch-backend = ["pimalaya-email/notmuch-backend"] smtp-sender = ["pimalaya-email/smtp-sender"] [dev-dependencies] +async-trait = "0.1" tempfile = "3.3" [dependencies] @@ -44,17 +45,17 @@ indicatif = "0.17" log = "0.4" md5 = "0.7.0" once_cell = "1.16.0" -pimalaya-email = { version = "=0.11.0", default-features = false } +pimalaya-email = { git = "https://git.sr.ht/~soywod/pimalaya", default-features = false } pimalaya-keyring = "=0.0.4" pimalaya-oauth2 = "=0.0.3" -pimalaya-process = "=0.0.2" -pimalaya-secret = "=0.0.2" +pimalaya-process = "=0.0.5" +pimalaya-secret = "=0.0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" shellexpand = "2.1" termcolor = "1.1" terminal_size = "0.1" -tokio = { version = "1.23", default-features = false, features = ["macros"] } +tokio = { version = "1.23", default-features = false, features = ["macros", "rt-multi-thread"] } toml = "0.7.4" toml_edit = "0.19.8" unicode-width = "0.1" diff --git a/src/cache/id_mapper.rs b/src/cache/id_mapper.rs index f770e0b..aa96b70 100644 --- a/src/cache/id_mapper.rs +++ b/src/cache/id_mapper.rs @@ -1,10 +1,10 @@ use anyhow::{anyhow, Context, Result}; use log::{debug, trace}; #[cfg(feature = "imap-backend")] -use pimalaya_email::ImapBackend; +use pimalaya_email::backend::ImapBackend; #[cfg(feature = "notmuch-backend")] -use pimalaya_email::NotmuchBackend; -use pimalaya_email::{Backend, MaildirBackend}; +use pimalaya_email::backend::NotmuchBackend; +use pimalaya_email::backend::{Backend, MaildirBackend}; use std::path::{Path, PathBuf}; const ID_MAPPER_DB_FILE_NAME: &str = ".id-mapper.sqlite"; diff --git a/src/config/config.rs b/src/config/config.rs index 660aa24..1949183 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -7,7 +7,10 @@ use anyhow::{anyhow, Context, Result}; use dialoguer::Confirm; use dirs::{config_dir, home_dir}; use log::{debug, trace}; -use pimalaya_email::{AccountConfig, EmailHooks, EmailTextPlainFormat}; +use pimalaya_email::{ + account::AccountConfig, + email::{EmailHooks, EmailTextPlainFormat}, +}; use pimalaya_process::Cmd; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fs, path::PathBuf, process}; @@ -158,16 +161,18 @@ impl DeserializedConfig { #[cfg(test)] mod tests { use pimalaya_email::{ - BackendConfig, MaildirConfig, PasswdConfig, SenderConfig, SendmailConfig, + account::PasswdConfig, + backend::{BackendConfig, MaildirConfig}, + sender::{SenderConfig, SendmailConfig}, }; use pimalaya_secret::Secret; #[cfg(feature = "notmuch-backend")] - use pimalaya_email::NotmuchConfig; + use pimalaya_email::backend::NotmuchConfig; #[cfg(feature = "imap-backend")] - use pimalaya_email::{ImapAuthConfig, ImapConfig}; + use pimalaya_email::backend::{ImapAuthConfig, ImapConfig}; #[cfg(feature = "smtp-sender")] - use pimalaya_email::{SmtpAuthConfig, SmtpConfig}; + use pimalaya_email::sender::{SmtpAuthConfig, SmtpConfig}; use std::io::Write; use tempfile::NamedTempFile; @@ -453,7 +458,7 @@ mod tests { #[cfg(feature = "smtp-sender")] #[test] fn account_smtp_sender_minimum_config() { - use pimalaya_email::SenderConfig; + use pimalaya_email::sender::SenderConfig; let config = make_config( "[account] diff --git a/src/config/prelude.rs b/src/config/prelude.rs index d0cfb2e..ce5631d 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -1,13 +1,16 @@ +#[cfg(feature = "imap-backend")] +use pimalaya_email::backend::{ImapAuthConfig, ImapConfig}; +#[cfg(feature = "smtp-sender")] +use pimalaya_email::sender::{SmtpAuthConfig, SmtpConfig}; #[cfg(feature = "notmuch-backend")] use pimalaya_email::NotmuchConfig; use pimalaya_email::{ - BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy, MaildirConfig, - OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SendmailConfig, + account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig}, + backend::{BackendConfig, MaildirConfig}, + email::{EmailHooks, EmailTextPlainFormat}, + folder::sync::FolderSyncStrategy, + sender::{SenderConfig, SendmailConfig}, }; -#[cfg(feature = "imap-backend")] -use pimalaya_email::{ImapAuthConfig, ImapConfig}; -#[cfg(feature = "smtp-sender")] -use pimalaya_email::{SmtpAuthConfig, SmtpConfig}; use pimalaya_keyring::Entry; use pimalaya_process::{Cmd, Pipeline, SingleCmd}; use pimalaya_secret::Secret; diff --git a/src/domain/account/accounts.rs b/src/domain/account/accounts.rs index dfec101..4da4d6e 100644 --- a/src/domain/account/accounts.rs +++ b/src/domain/account/accounts.rs @@ -5,7 +5,7 @@ //! accounts from the config file. use anyhow::Result; -use pimalaya_email::BackendConfig; +use pimalaya_email::backend::BackendConfig; use serde::Serialize; use std::{collections::hash_map::Iter, ops::Deref}; diff --git a/src/domain/account/args.rs b/src/domain/account/args.rs index baa4507..b043df9 100644 --- a/src/domain/account/args.rs +++ b/src/domain/account/args.rs @@ -3,7 +3,7 @@ use anyhow::Result; use clap::{Arg, ArgAction, ArgMatches, Command}; use log::info; -use pimalaya_email::FolderSyncStrategy; +use pimalaya_email::folder::sync::FolderSyncStrategy; use std::collections::HashSet; use crate::{folder, ui::table}; diff --git a/src/domain/account/config.rs b/src/domain/account/config.rs index 3ebfd1d..7673891 100644 --- a/src/domain/account/config.rs +++ b/src/domain/account/config.rs @@ -4,12 +4,15 @@ //! account in the accounts section of the user configuration file. #[cfg(feature = "imap-backend")] -use pimalaya_email::ImapAuthConfig; +use pimalaya_email::backend::ImapAuthConfig; #[cfg(feature = "smtp-sender")] -use pimalaya_email::SmtpAuthConfig; +use pimalaya_email::sender::SmtpAuthConfig; use pimalaya_email::{ - AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy, - SenderConfig, + account::AccountConfig, + backend::BackendConfig, + email::{EmailHooks, EmailTextPlainFormat}, + folder::sync::FolderSyncStrategy, + sender::SenderConfig, }; use pimalaya_process::Cmd; use serde::{Deserialize, Serialize}; diff --git a/src/domain/account/handlers.rs b/src/domain/account/handlers.rs index e575c17..be8d5d6 100644 --- a/src/domain/account/handlers.rs +++ b/src/domain/account/handlers.rs @@ -7,11 +7,16 @@ use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle}; use log::{info, trace, warn}; use once_cell::sync::Lazy; #[cfg(feature = "imap-backend")] -use pimalaya_email::ImapAuthConfig; +use pimalaya_email::backend::ImapAuthConfig; #[cfg(feature = "smtp-sender")] -use pimalaya_email::SmtpAuthConfig; +use pimalaya_email::sender::SmtpAuthConfig; use pimalaya_email::{ - AccountConfig, BackendConfig, BackendSyncBuilder, BackendSyncProgressEvent, SenderConfig, + account::{ + sync::{AccountSyncBuilder, AccountSyncProgressEvent}, + AccountConfig, + }, + backend::BackendConfig, + sender::SenderConfig, }; use std::{collections::HashMap, sync::Mutex}; @@ -40,7 +45,7 @@ const SUB_PROGRESS_DONE_STYLE: Lazy = Lazy::new(|| { }); /// Configure the current selected account -pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> { +pub async fn configure(config: &AccountConfig, reset: bool) -> Result<()> { info!("entering the configure account handler"); if reset { @@ -72,9 +77,13 @@ pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> { #[cfg(feature = "imap-backend")] if let BackendConfig::Imap(imap_config) = &config.backend { match &imap_config.auth { - ImapAuthConfig::Passwd(passwd) => passwd.configure(|| prompt_passwd("IMAP password")), + ImapAuthConfig::Passwd(passwd) => { + passwd.configure(|| prompt_passwd("IMAP password")).await + } ImapAuthConfig::OAuth2(oauth2) => { - oauth2.configure(|| prompt_secret("IMAP OAuth 2.0 client secret")) + oauth2 + .configure(|| prompt_secret("IMAP OAuth 2.0 client secret")) + .await } }?; } @@ -82,9 +91,13 @@ pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> { #[cfg(feature = "smtp-sender")] if let SenderConfig::Smtp(smtp_config) = &config.sender { match &smtp_config.auth { - SmtpAuthConfig::Passwd(passwd) => passwd.configure(|| prompt_passwd("SMTP password")), + SmtpAuthConfig::Passwd(passwd) => { + passwd.configure(|| prompt_passwd("SMTP password")).await + } SmtpAuthConfig::OAuth2(oauth2) => { - oauth2.configure(|| prompt_secret("SMTP OAuth 2.0 client secret")) + oauth2 + .configure(|| prompt_secret("SMTP OAuth 2.0 client secret")) + .await } }?; } @@ -123,16 +136,16 @@ pub fn list<'a, P: Printer>( /// Synchronizes the account defined using argument `-a|--account`. If /// no account given, synchronizes the default one. -pub fn sync<'a, P: Printer>( +pub async fn sync( printer: &mut P, - sync_builder: BackendSyncBuilder<'a>, + sync_builder: AccountSyncBuilder, dry_run: bool, ) -> Result<()> { info!("entering the sync accounts handler"); trace!("dry run: {dry_run}"); if dry_run { - let report = sync_builder.sync()?; + let report = sync_builder.sync().await?; let mut hunks_count = report.folders_patch.len(); if !report.folders_patch.is_empty() { @@ -143,9 +156,9 @@ pub fn sync<'a, P: Printer>( printer.print_log("")?; } - if !report.envelopes_patch.is_empty() { + if !report.emails_patch.is_empty() { printer.print_log("Envelopes patch:")?; - for (hunk, _) in report.envelopes_patch { + for (hunk, _) in report.emails_patch { hunks_count += 1; printer.print_log(format!(" - {hunk}"))?; } @@ -156,7 +169,7 @@ pub fn sync<'a, P: Printer>( "Estimated patch length for account to be synchronized: {hunks_count}", ))?; } else if printer.is_json() { - sync_builder.sync()?; + sync_builder.sync().await?; printer.print("Account successfully synchronized!")?; } else { let multi = MultiProgress::new(); @@ -172,7 +185,7 @@ pub fn sync<'a, P: Printer>( let report = sync_builder .with_on_progress(move |evt| { - use BackendSyncProgressEvent::*; + use AccountSyncProgressEvent::*; Ok(match evt { ApplyFolderPatches(..) => { main_progress.inc(3); @@ -223,7 +236,8 @@ pub fn sync<'a, P: Printer>( _ => (), }) }) - .sync()?; + .sync() + .await?; let folders_patch_err = report .folders_patch @@ -246,7 +260,7 @@ pub fn sync<'a, P: Printer>( } let envelopes_patch_err = report - .envelopes_patch + .emails_patch .iter() .filter_map(|(hunk, err)| err.as_ref().map(|err| (hunk, err))) .collect::>(); @@ -258,7 +272,7 @@ pub fn sync<'a, P: Printer>( } } - if let Some(err) = report.envelopes_cache_patch.1 { + if let Some(err) = report.emails_cache_patch.1 { printer.print_log("")?; printer.print_log(format!( "Error occurred while applying the envelopes cache patch: {err}" @@ -273,7 +287,7 @@ pub fn sync<'a, P: Printer>( #[cfg(test)] mod tests { - use pimalaya_email::{AccountConfig, ImapConfig}; + use pimalaya_email::{account::AccountConfig, backend::ImapConfig}; use std::{collections::HashMap, fmt::Debug, io}; use termcolor::ColorSpec; diff --git a/src/domain/backend/imap/handlers.rs b/src/domain/backend/imap/handlers.rs index 1a5b9f4..7c4ee11 100644 --- a/src/domain/backend/imap/handlers.rs +++ b/src/domain/backend/imap/handlers.rs @@ -3,14 +3,14 @@ //! This module gathers all IMAP handlers triggered by the CLI. use anyhow::Result; -use pimalaya_email::ImapBackend; +use pimalaya_email::backend::ImapBackend; -pub fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { - imap.notify(keepalive, folder)?; +pub async fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { + imap.notify(keepalive, folder).await?; Ok(()) } -pub fn watch(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { - imap.watch(keepalive, folder)?; +pub async fn watch(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { + imap.watch(keepalive, folder).await?; Ok(()) } diff --git a/src/domain/backend/imap/wizard.rs b/src/domain/backend/imap/wizard.rs index 32162c3..a5f21ee 100644 --- a/src/domain/backend/imap/wizard.rs +++ b/src/domain/backend/imap/wizard.rs @@ -1,8 +1,8 @@ use anyhow::Result; use dialoguer::{Confirm, Input, Password, Select}; use pimalaya_email::{ - BackendConfig, ImapAuthConfig, ImapConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, - PasswdConfig, + account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig}, + backend::{BackendConfig, ImapAuthConfig, ImapConfig}, }; use pimalaya_oauth2::{AuthorizationCodeGrant, Client}; use pimalaya_secret::Secret; diff --git a/src/domain/backend/maildir/wizard.rs b/src/domain/backend/maildir/wizard.rs index e0ac51e..d127ba7 100644 --- a/src/domain/backend/maildir/wizard.rs +++ b/src/domain/backend/maildir/wizard.rs @@ -1,7 +1,7 @@ use anyhow::Result; use dialoguer::Input; use dirs::home_dir; -use pimalaya_email::{BackendConfig, MaildirConfig}; +use pimalaya_email::backend::{BackendConfig, MaildirConfig}; use crate::config::wizard::THEME; diff --git a/src/domain/backend/wizard.rs b/src/domain/backend/wizard.rs index d8840b2..e8168ef 100644 --- a/src/domain/backend/wizard.rs +++ b/src/domain/backend/wizard.rs @@ -1,6 +1,6 @@ use anyhow::Result; use dialoguer::Select; -use pimalaya_email::BackendConfig; +use pimalaya_email::backend::BackendConfig; use crate::config::wizard::THEME; diff --git a/src/domain/email/handlers.rs b/src/domain/email/handlers.rs index 11cdef2..09ca8e5 100644 --- a/src/domain/email/handlers.rs +++ b/src/domain/email/handlers.rs @@ -2,7 +2,10 @@ use anyhow::{anyhow, Context, Result}; use atty::Stream; use log::{debug, trace}; use pimalaya_email::{ - AccountConfig, Backend, Email, EmailBuilder, FilterParts, Flag, Flags, Sender, + account::AccountConfig, + backend::Backend, + email::{template::FilterParts, Flag, Flags, Message, MessageBuilder}, + sender::Sender, }; use std::{ fs, @@ -17,7 +20,7 @@ use crate::{ Envelopes, IdMapper, }; -pub fn attachments( +pub async fn attachments( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -28,7 +31,7 @@ pub fn attachments( let folder = config.folder_alias(folder)?; let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - let emails = backend.get_emails(&folder, ids.clone())?; + let emails = backend.get_emails(&folder, ids.clone()).await?; let mut index = 0; let mut emails_count = 0; @@ -57,7 +60,7 @@ pub fn attachments( let filename = attachment .filename .unwrap_or_else(|| Uuid::new_v4().to_string()); - let filepath = config.get_download_file_path(&filename)?; + let filepath = config.download_fpath(&filename)?; printer.print_log(format!("Downloading {:?}…", filepath))?; fs::write(&filepath, &attachment.body).context("cannot download attachment")?; attachments_count = attachments_count + 1; @@ -74,7 +77,7 @@ pub fn attachments( } } -pub fn copy( +pub async fn copy( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -87,11 +90,11 @@ pub fn copy( let to_folder = config.folder_alias(to_folder)?; let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.copy_emails(&from_folder, &to_folder, ids)?; + backend.copy_emails(&from_folder, &to_folder, ids).await?; printer.print("Email(s) successfully copied!") } -pub fn delete( +pub async fn delete( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -102,11 +105,11 @@ pub fn delete( let folder = config.folder_alias(folder)?; let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.delete_emails(&folder, ids)?; + backend.delete_emails(&folder, ids).await?; printer.print("Email(s) successfully deleted!") } -pub fn forward( +pub async fn forward( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -123,19 +126,21 @@ pub fn forward( let ids = ids.iter().map(String::as_str).collect::>(); let tpl = backend - .get_emails(&folder, ids)? + .get_emails(&folder, ids) + .await? .first() .ok_or_else(|| anyhow!("cannot find email {}", id))? .to_forward_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .build()?; + .with_some_headers(headers) + .with_some_body(body) + .build() + .await?; trace!("initial template: {}", *tpl); - editor::edit_tpl_with_editor(config, printer, backend, sender, tpl)?; + editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?; Ok(()) } -pub fn list( +pub async fn list( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -152,7 +157,7 @@ pub fn list( let envelopes = Envelopes::from_backend( config, id_mapper, - backend.list_envelopes(&folder, page_size, page)?, + backend.list_envelopes(&folder, page_size, page).await?, )?; trace!("envelopes: {:?}", envelopes); @@ -168,14 +173,14 @@ pub fn list( /// Parses and edits a message from a [mailto] URL string. /// /// [mailto]: https://en.wikipedia.org/wiki/Mailto -pub fn mailto( +pub async fn mailto( config: &AccountConfig, backend: &mut dyn Backend, sender: &mut dyn Sender, printer: &mut P, url: &Url, ) -> Result<()> { - let mut builder = EmailBuilder::new().to(url.path()); + let mut builder = MessageBuilder::new().to(url.path()); for (key, val) in url.query_pairs() { match key.to_lowercase().as_bytes() { @@ -190,12 +195,13 @@ pub fn mailto( let tpl = config .generate_tpl_interpreter() .show_only_headers(config.email_writing_headers()) - .interpret_msg_builder(builder)?; + .interpret_msg_builder(builder) + .await?; - editor::edit_tpl_with_editor(config, printer, backend, sender, tpl) + editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await } -pub fn move_( +pub async fn move_( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -208,11 +214,11 @@ pub fn move_( let to_folder = config.folder_alias(to_folder)?; let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.move_emails(&from_folder, &to_folder, ids)?; + backend.move_emails(&from_folder, &to_folder, ids).await?; printer.print("Email(s) successfully moved!") } -pub fn read( +pub async fn read( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -226,7 +232,7 @@ pub fn read( let folder = config.folder_alias(folder)?; let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - let emails = backend.get_emails(&folder, ids)?; + let emails = backend.get_emails(&folder, ids).await?; let mut glue = ""; let mut bodies = String::default(); @@ -245,7 +251,8 @@ pub fn read( .hide_all_headers() .filter_parts(FilterParts::Only("text/html".into())), _ => tpl.show_additional_headers(&headers), - })? + }) + .await? .into(); bodies.push_str(&tpl); } @@ -256,7 +263,7 @@ pub fn read( printer.print(bodies) } -pub fn reply( +pub async fn reply( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -274,21 +281,25 @@ pub fn reply( let ids = ids.iter().map(String::as_str).collect::>(); let tpl = backend - .get_emails(&folder, ids)? + .get_emails(&folder, ids) + .await? .first() .ok_or_else(|| anyhow!("cannot find email {}", id))? .to_reply_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .reply_all(all) - .build()?; + .with_some_headers(headers) + .with_some_body(body) + .with_reply_all(all) + .build() + .await?; trace!("initial template: {}", *tpl); - editor::edit_tpl_with_editor(config, printer, backend, sender, tpl)?; - backend.add_flags(&folder, vec![id], &Flags::from_iter([Flag::Answered]))?; + editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?; + backend + .add_flags(&folder, vec![id], &Flags::from_iter([Flag::Answered])) + .await?; Ok(()) } -pub fn save( +pub async fn save( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -310,13 +321,15 @@ pub fn save( .join("\r\n") }; - let id = backend.add_email(&folder, raw_email.as_bytes(), &Flags::default())?; + let id = backend + .add_email(&folder, raw_email.as_bytes(), &Flags::default()) + .await?; id_mapper.create_alias(id)?; Ok(()) } -pub fn search( +pub async fn search( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -332,7 +345,9 @@ pub fn search( let envelopes = Envelopes::from_backend( config, id_mapper, - backend.search_envelopes(&folder, &query, "", page_size, page)?, + backend + .search_envelopes(&folder, &query, "", page_size, page) + .await?, )?; let opts = PrintTableOpts { format: &config.email_reading_format, @@ -342,7 +357,7 @@ pub fn search( printer.print_table(Box::new(envelopes), opts) } -pub fn sort( +pub async fn sort( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -359,7 +374,9 @@ pub fn sort( let envelopes = Envelopes::from_backend( config, id_mapper, - backend.search_envelopes(&folder, &query, &sort, page_size, page)?, + backend + .search_envelopes(&folder, &query, &sort, page_size, page) + .await?, )?; let opts = PrintTableOpts { format: &config.email_reading_format, @@ -369,7 +386,7 @@ pub fn sort( printer.print_table(Box::new(envelopes), opts) } -pub fn send( +pub async fn send( config: &AccountConfig, printer: &mut P, backend: &mut dyn Backend, @@ -390,18 +407,20 @@ pub fn send( .join("\r\n") }; trace!("raw email: {:?}", raw_email); - sender.send(raw_email.as_bytes())?; + sender.send(raw_email.as_bytes()).await?; if config.email_sending_save_copy { - backend.add_email( - &folder, - raw_email.as_bytes(), - &Flags::from_iter([Flag::Seen]), - )?; + backend + .add_email( + &folder, + raw_email.as_bytes(), + &Flags::from_iter([Flag::Seen]), + ) + .await?; } Ok(()) } -pub fn write( +pub async fn write( config: &AccountConfig, printer: &mut P, backend: &mut dyn Backend, @@ -409,11 +428,12 @@ pub fn write( headers: Option>, body: Option<&str>, ) -> Result<()> { - let tpl = Email::new_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .build()?; + let tpl = Message::new_tpl_builder(config) + .with_some_headers(headers) + .with_some_body(body) + .build() + .await?; trace!("initial template: {}", *tpl); - editor::edit_tpl_with_editor(config, printer, backend, sender, tpl)?; + editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?; Ok(()) } diff --git a/src/domain/envelope/envelopes.rs b/src/domain/envelope/envelopes.rs index dc98a23..cc4b031 100644 --- a/src/domain/envelope/envelopes.rs +++ b/src/domain/envelope/envelopes.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use pimalaya_email::AccountConfig; +use pimalaya_email::account::AccountConfig; use serde::Serialize; use std::ops; @@ -17,7 +17,7 @@ impl Envelopes { pub fn from_backend( config: &AccountConfig, id_mapper: &IdMapper, - envelopes: pimalaya_email::Envelopes, + envelopes: pimalaya_email::email::Envelopes, ) -> Result { let envelopes = envelopes .iter() @@ -58,10 +58,9 @@ impl PrintTable for Envelopes { #[cfg(test)] mod tests { - use std::env; - use chrono::DateTime; - use pimalaya_email::AccountConfig; + use pimalaya_email::account::AccountConfig; + use std::env; use crate::{Envelopes, IdMapper}; @@ -70,10 +69,11 @@ mod tests { let config = AccountConfig::default(); let id_mapper = IdMapper::Dummy; - let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { - date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), - ..Default::default() - }]); + let envelopes = + pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope { + date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), + ..Default::default() + }]); let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap(); let expected_date = "2023-06-15 09:42+04:00"; @@ -90,10 +90,11 @@ mod tests { ..AccountConfig::default() }; - let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { - date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), - ..Default::default() - }]); + let envelopes = + pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope { + date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), + ..Default::default() + }]); let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap(); let expected_date = "15/06/2023 09h42"; @@ -113,10 +114,11 @@ mod tests { ..AccountConfig::default() }; - let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { - date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), - ..Default::default() - }]); + let envelopes = + pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope { + date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), + ..Default::default() + }]); let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap(); let expected_date = "15/06/2023 05h42"; diff --git a/src/domain/flag/args.rs b/src/domain/flag/args.rs index 32fe191..56cfd48 100644 --- a/src/domain/flag/args.rs +++ b/src/domain/flag/args.rs @@ -6,7 +6,7 @@ use anyhow::Result; use clap::{Arg, ArgMatches, Command}; use log::{debug, info}; -use pimalaya_email::{Flag, Flags}; +use pimalaya_email::email::{Flag, Flags}; use crate::email; diff --git a/src/domain/flag/flag.rs b/src/domain/flag/flag.rs index daea47c..e326814 100644 --- a/src/domain/flag/flag.rs +++ b/src/domain/flag/flag.rs @@ -11,15 +11,16 @@ pub enum Flag { Custom(String), } -impl From<&pimalaya_email::Flag> for Flag { - fn from(flag: &pimalaya_email::Flag) -> Self { +impl From<&pimalaya_email::email::Flag> for Flag { + fn from(flag: &pimalaya_email::email::Flag) -> Self { + use pimalaya_email::email::Flag::*; match flag { - pimalaya_email::Flag::Seen => Flag::Seen, - pimalaya_email::Flag::Answered => Flag::Answered, - pimalaya_email::Flag::Flagged => Flag::Flagged, - pimalaya_email::Flag::Deleted => Flag::Deleted, - pimalaya_email::Flag::Draft => Flag::Draft, - pimalaya_email::Flag::Custom(flag) => Flag::Custom(flag.clone()), + Seen => Flag::Seen, + Answered => Flag::Answered, + Flagged => Flag::Flagged, + Deleted => Flag::Deleted, + Draft => Flag::Draft, + Custom(flag) => Flag::Custom(flag.clone()), } } } diff --git a/src/domain/flag/flags.rs b/src/domain/flag/flags.rs index 534cb97..55f38a4 100644 --- a/src/domain/flag/flags.rs +++ b/src/domain/flag/flags.rs @@ -14,8 +14,8 @@ impl ops::Deref for Flags { } } -impl From for Flags { - fn from(flags: pimalaya_email::Flags) -> Self { +impl From for Flags { + fn from(flags: pimalaya_email::email::Flags) -> Self { Flags(flags.iter().map(Flag::from).collect()) } } diff --git a/src/domain/flag/handlers.rs b/src/domain/flag/handlers.rs index 7381b69..13f53b8 100644 --- a/src/domain/flag/handlers.rs +++ b/src/domain/flag/handlers.rs @@ -1,9 +1,9 @@ use anyhow::Result; -use pimalaya_email::{Backend, Flags}; +use pimalaya_email::{backend::Backend, email::Flags}; use crate::{printer::Printer, IdMapper}; -pub fn add( +pub async fn add( printer: &mut P, id_mapper: &IdMapper, backend: &mut dyn Backend, @@ -13,11 +13,11 @@ pub fn add( ) -> Result<()> { let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.add_flags(folder, ids, flags)?; + backend.add_flags(folder, ids, flags).await?; printer.print("Flag(s) successfully added!") } -pub fn set( +pub async fn set( printer: &mut P, id_mapper: &IdMapper, backend: &mut dyn Backend, @@ -27,11 +27,11 @@ pub fn set( ) -> Result<()> { let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.set_flags(folder, ids, flags)?; + backend.set_flags(folder, ids, flags).await?; printer.print("Flag(s) successfully set!") } -pub fn remove( +pub async fn remove( printer: &mut P, id_mapper: &IdMapper, backend: &mut dyn Backend, @@ -41,6 +41,6 @@ pub fn remove( ) -> Result<()> { let ids = id_mapper.get_ids(ids)?; let ids = ids.iter().map(String::as_str).collect::>(); - backend.remove_flags(folder, ids, flags)?; + backend.remove_flags(folder, ids, flags).await?; printer.print("Flag(s) successfully removed!") } diff --git a/src/domain/folder/folder.rs b/src/domain/folder/folder.rs index 0aebace..4b49bbc 100644 --- a/src/domain/folder/folder.rs +++ b/src/domain/folder/folder.rs @@ -4,15 +4,13 @@ use crate::ui::{Cell, Row, Table}; #[derive(Clone, Debug, Default, Serialize)] pub struct Folder { - pub delim: String, pub name: String, pub desc: String, } -impl From<&pimalaya_email::Folder> for Folder { - fn from(folder: &pimalaya_email::Folder) -> Self { +impl From<&pimalaya_email::folder::Folder> for Folder { + fn from(folder: &pimalaya_email::folder::Folder) -> Self { Folder { - delim: folder.delim.clone(), name: folder.name.clone(), desc: folder.desc.clone(), } @@ -22,14 +20,12 @@ impl From<&pimalaya_email::Folder> for Folder { impl Table for Folder { fn head() -> Row { Row::new() - .cell(Cell::new("DELIM").bold().underline().white()) .cell(Cell::new("NAME").bold().underline().white()) .cell(Cell::new("DESC").bold().underline().white()) } fn row(&self) -> Row { Row::new() - .cell(Cell::new(&self.delim).white()) .cell(Cell::new(&self.name).blue()) .cell(Cell::new(&self.desc).green()) } diff --git a/src/domain/folder/folders.rs b/src/domain/folder/folders.rs index d3335ae..d53e37c 100644 --- a/src/domain/folder/folders.rs +++ b/src/domain/folder/folders.rs @@ -1,7 +1,6 @@ -use std::ops; - use anyhow::Result; use serde::Serialize; +use std::ops; use crate::{ printer::{PrintTable, PrintTableOpts, WriteColor}, @@ -20,8 +19,8 @@ impl ops::Deref for Folders { } } -impl From for Folders { - fn from(folders: pimalaya_email::Folders) -> Self { +impl From for Folders { + fn from(folders: pimalaya_email::folder::Folders) -> Self { Folders(folders.iter().map(Folder::from).collect()) } } diff --git a/src/domain/folder/handlers.rs b/src/domain/folder/handlers.rs index 7a1bc8e..7a76cfb 100644 --- a/src/domain/folder/handlers.rs +++ b/src/domain/folder/handlers.rs @@ -4,7 +4,7 @@ use anyhow::Result; use dialoguer::Confirm; -use pimalaya_email::{AccountConfig, Backend}; +use pimalaya_email::{account::AccountConfig, backend::Backend}; use std::process; use crate::{ @@ -12,18 +12,22 @@ use crate::{ Folders, }; -pub fn expunge(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { - backend.expunge_folder(folder)?; +pub async fn expunge( + printer: &mut P, + backend: &mut dyn Backend, + folder: &str, +) -> Result<()> { + backend.expunge_folder(folder).await?; printer.print(format!("Folder {folder} successfully expunged!")) } -pub fn list( +pub async fn list( config: &AccountConfig, printer: &mut P, backend: &mut dyn Backend, max_width: Option, ) -> Result<()> { - let folders: Folders = backend.list_folders()?.into(); + let folders: Folders = backend.list_folders().await?.into(); printer.print_table( // TODO: remove Box Box::new(folders), @@ -34,12 +38,20 @@ pub fn list( ) } -pub fn create(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { - backend.add_folder(folder)?; +pub async fn create( + printer: &mut P, + backend: &mut dyn Backend, + folder: &str, +) -> Result<()> { + backend.add_folder(folder).await?; printer.print("Folder successfully created!") } -pub fn delete(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { +pub async fn delete( + printer: &mut P, + backend: &mut dyn Backend, + folder: &str, +) -> Result<()> { if let Some(false) | None = Confirm::new() .with_prompt(format!("Confirm deletion of folder {folder}?")) .default(false) @@ -49,14 +61,18 @@ pub fn delete(printer: &mut P, backend: &mut dyn Backend, folder: &s process::exit(0); }; - backend.delete_folder(folder)?; + backend.delete_folder(folder).await?; printer.print("Folder successfully deleted!") } #[cfg(test)] mod tests { + use async_trait::async_trait; use pimalaya_email::{ - backend, AccountConfig, Backend, Emails, Envelope, Envelopes, Flags, Folder, Folders, + account::AccountConfig, + backend::Backend, + email::{Envelope, Envelopes, Flags, Messages}, + folder::{Folder, Folders}, }; use std::{any::Any, fmt::Debug, io}; use termcolor::ColorSpec; @@ -65,8 +81,8 @@ mod tests { use super::*; - #[test] - fn it_should_list_mboxes() { + #[tokio::test] + async fn it_should_list_mboxes() { #[derive(Debug, Default, Clone)] struct StringWriter { content: String, @@ -131,82 +147,119 @@ mod tests { struct TestBackend; + #[async_trait] impl Backend for TestBackend { fn name(&self) -> String { unimplemented!(); } - fn add_folder(&mut self, _: &str) -> backend::Result<()> { + async fn add_folder(&mut self, _: &str) -> pimalaya_email::Result<()> { unimplemented!(); } - fn list_folders(&mut self) -> backend::Result { + async fn list_folders(&mut self) -> pimalaya_email::Result { Ok(Folders::from_iter([ Folder { - delim: "/".into(), name: "INBOX".into(), desc: "desc".into(), }, Folder { - delim: "/".into(), name: "Sent".into(), desc: "desc".into(), }, ])) } - fn expunge_folder(&mut self, _: &str) -> backend::Result<()> { + async fn expunge_folder(&mut self, _: &str) -> pimalaya_email::Result<()> { unimplemented!(); } - fn purge_folder(&mut self, _: &str) -> backend::Result<()> { + async fn purge_folder(&mut self, _: &str) -> pimalaya_email::Result<()> { unimplemented!(); } - fn delete_folder(&mut self, _: &str) -> backend::Result<()> { + async fn delete_folder(&mut self, _: &str) -> pimalaya_email::Result<()> { unimplemented!(); } - fn get_envelope(&mut self, _: &str, _: &str) -> backend::Result { + async fn get_envelope(&mut self, _: &str, _: &str) -> pimalaya_email::Result { unimplemented!(); } - fn list_envelopes( + async fn list_envelopes( &mut self, _: &str, _: usize, _: usize, - ) -> backend::Result { + ) -> pimalaya_email::Result { unimplemented!() } - fn search_envelopes( + async fn search_envelopes( &mut self, _: &str, _: &str, _: &str, _: usize, _: usize, - ) -> backend::Result { + ) -> pimalaya_email::Result { unimplemented!() } - fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> backend::Result { + async fn add_email( + &mut self, + _: &str, + _: &[u8], + _: &Flags, + ) -> pimalaya_email::Result { unimplemented!() } - fn get_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result { + async fn get_emails( + &mut self, + _: &str, + _: Vec<&str>, + ) -> pimalaya_email::Result { unimplemented!() } - fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result { + async fn preview_emails( + &mut self, + _: &str, + _: Vec<&str>, + ) -> pimalaya_email::Result { unimplemented!() } - fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> { + async fn copy_emails( + &mut self, + _: &str, + _: &str, + _: Vec<&str>, + ) -> pimalaya_email::Result<()> { unimplemented!() } - fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> backend::Result<()> { + async fn move_emails( + &mut self, + _: &str, + _: &str, + _: Vec<&str>, + ) -> pimalaya_email::Result<()> { unimplemented!() } - fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<()> { + async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> pimalaya_email::Result<()> { unimplemented!() } - fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> { + async fn add_flags( + &mut self, + _: &str, + _: Vec<&str>, + _: &Flags, + ) -> pimalaya_email::Result<()> { unimplemented!() } - fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> { + async fn set_flags( + &mut self, + _: &str, + _: Vec<&str>, + _: &Flags, + ) -> pimalaya_email::Result<()> { unimplemented!() } - fn remove_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> backend::Result<()> { + async fn remove_flags( + &mut self, + _: &str, + _: Vec<&str>, + _: &Flags, + ) -> pimalaya_email::Result<()> { unimplemented!() } fn as_any(&self) -> &dyn Any { @@ -218,13 +271,15 @@ mod tests { let mut printer = PrinterServiceTest::default(); let mut backend = TestBackend {}; - assert!(list(&account_config, &mut printer, &mut backend, None).is_ok()); + assert!(list(&account_config, &mut printer, &mut backend, None) + .await + .is_ok()); assert_eq!( concat![ "\n", - "DELIM │NAME │DESC \n", - "/ │INBOX │desc \n", - "/ │Sent │desc \n", + "NAME │DESC \n", + "INBOX │desc \n", + "Sent │desc \n", "\n" ], printer.writer.content diff --git a/src/domain/sender/sendmail/wizard.rs b/src/domain/sender/sendmail/wizard.rs index 59994d3..5df724c 100644 --- a/src/domain/sender/sendmail/wizard.rs +++ b/src/domain/sender/sendmail/wizard.rs @@ -1,6 +1,6 @@ use anyhow::Result; use dialoguer::Input; -use pimalaya_email::{SenderConfig, SendmailConfig}; +use pimalaya_email::sender::{SenderConfig, SendmailConfig}; use crate::config::wizard::THEME; diff --git a/src/domain/sender/smtp/wizard.rs b/src/domain/sender/smtp/wizard.rs index a041630..5f92a9f 100644 --- a/src/domain/sender/smtp/wizard.rs +++ b/src/domain/sender/smtp/wizard.rs @@ -1,8 +1,8 @@ use anyhow::Result; use dialoguer::{Confirm, Input, Select}; use pimalaya_email::{ - OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SmtpAuthConfig, - SmtpConfig, + account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig}, + sender::{SenderConfig, SmtpAuthConfig, SmtpConfig}, }; use pimalaya_oauth2::{AuthorizationCodeGrant, Client}; use pimalaya_secret::Secret; diff --git a/src/domain/sender/wizard.rs b/src/domain/sender/wizard.rs index 8bc2e8f..32ce06d 100644 --- a/src/domain/sender/wizard.rs +++ b/src/domain/sender/wizard.rs @@ -1,6 +1,6 @@ use anyhow::Result; use dialoguer::Select; -use pimalaya_email::SenderConfig; +use pimalaya_email::sender::SenderConfig; use crate::config::wizard::THEME; diff --git a/src/domain/tpl/handlers.rs b/src/domain/tpl/handlers.rs index 22878ab..17ec628 100644 --- a/src/domain/tpl/handlers.rs +++ b/src/domain/tpl/handlers.rs @@ -1,11 +1,16 @@ use anyhow::{anyhow, Result}; use atty::Stream; -use pimalaya_email::{AccountConfig, Backend, Email, Flags, Sender, Tpl}; +use pimalaya_email::{ + account::AccountConfig, + backend::Backend, + email::{Flags, Message, Tpl}, + sender::Sender, +}; use std::io::{stdin, BufRead}; use crate::{printer::Printer, IdMapper}; -pub fn forward( +pub async fn forward( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -19,19 +24,21 @@ pub fn forward( let ids = ids.iter().map(String::as_str).collect::>(); let tpl: String = backend - .get_emails(folder, ids)? + .get_emails(folder, ids) + .await? .first() .ok_or_else(|| anyhow!("cannot find email {}", id))? .to_forward_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .build()? + .with_some_headers(headers) + .with_some_body(body) + .build() + .await? .into(); printer.print(tpl) } -pub fn reply( +pub async fn reply( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -46,20 +53,22 @@ pub fn reply( let ids = ids.iter().map(String::as_str).collect::>(); let tpl: String = backend - .get_emails(folder, ids)? + .get_emails(folder, ids) + .await? .first() .ok_or_else(|| anyhow!("cannot find email {}", id))? .to_reply_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .reply_all(all) - .build()? + .with_some_headers(headers) + .with_some_body(body) + .with_reply_all(all) + .build() + .await? .into(); printer.print(tpl) } -pub fn save( +pub async fn save( config: &AccountConfig, printer: &mut P, id_mapper: &IdMapper, @@ -79,16 +88,17 @@ pub fn save( }) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) - .compile()? + .compile() + .await? .write_to_vec()?; - let id = backend.add_email(folder, &email, &Flags::default())?; + let id = backend.add_email(folder, &email, &Flags::default()).await?; id_mapper.create_alias(id)?; printer.print("Template successfully saved!") } -pub fn send( +pub async fn send( config: &AccountConfig, printer: &mut P, backend: &mut dyn Backend, @@ -108,26 +118,31 @@ pub fn send( }) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) - .compile()? + .compile() + .await? .write_to_vec()?; - sender.send(&email)?; + + sender.send(&email).await?; + if config.email_sending_save_copy { - backend.add_email(folder, &email, &Flags::default())?; + backend.add_email(folder, &email, &Flags::default()).await?; } + printer.print("Template successfully sent!")?; Ok(()) } -pub fn write( +pub async fn write( config: &AccountConfig, printer: &mut P, headers: Option>, body: Option<&str>, ) -> Result<()> { - let tpl: String = Email::new_tpl_builder(config) - .some_headers(headers) - .some_body(body) - .build()? + let tpl: String = Message::new_tpl_builder(config) + .with_some_headers(headers) + .with_some_body(body) + .build() + .await? .into(); printer.print(tpl) diff --git a/src/main.rs b/src/main.rs index 2b48251..739b1d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ use anyhow::{anyhow, Context, Result}; use clap::Command; #[cfg(feature = "imap-backend")] -use pimalaya_email::ImapBackend; +use pimalaya_email::backend::ImapBackend; use pimalaya_email::{ - BackendBuilder, BackendConfig, BackendSyncBuilder, SenderBuilder, DEFAULT_INBOX_FOLDER, + account::{sync::AccountSyncBuilder, DEFAULT_INBOX_FOLDER}, + backend::{BackendBuilder, BackendConfig}, + sender::SenderBuilder, }; use std::env; use url::Url; @@ -54,17 +56,20 @@ async fn main() -> Result<()> { let url = Url::parse(&raw_args[1])?; let config = DeserializedConfig::from_opt_path(None)?; let account_config = config.to_account_config(None)?; - let mut backend = BackendBuilder::new(account_config.clone()).build()?; - let mut sender = SenderBuilder::new(account_config.clone()).build()?; + let mut backend = BackendBuilder::new(account_config.clone()).build().await?; + let mut sender = SenderBuilder::new(account_config.clone()).build().await?; let mut printer = StdoutPrinter::default(); - return email::handlers::mailto( + email::handlers::mailto( &account_config, backend.as_mut(), sender.as_mut(), &mut printer, &url, - ); + ) + .await?; + + return Ok(()); } let app = create_app(); @@ -107,13 +112,15 @@ async fn main() -> Result<()> { match imap::args::matches(&m)? { Some(imap::args::Cmd::Notify(keepalive)) => { let mut backend = - ImapBackend::new(account_config.clone(), imap_config.clone(), None)?; - return imap::handlers::notify(&mut backend, &folder, keepalive); + ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?; + imap::handlers::notify(&mut backend, &folder, keepalive).await?; + return Ok(()); } Some(imap::args::Cmd::Watch(keepalive)) => { let mut backend = - ImapBackend::new(account_config.clone(), imap_config.clone(), None)?; - return imap::handlers::watch(&mut backend, &folder, keepalive); + ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?; + imap::handlers::watch(&mut backend, &folder, keepalive).await?; + return Ok(()); } _ => (), } @@ -121,17 +128,20 @@ async fn main() -> Result<()> { match account::args::matches(&m)? { Some(account::args::Cmd::List(max_width)) => { - return account::handlers::list(max_width, &account_config, &config, &mut printer); + account::handlers::list(max_width, &account_config, &config, &mut printer)?; + return Ok(()); } Some(account::args::Cmd::Sync(strategy, dry_run)) => { - let sync_builder = BackendSyncBuilder::new(account_config, backend_builder)? + let sync_builder = AccountSyncBuilder::new(account_config, backend_builder) + .await? .with_some_folders_strategy(strategy) .with_dry_run(dry_run); - account::handlers::sync(&mut printer, sync_builder, dry_run)?; + account::handlers::sync(&mut printer, sync_builder, dry_run).await?; return Ok(()); } Some(account::args::Cmd::Configure(reset)) => { - return account::handlers::configure(&account_config, reset); + account::handlers::configure(&account_config, reset).await?; + return Ok(()); } _ => (), } @@ -143,30 +153,30 @@ async fn main() -> Result<()> { .ok_or_else(|| anyhow!("the folder argument is missing")) .context("cannot create folder")?; let folder = account_config.folder_alias(folder)?; - let mut backend = backend_builder.build()?; - return folder::handlers::create(&mut printer, backend.as_mut(), &folder); + let mut backend = backend_builder.build().await?; + folder::handlers::create(&mut printer, backend.as_mut(), &folder).await?; + return Ok(()); } Some(folder::args::Cmd::List(max_width)) => { - let mut backend = backend_builder.build()?; - return folder::handlers::list( - &account_config, - &mut printer, - backend.as_mut(), - max_width, - ); + let mut backend = backend_builder.build().await?; + folder::handlers::list(&account_config, &mut printer, backend.as_mut(), max_width) + .await?; + return Ok(()); } Some(folder::args::Cmd::Expunge) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.build()?; - return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder); + let mut backend = backend_builder.build().await?; + folder::handlers::expunge(&mut printer, backend.as_mut(), &folder).await?; + return Ok(()); } Some(folder::args::Cmd::Delete) => { let folder = folder .ok_or_else(|| anyhow!("the folder argument is missing")) .context("cannot delete folder")?; let folder = account_config.folder_alias(folder)?; - let mut backend = backend_builder.build()?; - return folder::handlers::delete(&mut printer, backend.as_mut(), &folder); + let mut backend = backend_builder.build().await?; + folder::handlers::delete(&mut printer, backend.as_mut(), &folder).await?; + return Ok(()); } _ => (), } @@ -175,23 +185,25 @@ async fn main() -> Result<()> { match email::args::matches(&m)? { Some(email::args::Cmd::Attachments(ids)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::attachments( + email::handlers::attachments( &account_config, &mut printer, &id_mapper, backend.as_mut(), &folder, ids, - ); + ) + .await?; + return Ok(()); } Some(email::args::Cmd::Copy(ids, to_folder)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::copy( + email::handlers::copy( &account_config, &mut printer, &id_mapper, @@ -199,29 +211,35 @@ async fn main() -> Result<()> { &folder, to_folder, ids, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Delete(ids)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::delete( + email::handlers::delete( &account_config, &mut printer, &id_mapper, backend.as_mut(), &folder, ids, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Forward(id, headers, body)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; - let mut sender = sender_builder.build()?; + let mut backend = backend_builder.clone().into_build().await?; + let mut sender = sender_builder.build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::forward( + email::handlers::forward( &account_config, &mut printer, &id_mapper, @@ -231,14 +249,17 @@ async fn main() -> Result<()> { id, headers, body, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::List(max_width, page_size, page)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::list( + email::handlers::list( &account_config, &mut printer, &id_mapper, @@ -247,14 +268,17 @@ async fn main() -> Result<()> { max_width, page_size, page, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Move(ids, to_folder)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::move_( + email::handlers::move_( &account_config, &mut printer, &id_mapper, @@ -262,14 +286,17 @@ async fn main() -> Result<()> { &folder, to_folder, ids, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Read(ids, text_mime, raw, headers)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::read( + email::handlers::read( &account_config, &mut printer, &id_mapper, @@ -279,15 +306,18 @@ async fn main() -> Result<()> { text_mime, raw, headers, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Reply(id, all, headers, body)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; - let mut sender = sender_builder.build()?; + let mut backend = backend_builder.clone().into_build().await?; + let mut sender = sender_builder.build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::reply( + email::handlers::reply( &account_config, &mut printer, &id_mapper, @@ -298,28 +328,34 @@ async fn main() -> Result<()> { all, headers, body, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Save(raw_email)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::save( + email::handlers::save( &account_config, &mut printer, &id_mapper, backend.as_mut(), &folder, raw_email, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Search(query, max_width, page_size, page)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::search( + email::handlers::search( &account_config, &mut printer, &id_mapper, @@ -329,14 +365,17 @@ async fn main() -> Result<()> { max_width, page_size, page, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return email::handlers::sort( + email::handlers::sort( &account_config, &mut printer, &id_mapper, @@ -347,71 +386,86 @@ async fn main() -> Result<()> { max_width, page_size, page, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Send(raw_email)) => { - let mut backend = backend_builder.build()?; - let mut sender = sender_builder.build()?; - return email::handlers::send( + let mut backend = backend_builder.build().await?; + let mut sender = sender_builder.build().await?; + email::handlers::send( &account_config, &mut printer, backend.as_mut(), sender.as_mut(), raw_email, - ); + ) + .await?; + + return Ok(()); } Some(email::args::Cmd::Flag(m)) => match m { Some(flag::args::Cmd::Set(ids, ref flags)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return flag::handlers::set( + flag::handlers::set( &mut printer, &id_mapper, backend.as_mut(), &folder, ids, flags, - ); + ) + .await?; + + return Ok(()); } Some(flag::args::Cmd::Add(ids, ref flags)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return flag::handlers::add( + flag::handlers::add( &mut printer, &id_mapper, backend.as_mut(), &folder, ids, flags, - ); + ) + .await?; + + return Ok(()); } Some(flag::args::Cmd::Remove(ids, ref flags)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return flag::handlers::remove( + flag::handlers::remove( &mut printer, &id_mapper, backend.as_mut(), &folder, ids, flags, - ); + ) + .await?; + + return Ok(()); } _ => (), }, Some(email::args::Cmd::Tpl(m)) => match m { Some(tpl::args::Cmd::Forward(id, headers, body)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return tpl::handlers::forward( + tpl::handlers::forward( &account_config, &mut printer, &id_mapper, @@ -420,17 +474,21 @@ async fn main() -> Result<()> { id, headers, body, - ); + ) + .await?; + + return Ok(()); } Some(tpl::args::Cmd::Write(headers, body)) => { - return tpl::handlers::write(&account_config, &mut printer, headers, body); + tpl::handlers::write(&account_config, &mut printer, headers, body).await?; + return Ok(()); } Some(tpl::args::Cmd::Reply(id, all, headers, body)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return tpl::handlers::reply( + tpl::handlers::reply( &account_config, &mut printer, &id_mapper, @@ -440,48 +498,60 @@ async fn main() -> Result<()> { all, headers, body, - ); + ) + .await?; + + return Ok(()); } Some(tpl::args::Cmd::Save(tpl)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; + let mut backend = backend_builder.clone().into_build().await?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; - return tpl::handlers::save( + tpl::handlers::save( &account_config, &mut printer, &id_mapper, backend.as_mut(), &folder, tpl, - ); + ) + .await?; + + return Ok(()); } Some(tpl::args::Cmd::Send(tpl)) => { let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; - let mut backend = backend_builder.clone().into_build()?; - let mut sender = sender_builder.build()?; - return tpl::handlers::send( + let mut backend = backend_builder.clone().into_build().await?; + let mut sender = sender_builder.build().await?; + tpl::handlers::send( &account_config, &mut printer, backend.as_mut(), sender.as_mut(), &folder, tpl, - ); + ) + .await?; + + return Ok(()); } _ => (), }, Some(email::args::Cmd::Write(headers, body)) => { - let mut backend = backend_builder.build()?; - let mut sender = sender_builder.build()?; - return email::handlers::write( + let mut backend = backend_builder.build().await?; + let mut sender = sender_builder.build().await?; + email::handlers::write( &account_config, &mut printer, backend.as_mut(), sender.as_mut(), headers, body, - ); + ) + .await?; + + return Ok(()); } _ => (), } diff --git a/src/printer/print.rs b/src/printer/print.rs index 680fb11..304e57b 100644 --- a/src/printer/print.rs +++ b/src/printer/print.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use pimalaya_email::Tpl; +use pimalaya_email::email::Tpl; use crate::printer::WriteColor; diff --git a/src/printer/print_table.rs b/src/printer/print_table.rs index a2f847a..c5e01dc 100644 --- a/src/printer/print_table.rs +++ b/src/printer/print_table.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use pimalaya_email::EmailTextPlainFormat; +use pimalaya_email::email::EmailTextPlainFormat; use std::io; use termcolor::{self, StandardStream}; diff --git a/src/ui/editor.rs b/src/ui/editor.rs index fd8e261..39a3d08 100644 --- a/src/ui/editor.rs +++ b/src/ui/editor.rs @@ -1,8 +1,10 @@ use anyhow::{Context, Result}; use log::debug; use pimalaya_email::{ - email::{local_draft_path, remove_local_draft}, - AccountConfig, Backend, Flag, Flags, Sender, Tpl, + account::AccountConfig, + backend::Backend, + email::{local_draft_path, remove_local_draft, Flag, Flags, Tpl}, + sender::Sender, }; use std::{env, fs, process::Command}; @@ -37,7 +39,7 @@ pub fn open_with_local_draft() -> Result { open_with_tpl(Tpl::from(content)) } -pub fn edit_tpl_with_editor( +pub async fn edit_tpl_with_editor( config: &AccountConfig, printer: &mut P, backend: &mut dyn Backend, @@ -76,13 +78,16 @@ pub fn edit_tpl_with_editor( let email = tpl .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) - .compile()? + .compile() + .await? .write_to_vec()?; - sender.send(&email)?; + sender.send(&email).await?; if config.email_sending_save_copy { let sent_folder = config.sent_folder_alias()?; printer.print_log(format!("Adding email to the {} folder…", sent_folder))?; - backend.add_email(&sent_folder, &email, &Flags::from_iter([Flag::Seen]))?; + backend + .add_email(&sent_folder, &email, &Flags::from_iter([Flag::Seen])) + .await?; } remove_local_draft()?; printer.print("Done!")?; @@ -101,13 +106,16 @@ pub fn edit_tpl_with_editor( let email = tpl .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) - .compile()? + .compile() + .await? .write_to_vec()?; - backend.add_email( - &draft_folder, - &email, - &Flags::from_iter([Flag::Seen, Flag::Draft]), - )?; + backend + .add_email( + &draft_folder, + &email, + &Flags::from_iter([Flag::Seen, Flag::Draft]), + ) + .await?; remove_local_draft()?; printer.print(format!("Email successfully saved to {}", draft_folder))?; break; diff --git a/src/ui/table/table.rs b/src/ui/table/table.rs index 8864db9..59b4ed6 100644 --- a/src/ui/table/table.rs +++ b/src/ui/table/table.rs @@ -6,7 +6,7 @@ use anyhow::{Context, Result}; use log::trace; -use pimalaya_email::EmailTextPlainFormat; +use pimalaya_email::email::EmailTextPlainFormat; use termcolor::{Color, ColorSpec}; use terminal_size::terminal_size; use unicode_width::UnicodeWidthStr; @@ -267,6 +267,7 @@ where #[cfg(test)] mod tests { + use pimalaya_email::email::EmailTextPlainFormat; use std::io; use super::*;