use tokio async runtime

last fixes before merge
This commit is contained in:
Clément DOUIN 2023-07-03 23:08:01 +02:00
parent f8ca248bce
commit cac8280c8c
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
31 changed files with 538 additions and 332 deletions

31
Cargo.lock generated
View file

@ -1218,6 +1218,7 @@ name = "himalaya"
version = "0.8.1" version = "0.8.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait",
"atty", "atty",
"chrono", "chrono",
"clap", "clap",
@ -2199,12 +2200,12 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-email" name = "pimalaya-email"
version = "0.11.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://git.sr.ht/~soywod/pimalaya#5250a4c906228da7f2d1ca1d58468a760c69c720"
checksum = "8375dc804686e9e03a8b6f87d80b9038b5a734efb4b77f81b90b07b8ac1d4499"
dependencies = [ dependencies = [
"advisory-lock", "advisory-lock",
"ammonia", "ammonia",
"async-trait",
"chrono", "chrono",
"convert_case", "convert_case",
"dirs", "dirs",
@ -2244,10 +2245,11 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-email-tpl" name = "pimalaya-email-tpl"
version = "0.2.3" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a838dd91468bf79997ead555d6a5b93f066e363259925ec931d49591b1ebb79" checksum = "c536455f778ed7aa0948b755281e8e6a61650a857a3f610ff7e60b097a681838"
dependencies = [ dependencies = [
"async-recursion",
"chumsky 0.9.0", "chumsky 0.9.0",
"log", "log",
"mail-builder", "mail-builder",
@ -2285,19 +2287,20 @@ dependencies = [
[[package]] [[package]]
name = "pimalaya-process" name = "pimalaya-process"
version = "0.0.2" version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d8d2853bf2f0efbe397ce22bb4e6d53a7464f6dcc0abe7e2936f6f4e8e2726a" checksum = "1a9d1e1ab6334d4e4d06613cd65f3fed34e5d2d7b1488f3d1a0d2fdffdba5d1c"
dependencies = [ dependencies = [
"log", "log",
"thiserror", "thiserror",
"tokio",
] ]
[[package]] [[package]]
name = "pimalaya-secret" name = "pimalaya-secret"
version = "0.0.2" version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9338dc84e5ec9fc25f3a36d82ed68ffe4888ad728ca1aa42228e27648ddff01f" checksum = "0f975cafe977327bb215cf71e953dd1f1bfa45d3cf4ab5e806493d917e0a0cd1"
dependencies = [ dependencies = [
"log", "log",
"pimalaya-keyring", "pimalaya-keyring",
@ -2925,6 +2928,15 @@ dependencies = [
"dirs", "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]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.10" version = "0.3.10"
@ -3181,6 +3193,7 @@ dependencies = [
"mio", "mio",
"num_cpus", "num_cpus",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.42.0", "windows-sys 0.42.0",

View file

@ -25,6 +25,7 @@ notmuch-backend = ["pimalaya-email/notmuch-backend"]
smtp-sender = ["pimalaya-email/smtp-sender"] smtp-sender = ["pimalaya-email/smtp-sender"]
[dev-dependencies] [dev-dependencies]
async-trait = "0.1"
tempfile = "3.3" tempfile = "3.3"
[dependencies] [dependencies]
@ -44,17 +45,17 @@ indicatif = "0.17"
log = "0.4" log = "0.4"
md5 = "0.7.0" md5 = "0.7.0"
once_cell = "1.16.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-keyring = "=0.0.4"
pimalaya-oauth2 = "=0.0.3" pimalaya-oauth2 = "=0.0.3"
pimalaya-process = "=0.0.2" pimalaya-process = "=0.0.5"
pimalaya-secret = "=0.0.2" pimalaya-secret = "=0.0.4"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
shellexpand = "2.1" shellexpand = "2.1"
termcolor = "1.1" termcolor = "1.1"
terminal_size = "0.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 = "0.7.4"
toml_edit = "0.19.8" toml_edit = "0.19.8"
unicode-width = "0.1" unicode-width = "0.1"

View file

@ -1,10 +1,10 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use log::{debug, trace}; use log::{debug, trace};
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use pimalaya_email::ImapBackend; use pimalaya_email::backend::ImapBackend;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use pimalaya_email::NotmuchBackend; use pimalaya_email::backend::NotmuchBackend;
use pimalaya_email::{Backend, MaildirBackend}; use pimalaya_email::backend::{Backend, MaildirBackend};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const ID_MAPPER_DB_FILE_NAME: &str = ".id-mapper.sqlite"; const ID_MAPPER_DB_FILE_NAME: &str = ".id-mapper.sqlite";

View file

@ -7,7 +7,10 @@ use anyhow::{anyhow, Context, Result};
use dialoguer::Confirm; use dialoguer::Confirm;
use dirs::{config_dir, home_dir}; use dirs::{config_dir, home_dir};
use log::{debug, trace}; use log::{debug, trace};
use pimalaya_email::{AccountConfig, EmailHooks, EmailTextPlainFormat}; use pimalaya_email::{
account::AccountConfig,
email::{EmailHooks, EmailTextPlainFormat},
};
use pimalaya_process::Cmd; use pimalaya_process::Cmd;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::PathBuf, process}; use std::{collections::HashMap, fs, path::PathBuf, process};
@ -158,16 +161,18 @@ impl DeserializedConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pimalaya_email::{ use pimalaya_email::{
BackendConfig, MaildirConfig, PasswdConfig, SenderConfig, SendmailConfig, account::PasswdConfig,
backend::{BackendConfig, MaildirConfig},
sender::{SenderConfig, SendmailConfig},
}; };
use pimalaya_secret::Secret; use pimalaya_secret::Secret;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use pimalaya_email::NotmuchConfig; use pimalaya_email::backend::NotmuchConfig;
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use pimalaya_email::{ImapAuthConfig, ImapConfig}; use pimalaya_email::backend::{ImapAuthConfig, ImapConfig};
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
use pimalaya_email::{SmtpAuthConfig, SmtpConfig}; use pimalaya_email::sender::{SmtpAuthConfig, SmtpConfig};
use std::io::Write; use std::io::Write;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
@ -453,7 +458,7 @@ mod tests {
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
#[test] #[test]
fn account_smtp_sender_minimum_config() { fn account_smtp_sender_minimum_config() {
use pimalaya_email::SenderConfig; use pimalaya_email::sender::SenderConfig;
let config = make_config( let config = make_config(
"[account] "[account]

View file

@ -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")] #[cfg(feature = "notmuch-backend")]
use pimalaya_email::NotmuchConfig; use pimalaya_email::NotmuchConfig;
use pimalaya_email::{ use pimalaya_email::{
BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy, MaildirConfig, account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig},
OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SendmailConfig, 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_keyring::Entry;
use pimalaya_process::{Cmd, Pipeline, SingleCmd}; use pimalaya_process::{Cmd, Pipeline, SingleCmd};
use pimalaya_secret::Secret; use pimalaya_secret::Secret;

View file

@ -5,7 +5,7 @@
//! accounts from the config file. //! accounts from the config file.
use anyhow::Result; use anyhow::Result;
use pimalaya_email::BackendConfig; use pimalaya_email::backend::BackendConfig;
use serde::Serialize; use serde::Serialize;
use std::{collections::hash_map::Iter, ops::Deref}; use std::{collections::hash_map::Iter, ops::Deref};

View file

@ -3,7 +3,7 @@
use anyhow::Result; use anyhow::Result;
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use log::info; use log::info;
use pimalaya_email::FolderSyncStrategy; use pimalaya_email::folder::sync::FolderSyncStrategy;
use std::collections::HashSet; use std::collections::HashSet;
use crate::{folder, ui::table}; use crate::{folder, ui::table};

View file

@ -4,12 +4,15 @@
//! account in the accounts section of the user configuration file. //! account in the accounts section of the user configuration file.
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use pimalaya_email::ImapAuthConfig; use pimalaya_email::backend::ImapAuthConfig;
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
use pimalaya_email::SmtpAuthConfig; use pimalaya_email::sender::SmtpAuthConfig;
use pimalaya_email::{ use pimalaya_email::{
AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat, FolderSyncStrategy, account::AccountConfig,
SenderConfig, backend::BackendConfig,
email::{EmailHooks, EmailTextPlainFormat},
folder::sync::FolderSyncStrategy,
sender::SenderConfig,
}; };
use pimalaya_process::Cmd; use pimalaya_process::Cmd;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -7,11 +7,16 @@ use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
use log::{info, trace, warn}; use log::{info, trace, warn};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use pimalaya_email::ImapAuthConfig; use pimalaya_email::backend::ImapAuthConfig;
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
use pimalaya_email::SmtpAuthConfig; use pimalaya_email::sender::SmtpAuthConfig;
use pimalaya_email::{ use pimalaya_email::{
AccountConfig, BackendConfig, BackendSyncBuilder, BackendSyncProgressEvent, SenderConfig, account::{
sync::{AccountSyncBuilder, AccountSyncProgressEvent},
AccountConfig,
},
backend::BackendConfig,
sender::SenderConfig,
}; };
use std::{collections::HashMap, sync::Mutex}; use std::{collections::HashMap, sync::Mutex};
@ -40,7 +45,7 @@ const SUB_PROGRESS_DONE_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
}); });
/// Configure the current selected account /// 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"); info!("entering the configure account handler");
if reset { if reset {
@ -72,9 +77,13 @@ pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
if let BackendConfig::Imap(imap_config) = &config.backend { if let BackendConfig::Imap(imap_config) = &config.backend {
match &imap_config.auth { 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) => { 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")] #[cfg(feature = "smtp-sender")]
if let SenderConfig::Smtp(smtp_config) = &config.sender { if let SenderConfig::Smtp(smtp_config) = &config.sender {
match &smtp_config.auth { 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) => { 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 /// Synchronizes the account defined using argument `-a|--account`. If
/// no account given, synchronizes the default one. /// no account given, synchronizes the default one.
pub fn sync<'a, P: Printer>( pub async fn sync<P: Printer>(
printer: &mut P, printer: &mut P,
sync_builder: BackendSyncBuilder<'a>, sync_builder: AccountSyncBuilder,
dry_run: bool, dry_run: bool,
) -> Result<()> { ) -> Result<()> {
info!("entering the sync accounts handler"); info!("entering the sync accounts handler");
trace!("dry run: {dry_run}"); trace!("dry run: {dry_run}");
if dry_run { if dry_run {
let report = sync_builder.sync()?; let report = sync_builder.sync().await?;
let mut hunks_count = report.folders_patch.len(); let mut hunks_count = report.folders_patch.len();
if !report.folders_patch.is_empty() { if !report.folders_patch.is_empty() {
@ -143,9 +156,9 @@ pub fn sync<'a, P: Printer>(
printer.print_log("")?; printer.print_log("")?;
} }
if !report.envelopes_patch.is_empty() { if !report.emails_patch.is_empty() {
printer.print_log("Envelopes patch:")?; printer.print_log("Envelopes patch:")?;
for (hunk, _) in report.envelopes_patch { for (hunk, _) in report.emails_patch {
hunks_count += 1; hunks_count += 1;
printer.print_log(format!(" - {hunk}"))?; 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}", "Estimated patch length for account to be synchronized: {hunks_count}",
))?; ))?;
} else if printer.is_json() { } else if printer.is_json() {
sync_builder.sync()?; sync_builder.sync().await?;
printer.print("Account successfully synchronized!")?; printer.print("Account successfully synchronized!")?;
} else { } else {
let multi = MultiProgress::new(); let multi = MultiProgress::new();
@ -172,7 +185,7 @@ pub fn sync<'a, P: Printer>(
let report = sync_builder let report = sync_builder
.with_on_progress(move |evt| { .with_on_progress(move |evt| {
use BackendSyncProgressEvent::*; use AccountSyncProgressEvent::*;
Ok(match evt { Ok(match evt {
ApplyFolderPatches(..) => { ApplyFolderPatches(..) => {
main_progress.inc(3); main_progress.inc(3);
@ -223,7 +236,8 @@ pub fn sync<'a, P: Printer>(
_ => (), _ => (),
}) })
}) })
.sync()?; .sync()
.await?;
let folders_patch_err = report let folders_patch_err = report
.folders_patch .folders_patch
@ -246,7 +260,7 @@ pub fn sync<'a, P: Printer>(
} }
let envelopes_patch_err = report let envelopes_patch_err = report
.envelopes_patch .emails_patch
.iter() .iter()
.filter_map(|(hunk, err)| err.as_ref().map(|err| (hunk, err))) .filter_map(|(hunk, err)| err.as_ref().map(|err| (hunk, err)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -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("")?;
printer.print_log(format!( printer.print_log(format!(
"Error occurred while applying the envelopes cache patch: {err}" "Error occurred while applying the envelopes cache patch: {err}"
@ -273,7 +287,7 @@ pub fn sync<'a, P: Printer>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pimalaya_email::{AccountConfig, ImapConfig}; use pimalaya_email::{account::AccountConfig, backend::ImapConfig};
use std::{collections::HashMap, fmt::Debug, io}; use std::{collections::HashMap, fmt::Debug, io};
use termcolor::ColorSpec; use termcolor::ColorSpec;

View file

@ -3,14 +3,14 @@
//! This module gathers all IMAP handlers triggered by the CLI. //! This module gathers all IMAP handlers triggered by the CLI.
use anyhow::Result; use anyhow::Result;
use pimalaya_email::ImapBackend; use pimalaya_email::backend::ImapBackend;
pub fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { pub async fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.notify(keepalive, folder)?; imap.notify(keepalive, folder).await?;
Ok(()) Ok(())
} }
pub fn watch(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> { pub async fn watch(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
imap.watch(keepalive, folder)?; imap.watch(keepalive, folder).await?;
Ok(()) Ok(())
} }

View file

@ -1,8 +1,8 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::{Confirm, Input, Password, Select}; use dialoguer::{Confirm, Input, Password, Select};
use pimalaya_email::{ use pimalaya_email::{
BackendConfig, ImapAuthConfig, ImapConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig},
PasswdConfig, backend::{BackendConfig, ImapAuthConfig, ImapConfig},
}; };
use pimalaya_oauth2::{AuthorizationCodeGrant, Client}; use pimalaya_oauth2::{AuthorizationCodeGrant, Client};
use pimalaya_secret::Secret; use pimalaya_secret::Secret;

View file

@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::Input; use dialoguer::Input;
use dirs::home_dir; use dirs::home_dir;
use pimalaya_email::{BackendConfig, MaildirConfig}; use pimalaya_email::backend::{BackendConfig, MaildirConfig};
use crate::config::wizard::THEME; use crate::config::wizard::THEME;

View file

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::Select; use dialoguer::Select;
use pimalaya_email::BackendConfig; use pimalaya_email::backend::BackendConfig;
use crate::config::wizard::THEME; use crate::config::wizard::THEME;

View file

@ -2,7 +2,10 @@ use anyhow::{anyhow, Context, Result};
use atty::Stream; use atty::Stream;
use log::{debug, trace}; use log::{debug, trace};
use pimalaya_email::{ 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::{ use std::{
fs, fs,
@ -17,7 +20,7 @@ use crate::{
Envelopes, IdMapper, Envelopes, IdMapper,
}; };
pub fn attachments<P: Printer>( pub async fn attachments<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -28,7 +31,7 @@ pub fn attachments<P: Printer>(
let folder = config.folder_alias(folder)?; let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let emails = backend.get_emails(&folder, ids.clone())?; let emails = backend.get_emails(&folder, ids.clone()).await?;
let mut index = 0; let mut index = 0;
let mut emails_count = 0; let mut emails_count = 0;
@ -57,7 +60,7 @@ pub fn attachments<P: Printer>(
let filename = attachment let filename = attachment
.filename .filename
.unwrap_or_else(|| Uuid::new_v4().to_string()); .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))?; printer.print_log(format!("Downloading {:?}", filepath))?;
fs::write(&filepath, &attachment.body).context("cannot download attachment")?; fs::write(&filepath, &attachment.body).context("cannot download attachment")?;
attachments_count = attachments_count + 1; attachments_count = attachments_count + 1;
@ -74,7 +77,7 @@ pub fn attachments<P: Printer>(
} }
} }
pub fn copy<P: Printer>( pub async fn copy<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -87,11 +90,11 @@ pub fn copy<P: Printer>(
let to_folder = config.folder_alias(to_folder)?; let to_folder = config.folder_alias(to_folder)?;
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.copy_emails(&from_folder, &to_folder, ids)?; backend.copy_emails(&from_folder, &to_folder, ids).await?;
printer.print("Email(s) successfully copied!") printer.print("Email(s) successfully copied!")
} }
pub fn delete<P: Printer>( pub async fn delete<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -102,11 +105,11 @@ pub fn delete<P: Printer>(
let folder = config.folder_alias(folder)?; let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.delete_emails(&folder, ids)?; backend.delete_emails(&folder, ids).await?;
printer.print("Email(s) successfully deleted!") printer.print("Email(s) successfully deleted!")
} }
pub fn forward<P: Printer>( pub async fn forward<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -123,19 +126,21 @@ pub fn forward<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let tpl = backend let tpl = backend
.get_emails(&folder, ids)? .get_emails(&folder, ids)
.await?
.first() .first()
.ok_or_else(|| anyhow!("cannot find email {}", id))? .ok_or_else(|| anyhow!("cannot find email {}", id))?
.to_forward_tpl_builder(config) .to_forward_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.build()?; .build()
.await?;
trace!("initial template: {}", *tpl); 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(()) Ok(())
} }
pub fn list<P: Printer>( pub async fn list<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -152,7 +157,7 @@ pub fn list<P: Printer>(
let envelopes = Envelopes::from_backend( let envelopes = Envelopes::from_backend(
config, config,
id_mapper, id_mapper,
backend.list_envelopes(&folder, page_size, page)?, backend.list_envelopes(&folder, page_size, page).await?,
)?; )?;
trace!("envelopes: {:?}", envelopes); trace!("envelopes: {:?}", envelopes);
@ -168,14 +173,14 @@ pub fn list<P: Printer>(
/// Parses and edits a message from a [mailto] URL string. /// Parses and edits a message from a [mailto] URL string.
/// ///
/// [mailto]: https://en.wikipedia.org/wiki/Mailto /// [mailto]: https://en.wikipedia.org/wiki/Mailto
pub fn mailto<P: Printer>( pub async fn mailto<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
backend: &mut dyn Backend, backend: &mut dyn Backend,
sender: &mut dyn Sender, sender: &mut dyn Sender,
printer: &mut P, printer: &mut P,
url: &Url, url: &Url,
) -> Result<()> { ) -> Result<()> {
let mut builder = EmailBuilder::new().to(url.path()); let mut builder = MessageBuilder::new().to(url.path());
for (key, val) in url.query_pairs() { for (key, val) in url.query_pairs() {
match key.to_lowercase().as_bytes() { match key.to_lowercase().as_bytes() {
@ -190,12 +195,13 @@ pub fn mailto<P: Printer>(
let tpl = config let tpl = config
.generate_tpl_interpreter() .generate_tpl_interpreter()
.show_only_headers(config.email_writing_headers()) .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_<P: Printer>( pub async fn move_<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -208,11 +214,11 @@ pub fn move_<P: Printer>(
let to_folder = config.folder_alias(to_folder)?; let to_folder = config.folder_alias(to_folder)?;
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.move_emails(&from_folder, &to_folder, ids)?; backend.move_emails(&from_folder, &to_folder, ids).await?;
printer.print("Email(s) successfully moved!") printer.print("Email(s) successfully moved!")
} }
pub fn read<P: Printer>( pub async fn read<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -226,7 +232,7 @@ pub fn read<P: Printer>(
let folder = config.folder_alias(folder)?; let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let emails = backend.get_emails(&folder, ids)?; let emails = backend.get_emails(&folder, ids).await?;
let mut glue = ""; let mut glue = "";
let mut bodies = String::default(); let mut bodies = String::default();
@ -245,7 +251,8 @@ pub fn read<P: Printer>(
.hide_all_headers() .hide_all_headers()
.filter_parts(FilterParts::Only("text/html".into())), .filter_parts(FilterParts::Only("text/html".into())),
_ => tpl.show_additional_headers(&headers), _ => tpl.show_additional_headers(&headers),
})? })
.await?
.into(); .into();
bodies.push_str(&tpl); bodies.push_str(&tpl);
} }
@ -256,7 +263,7 @@ pub fn read<P: Printer>(
printer.print(bodies) printer.print(bodies)
} }
pub fn reply<P: Printer>( pub async fn reply<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -274,21 +281,25 @@ pub fn reply<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let tpl = backend let tpl = backend
.get_emails(&folder, ids)? .get_emails(&folder, ids)
.await?
.first() .first()
.ok_or_else(|| anyhow!("cannot find email {}", id))? .ok_or_else(|| anyhow!("cannot find email {}", id))?
.to_reply_tpl_builder(config) .to_reply_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.reply_all(all) .with_reply_all(all)
.build()?; .build()
.await?;
trace!("initial template: {}", *tpl); 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?;
backend.add_flags(&folder, vec![id], &Flags::from_iter([Flag::Answered]))?; backend
.add_flags(&folder, vec![id], &Flags::from_iter([Flag::Answered]))
.await?;
Ok(()) Ok(())
} }
pub fn save<P: Printer>( pub async fn save<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -310,13 +321,15 @@ pub fn save<P: Printer>(
.join("\r\n") .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)?; id_mapper.create_alias(id)?;
Ok(()) Ok(())
} }
pub fn search<P: Printer>( pub async fn search<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -332,7 +345,9 @@ pub fn search<P: Printer>(
let envelopes = Envelopes::from_backend( let envelopes = Envelopes::from_backend(
config, config,
id_mapper, id_mapper,
backend.search_envelopes(&folder, &query, "", page_size, page)?, backend
.search_envelopes(&folder, &query, "", page_size, page)
.await?,
)?; )?;
let opts = PrintTableOpts { let opts = PrintTableOpts {
format: &config.email_reading_format, format: &config.email_reading_format,
@ -342,7 +357,7 @@ pub fn search<P: Printer>(
printer.print_table(Box::new(envelopes), opts) printer.print_table(Box::new(envelopes), opts)
} }
pub fn sort<P: Printer>( pub async fn sort<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -359,7 +374,9 @@ pub fn sort<P: Printer>(
let envelopes = Envelopes::from_backend( let envelopes = Envelopes::from_backend(
config, config,
id_mapper, id_mapper,
backend.search_envelopes(&folder, &query, &sort, page_size, page)?, backend
.search_envelopes(&folder, &query, &sort, page_size, page)
.await?,
)?; )?;
let opts = PrintTableOpts { let opts = PrintTableOpts {
format: &config.email_reading_format, format: &config.email_reading_format,
@ -369,7 +386,7 @@ pub fn sort<P: Printer>(
printer.print_table(Box::new(envelopes), opts) printer.print_table(Box::new(envelopes), opts)
} }
pub fn send<P: Printer>( pub async fn send<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -390,18 +407,20 @@ pub fn send<P: Printer>(
.join("\r\n") .join("\r\n")
}; };
trace!("raw email: {:?}", raw_email); trace!("raw email: {:?}", raw_email);
sender.send(raw_email.as_bytes())?; sender.send(raw_email.as_bytes()).await?;
if config.email_sending_save_copy { if config.email_sending_save_copy {
backend.add_email( backend
&folder, .add_email(
raw_email.as_bytes(), &folder,
&Flags::from_iter([Flag::Seen]), raw_email.as_bytes(),
)?; &Flags::from_iter([Flag::Seen]),
)
.await?;
} }
Ok(()) Ok(())
} }
pub fn write<P: Printer>( pub async fn write<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -409,11 +428,12 @@ pub fn write<P: Printer>(
headers: Option<Vec<(&str, &str)>>, headers: Option<Vec<(&str, &str)>>,
body: Option<&str>, body: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let tpl = Email::new_tpl_builder(config) let tpl = Message::new_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.build()?; .build()
.await?;
trace!("initial template: {}", *tpl); 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(()) Ok(())
} }

View file

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use pimalaya_email::AccountConfig; use pimalaya_email::account::AccountConfig;
use serde::Serialize; use serde::Serialize;
use std::ops; use std::ops;
@ -17,7 +17,7 @@ impl Envelopes {
pub fn from_backend( pub fn from_backend(
config: &AccountConfig, config: &AccountConfig,
id_mapper: &IdMapper, id_mapper: &IdMapper,
envelopes: pimalaya_email::Envelopes, envelopes: pimalaya_email::email::Envelopes,
) -> Result<Envelopes> { ) -> Result<Envelopes> {
let envelopes = envelopes let envelopes = envelopes
.iter() .iter()
@ -58,10 +58,9 @@ impl PrintTable for Envelopes {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::env;
use chrono::DateTime; use chrono::DateTime;
use pimalaya_email::AccountConfig; use pimalaya_email::account::AccountConfig;
use std::env;
use crate::{Envelopes, IdMapper}; use crate::{Envelopes, IdMapper};
@ -70,10 +69,11 @@ mod tests {
let config = AccountConfig::default(); let config = AccountConfig::default();
let id_mapper = IdMapper::Dummy; let id_mapper = IdMapper::Dummy;
let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { let envelopes =
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope {
..Default::default() 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 envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();
let expected_date = "2023-06-15 09:42+04:00"; let expected_date = "2023-06-15 09:42+04:00";
@ -90,10 +90,11 @@ mod tests {
..AccountConfig::default() ..AccountConfig::default()
}; };
let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { let envelopes =
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope {
..Default::default() 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 envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();
let expected_date = "15/06/2023 09h42"; let expected_date = "15/06/2023 09h42";
@ -113,10 +114,11 @@ mod tests {
..AccountConfig::default() ..AccountConfig::default()
}; };
let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope { let envelopes =
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(), pimalaya_email::email::Envelopes::from_iter([pimalaya_email::email::Envelope {
..Default::default() 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 envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();
let expected_date = "15/06/2023 05h42"; let expected_date = "15/06/2023 05h42";

View file

@ -6,7 +6,7 @@
use anyhow::Result; use anyhow::Result;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use log::{debug, info}; use log::{debug, info};
use pimalaya_email::{Flag, Flags}; use pimalaya_email::email::{Flag, Flags};
use crate::email; use crate::email;

View file

@ -11,15 +11,16 @@ pub enum Flag {
Custom(String), Custom(String),
} }
impl From<&pimalaya_email::Flag> for Flag { impl From<&pimalaya_email::email::Flag> for Flag {
fn from(flag: &pimalaya_email::Flag) -> Self { fn from(flag: &pimalaya_email::email::Flag) -> Self {
use pimalaya_email::email::Flag::*;
match flag { match flag {
pimalaya_email::Flag::Seen => Flag::Seen, Seen => Flag::Seen,
pimalaya_email::Flag::Answered => Flag::Answered, Answered => Flag::Answered,
pimalaya_email::Flag::Flagged => Flag::Flagged, Flagged => Flag::Flagged,
pimalaya_email::Flag::Deleted => Flag::Deleted, Deleted => Flag::Deleted,
pimalaya_email::Flag::Draft => Flag::Draft, Draft => Flag::Draft,
pimalaya_email::Flag::Custom(flag) => Flag::Custom(flag.clone()), Custom(flag) => Flag::Custom(flag.clone()),
} }
} }
} }

View file

@ -14,8 +14,8 @@ impl ops::Deref for Flags {
} }
} }
impl From<pimalaya_email::Flags> for Flags { impl From<pimalaya_email::email::Flags> for Flags {
fn from(flags: pimalaya_email::Flags) -> Self { fn from(flags: pimalaya_email::email::Flags) -> Self {
Flags(flags.iter().map(Flag::from).collect()) Flags(flags.iter().map(Flag::from).collect())
} }
} }

View file

@ -1,9 +1,9 @@
use anyhow::Result; use anyhow::Result;
use pimalaya_email::{Backend, Flags}; use pimalaya_email::{backend::Backend, email::Flags};
use crate::{printer::Printer, IdMapper}; use crate::{printer::Printer, IdMapper};
pub fn add<P: Printer>( pub async fn add<P: Printer>(
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -13,11 +13,11 @@ pub fn add<P: Printer>(
) -> Result<()> { ) -> Result<()> {
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.add_flags(folder, ids, flags)?; backend.add_flags(folder, ids, flags).await?;
printer.print("Flag(s) successfully added!") printer.print("Flag(s) successfully added!")
} }
pub fn set<P: Printer>( pub async fn set<P: Printer>(
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -27,11 +27,11 @@ pub fn set<P: Printer>(
) -> Result<()> { ) -> Result<()> {
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.set_flags(folder, ids, flags)?; backend.set_flags(folder, ids, flags).await?;
printer.print("Flag(s) successfully set!") printer.print("Flag(s) successfully set!")
} }
pub fn remove<P: Printer>( pub async fn remove<P: Printer>(
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -41,6 +41,6 @@ pub fn remove<P: Printer>(
) -> Result<()> { ) -> Result<()> {
let ids = id_mapper.get_ids(ids)?; let ids = id_mapper.get_ids(ids)?;
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
backend.remove_flags(folder, ids, flags)?; backend.remove_flags(folder, ids, flags).await?;
printer.print("Flag(s) successfully removed!") printer.print("Flag(s) successfully removed!")
} }

View file

@ -4,15 +4,13 @@ use crate::ui::{Cell, Row, Table};
#[derive(Clone, Debug, Default, Serialize)] #[derive(Clone, Debug, Default, Serialize)]
pub struct Folder { pub struct Folder {
pub delim: String,
pub name: String, pub name: String,
pub desc: String, pub desc: String,
} }
impl From<&pimalaya_email::Folder> for Folder { impl From<&pimalaya_email::folder::Folder> for Folder {
fn from(folder: &pimalaya_email::Folder) -> Self { fn from(folder: &pimalaya_email::folder::Folder) -> Self {
Folder { Folder {
delim: folder.delim.clone(),
name: folder.name.clone(), name: folder.name.clone(),
desc: folder.desc.clone(), desc: folder.desc.clone(),
} }
@ -22,14 +20,12 @@ impl From<&pimalaya_email::Folder> for Folder {
impl Table for Folder { impl Table for Folder {
fn head() -> Row { fn head() -> Row {
Row::new() Row::new()
.cell(Cell::new("DELIM").bold().underline().white())
.cell(Cell::new("NAME").bold().underline().white()) .cell(Cell::new("NAME").bold().underline().white())
.cell(Cell::new("DESC").bold().underline().white()) .cell(Cell::new("DESC").bold().underline().white())
} }
fn row(&self) -> Row { fn row(&self) -> Row {
Row::new() Row::new()
.cell(Cell::new(&self.delim).white())
.cell(Cell::new(&self.name).blue()) .cell(Cell::new(&self.name).blue())
.cell(Cell::new(&self.desc).green()) .cell(Cell::new(&self.desc).green())
} }

View file

@ -1,7 +1,6 @@
use std::ops;
use anyhow::Result; use anyhow::Result;
use serde::Serialize; use serde::Serialize;
use std::ops;
use crate::{ use crate::{
printer::{PrintTable, PrintTableOpts, WriteColor}, printer::{PrintTable, PrintTableOpts, WriteColor},
@ -20,8 +19,8 @@ impl ops::Deref for Folders {
} }
} }
impl From<pimalaya_email::Folders> for Folders { impl From<pimalaya_email::folder::Folders> for Folders {
fn from(folders: pimalaya_email::Folders) -> Self { fn from(folders: pimalaya_email::folder::Folders) -> Self {
Folders(folders.iter().map(Folder::from).collect()) Folders(folders.iter().map(Folder::from).collect())
} }
} }

View file

@ -4,7 +4,7 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::Confirm; use dialoguer::Confirm;
use pimalaya_email::{AccountConfig, Backend}; use pimalaya_email::{account::AccountConfig, backend::Backend};
use std::process; use std::process;
use crate::{ use crate::{
@ -12,18 +12,22 @@ use crate::{
Folders, Folders,
}; };
pub fn expunge<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { pub async fn expunge<P: Printer>(
backend.expunge_folder(folder)?; printer: &mut P,
backend: &mut dyn Backend,
folder: &str,
) -> Result<()> {
backend.expunge_folder(folder).await?;
printer.print(format!("Folder {folder} successfully expunged!")) printer.print(format!("Folder {folder} successfully expunged!"))
} }
pub fn list<P: Printer>( pub async fn list<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
max_width: Option<usize>, max_width: Option<usize>,
) -> Result<()> { ) -> Result<()> {
let folders: Folders = backend.list_folders()?.into(); let folders: Folders = backend.list_folders().await?.into();
printer.print_table( printer.print_table(
// TODO: remove Box // TODO: remove Box
Box::new(folders), Box::new(folders),
@ -34,12 +38,20 @@ pub fn list<P: Printer>(
) )
} }
pub fn create<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { pub async fn create<P: Printer>(
backend.add_folder(folder)?; printer: &mut P,
backend: &mut dyn Backend,
folder: &str,
) -> Result<()> {
backend.add_folder(folder).await?;
printer.print("Folder successfully created!") printer.print("Folder successfully created!")
} }
pub fn delete<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> { pub async fn delete<P: Printer>(
printer: &mut P,
backend: &mut dyn Backend,
folder: &str,
) -> Result<()> {
if let Some(false) | None = Confirm::new() if let Some(false) | None = Confirm::new()
.with_prompt(format!("Confirm deletion of folder {folder}?")) .with_prompt(format!("Confirm deletion of folder {folder}?"))
.default(false) .default(false)
@ -49,14 +61,18 @@ pub fn delete<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &s
process::exit(0); process::exit(0);
}; };
backend.delete_folder(folder)?; backend.delete_folder(folder).await?;
printer.print("Folder successfully deleted!") printer.print("Folder successfully deleted!")
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use async_trait::async_trait;
use pimalaya_email::{ 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 std::{any::Any, fmt::Debug, io};
use termcolor::ColorSpec; use termcolor::ColorSpec;
@ -65,8 +81,8 @@ mod tests {
use super::*; use super::*;
#[test] #[tokio::test]
fn it_should_list_mboxes() { async fn it_should_list_mboxes() {
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct StringWriter { struct StringWriter {
content: String, content: String,
@ -131,82 +147,119 @@ mod tests {
struct TestBackend; struct TestBackend;
#[async_trait]
impl Backend for TestBackend { impl Backend for TestBackend {
fn name(&self) -> String { fn name(&self) -> String {
unimplemented!(); unimplemented!();
} }
fn add_folder(&mut self, _: &str) -> backend::Result<()> { async fn add_folder(&mut self, _: &str) -> pimalaya_email::Result<()> {
unimplemented!(); unimplemented!();
} }
fn list_folders(&mut self) -> backend::Result<Folders> { async fn list_folders(&mut self) -> pimalaya_email::Result<Folders> {
Ok(Folders::from_iter([ Ok(Folders::from_iter([
Folder { Folder {
delim: "/".into(),
name: "INBOX".into(), name: "INBOX".into(),
desc: "desc".into(), desc: "desc".into(),
}, },
Folder { Folder {
delim: "/".into(),
name: "Sent".into(), name: "Sent".into(),
desc: "desc".into(), desc: "desc".into(),
}, },
])) ]))
} }
fn expunge_folder(&mut self, _: &str) -> backend::Result<()> { async fn expunge_folder(&mut self, _: &str) -> pimalaya_email::Result<()> {
unimplemented!(); unimplemented!();
} }
fn purge_folder(&mut self, _: &str) -> backend::Result<()> { async fn purge_folder(&mut self, _: &str) -> pimalaya_email::Result<()> {
unimplemented!(); unimplemented!();
} }
fn delete_folder(&mut self, _: &str) -> backend::Result<()> { async fn delete_folder(&mut self, _: &str) -> pimalaya_email::Result<()> {
unimplemented!(); unimplemented!();
} }
fn get_envelope(&mut self, _: &str, _: &str) -> backend::Result<Envelope> { async fn get_envelope(&mut self, _: &str, _: &str) -> pimalaya_email::Result<Envelope> {
unimplemented!(); unimplemented!();
} }
fn list_envelopes( async fn list_envelopes(
&mut self, &mut self,
_: &str, _: &str,
_: usize, _: usize,
_: usize, _: usize,
) -> backend::Result<Envelopes> { ) -> pimalaya_email::Result<Envelopes> {
unimplemented!() unimplemented!()
} }
fn search_envelopes( async fn search_envelopes(
&mut self, &mut self,
_: &str, _: &str,
_: &str, _: &str,
_: &str, _: &str,
_: usize, _: usize,
_: usize, _: usize,
) -> backend::Result<Envelopes> { ) -> pimalaya_email::Result<Envelopes> {
unimplemented!() unimplemented!()
} }
fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> backend::Result<String> { async fn add_email(
&mut self,
_: &str,
_: &[u8],
_: &Flags,
) -> pimalaya_email::Result<String> {
unimplemented!() unimplemented!()
} }
fn get_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<Emails> { async fn get_emails(
&mut self,
_: &str,
_: Vec<&str>,
) -> pimalaya_email::Result<Messages> {
unimplemented!() unimplemented!()
} }
fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<Emails> { async fn preview_emails(
&mut self,
_: &str,
_: Vec<&str>,
) -> pimalaya_email::Result<Messages> {
unimplemented!() 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!() 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!() unimplemented!()
} }
fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> backend::Result<()> { async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> pimalaya_email::Result<()> {
unimplemented!() 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!() 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!() 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!() unimplemented!()
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -218,13 +271,15 @@ mod tests {
let mut printer = PrinterServiceTest::default(); let mut printer = PrinterServiceTest::default();
let mut backend = TestBackend {}; 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!( assert_eq!(
concat![ concat![
"\n", "\n",
"DELIM │NAME │DESC \n", "NAME │DESC \n",
"/ │INBOX │desc \n", "INBOX │desc \n",
"/ │Sent │desc \n", "Sent │desc \n",
"\n" "\n"
], ],
printer.writer.content printer.writer.content

View file

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::Input; use dialoguer::Input;
use pimalaya_email::{SenderConfig, SendmailConfig}; use pimalaya_email::sender::{SenderConfig, SendmailConfig};
use crate::config::wizard::THEME; use crate::config::wizard::THEME;

View file

@ -1,8 +1,8 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::{Confirm, Input, Select}; use dialoguer::{Confirm, Input, Select};
use pimalaya_email::{ use pimalaya_email::{
OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, SenderConfig, SmtpAuthConfig, account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig},
SmtpConfig, sender::{SenderConfig, SmtpAuthConfig, SmtpConfig},
}; };
use pimalaya_oauth2::{AuthorizationCodeGrant, Client}; use pimalaya_oauth2::{AuthorizationCodeGrant, Client};
use pimalaya_secret::Secret; use pimalaya_secret::Secret;

View file

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use dialoguer::Select; use dialoguer::Select;
use pimalaya_email::SenderConfig; use pimalaya_email::sender::SenderConfig;
use crate::config::wizard::THEME; use crate::config::wizard::THEME;

View file

@ -1,11 +1,16 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use atty::Stream; 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 std::io::{stdin, BufRead};
use crate::{printer::Printer, IdMapper}; use crate::{printer::Printer, IdMapper};
pub fn forward<P: Printer>( pub async fn forward<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -19,19 +24,21 @@ pub fn forward<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let tpl: String = backend let tpl: String = backend
.get_emails(folder, ids)? .get_emails(folder, ids)
.await?
.first() .first()
.ok_or_else(|| anyhow!("cannot find email {}", id))? .ok_or_else(|| anyhow!("cannot find email {}", id))?
.to_forward_tpl_builder(config) .to_forward_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.build()? .build()
.await?
.into(); .into();
printer.print(tpl) printer.print(tpl)
} }
pub fn reply<P: Printer>( pub async fn reply<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -46,20 +53,22 @@ pub fn reply<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>(); let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
let tpl: String = backend let tpl: String = backend
.get_emails(folder, ids)? .get_emails(folder, ids)
.await?
.first() .first()
.ok_or_else(|| anyhow!("cannot find email {}", id))? .ok_or_else(|| anyhow!("cannot find email {}", id))?
.to_reply_tpl_builder(config) .to_reply_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.reply_all(all) .with_reply_all(all)
.build()? .build()
.await?
.into(); .into();
printer.print(tpl) printer.print(tpl)
} }
pub fn save<P: Printer>( pub async fn save<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
id_mapper: &IdMapper, id_mapper: &IdMapper,
@ -79,16 +88,17 @@ pub fn save<P: Printer>(
}) })
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone())
.compile()? .compile()
.await?
.write_to_vec()?; .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)?; id_mapper.create_alias(id)?;
printer.print("Template successfully saved!") printer.print("Template successfully saved!")
} }
pub fn send<P: Printer>( pub async fn send<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -108,26 +118,31 @@ pub fn send<P: Printer>(
}) })
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone())
.compile()? .compile()
.await?
.write_to_vec()?; .write_to_vec()?;
sender.send(&email)?;
sender.send(&email).await?;
if config.email_sending_save_copy { 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!")?; printer.print("Template successfully sent!")?;
Ok(()) Ok(())
} }
pub fn write<P: Printer>( pub async fn write<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
headers: Option<Vec<(&str, &str)>>, headers: Option<Vec<(&str, &str)>>,
body: Option<&str>, body: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let tpl: String = Email::new_tpl_builder(config) let tpl: String = Message::new_tpl_builder(config)
.some_headers(headers) .with_some_headers(headers)
.some_body(body) .with_some_body(body)
.build()? .build()
.await?
.into(); .into();
printer.print(tpl) printer.print(tpl)

View file

@ -1,9 +1,11 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use clap::Command; use clap::Command;
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use pimalaya_email::ImapBackend; use pimalaya_email::backend::ImapBackend;
use pimalaya_email::{ 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 std::env;
use url::Url; use url::Url;
@ -54,17 +56,20 @@ async fn main() -> Result<()> {
let url = Url::parse(&raw_args[1])?; let url = Url::parse(&raw_args[1])?;
let config = DeserializedConfig::from_opt_path(None)?; let config = DeserializedConfig::from_opt_path(None)?;
let account_config = config.to_account_config(None)?; let account_config = config.to_account_config(None)?;
let mut backend = BackendBuilder::new(account_config.clone()).build()?; let mut backend = BackendBuilder::new(account_config.clone()).build().await?;
let mut sender = SenderBuilder::new(account_config.clone()).build()?; let mut sender = SenderBuilder::new(account_config.clone()).build().await?;
let mut printer = StdoutPrinter::default(); let mut printer = StdoutPrinter::default();
return email::handlers::mailto( email::handlers::mailto(
&account_config, &account_config,
backend.as_mut(), backend.as_mut(),
sender.as_mut(), sender.as_mut(),
&mut printer, &mut printer,
&url, &url,
); )
.await?;
return Ok(());
} }
let app = create_app(); let app = create_app();
@ -107,13 +112,15 @@ async fn main() -> Result<()> {
match imap::args::matches(&m)? { match imap::args::matches(&m)? {
Some(imap::args::Cmd::Notify(keepalive)) => { Some(imap::args::Cmd::Notify(keepalive)) => {
let mut backend = let mut backend =
ImapBackend::new(account_config.clone(), imap_config.clone(), None)?; ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?;
return imap::handlers::notify(&mut backend, &folder, keepalive); imap::handlers::notify(&mut backend, &folder, keepalive).await?;
return Ok(());
} }
Some(imap::args::Cmd::Watch(keepalive)) => { Some(imap::args::Cmd::Watch(keepalive)) => {
let mut backend = let mut backend =
ImapBackend::new(account_config.clone(), imap_config.clone(), None)?; ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?;
return imap::handlers::watch(&mut backend, &folder, keepalive); imap::handlers::watch(&mut backend, &folder, keepalive).await?;
return Ok(());
} }
_ => (), _ => (),
} }
@ -121,17 +128,20 @@ async fn main() -> Result<()> {
match account::args::matches(&m)? { match account::args::matches(&m)? {
Some(account::args::Cmd::List(max_width)) => { 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)) => { 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_some_folders_strategy(strategy)
.with_dry_run(dry_run); .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(()); return Ok(());
} }
Some(account::args::Cmd::Configure(reset)) => { 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")) .ok_or_else(|| anyhow!("the folder argument is missing"))
.context("cannot create folder")?; .context("cannot create folder")?;
let folder = account_config.folder_alias(folder)?; let folder = account_config.folder_alias(folder)?;
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
return folder::handlers::create(&mut printer, backend.as_mut(), &folder); folder::handlers::create(&mut printer, backend.as_mut(), &folder).await?;
return Ok(());
} }
Some(folder::args::Cmd::List(max_width)) => { Some(folder::args::Cmd::List(max_width)) => {
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
return folder::handlers::list( folder::handlers::list(&account_config, &mut printer, backend.as_mut(), max_width)
&account_config, .await?;
&mut printer, return Ok(());
backend.as_mut(),
max_width,
);
} }
Some(folder::args::Cmd::Expunge) => { Some(folder::args::Cmd::Expunge) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?; let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder); folder::handlers::expunge(&mut printer, backend.as_mut(), &folder).await?;
return Ok(());
} }
Some(folder::args::Cmd::Delete) => { Some(folder::args::Cmd::Delete) => {
let folder = folder let folder = folder
.ok_or_else(|| anyhow!("the folder argument is missing")) .ok_or_else(|| anyhow!("the folder argument is missing"))
.context("cannot delete folder")?; .context("cannot delete folder")?;
let folder = account_config.folder_alias(folder)?; let folder = account_config.folder_alias(folder)?;
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder); 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)? { match email::args::matches(&m)? {
Some(email::args::Cmd::Attachments(ids)) => { Some(email::args::Cmd::Attachments(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::attachments( email::handlers::attachments(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
ids, ids,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Copy(ids, to_folder)) => { Some(email::args::Cmd::Copy(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::copy( email::handlers::copy(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -199,29 +211,35 @@ async fn main() -> Result<()> {
&folder, &folder,
to_folder, to_folder,
ids, ids,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Delete(ids)) => { Some(email::args::Cmd::Delete(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::delete( email::handlers::delete(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
ids, ids,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Forward(id, headers, body)) => { Some(email::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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 mut sender = sender_builder.build()?; let mut sender = sender_builder.build().await?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::forward( email::handlers::forward(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -231,14 +249,17 @@ async fn main() -> Result<()> {
id, id,
headers, headers,
body, body,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::List(max_width, page_size, page)) => { Some(email::args::Cmd::List(max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::list( email::handlers::list(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -247,14 +268,17 @@ async fn main() -> Result<()> {
max_width, max_width,
page_size, page_size,
page, page,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Move(ids, to_folder)) => { Some(email::args::Cmd::Move(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::move_( email::handlers::move_(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -262,14 +286,17 @@ async fn main() -> Result<()> {
&folder, &folder,
to_folder, to_folder,
ids, ids,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Read(ids, text_mime, raw, headers)) => { Some(email::args::Cmd::Read(ids, text_mime, raw, headers)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::read( email::handlers::read(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -279,15 +306,18 @@ async fn main() -> Result<()> {
text_mime, text_mime,
raw, raw,
headers, headers,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Reply(id, all, headers, body)) => { Some(email::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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 mut sender = sender_builder.build()?; let mut sender = sender_builder.build().await?;
let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::reply( email::handlers::reply(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -298,28 +328,34 @@ async fn main() -> Result<()> {
all, all,
headers, headers,
body, body,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Save(raw_email)) => { Some(email::args::Cmd::Save(raw_email)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::save( email::handlers::save(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
raw_email, raw_email,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Search(query, max_width, page_size, page)) => { Some(email::args::Cmd::Search(query, max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::search( email::handlers::search(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -329,14 +365,17 @@ async fn main() -> Result<()> {
max_width, max_width,
page_size, page_size,
page, page,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => { 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 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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return email::handlers::sort( email::handlers::sort(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -347,71 +386,86 @@ async fn main() -> Result<()> {
max_width, max_width,
page_size, page_size,
page, page,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Send(raw_email)) => { Some(email::args::Cmd::Send(raw_email)) => {
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
let mut sender = sender_builder.build()?; let mut sender = sender_builder.build().await?;
return email::handlers::send( email::handlers::send(
&account_config, &account_config,
&mut printer, &mut printer,
backend.as_mut(), backend.as_mut(),
sender.as_mut(), sender.as_mut(),
raw_email, raw_email,
); )
.await?;
return Ok(());
} }
Some(email::args::Cmd::Flag(m)) => match m { Some(email::args::Cmd::Flag(m)) => match m {
Some(flag::args::Cmd::Set(ids, ref flags)) => { Some(flag::args::Cmd::Set(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::set( flag::handlers::set(
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
ids, ids,
flags, flags,
); )
.await?;
return Ok(());
} }
Some(flag::args::Cmd::Add(ids, ref flags)) => { Some(flag::args::Cmd::Add(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::add( flag::handlers::add(
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
ids, ids,
flags, flags,
); )
.await?;
return Ok(());
} }
Some(flag::args::Cmd::Remove(ids, ref flags)) => { Some(flag::args::Cmd::Remove(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return flag::handlers::remove( flag::handlers::remove(
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
ids, ids,
flags, flags,
); )
.await?;
return Ok(());
} }
_ => (), _ => (),
}, },
Some(email::args::Cmd::Tpl(m)) => match m { Some(email::args::Cmd::Tpl(m)) => match m {
Some(tpl::args::Cmd::Forward(id, headers, body)) => { Some(tpl::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::forward( tpl::handlers::forward(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -420,17 +474,21 @@ async fn main() -> Result<()> {
id, id,
headers, headers,
body, body,
); )
.await?;
return Ok(());
} }
Some(tpl::args::Cmd::Write(headers, body)) => { 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)) => { Some(tpl::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::reply( tpl::handlers::reply(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
@ -440,48 +498,60 @@ async fn main() -> Result<()> {
all, all,
headers, headers,
body, body,
); )
.await?;
return Ok(());
} }
Some(tpl::args::Cmd::Save(tpl)) => { Some(tpl::args::Cmd::Save(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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)?; let id_mapper = IdMapper::new(backend.as_ref(), &account_name, &folder)?;
return tpl::handlers::save( tpl::handlers::save(
&account_config, &account_config,
&mut printer, &mut printer,
&id_mapper, &id_mapper,
backend.as_mut(), backend.as_mut(),
&folder, &folder,
tpl, tpl,
); )
.await?;
return Ok(());
} }
Some(tpl::args::Cmd::Send(tpl)) => { Some(tpl::args::Cmd::Send(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_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 mut sender = sender_builder.build()?; let mut sender = sender_builder.build().await?;
return tpl::handlers::send( tpl::handlers::send(
&account_config, &account_config,
&mut printer, &mut printer,
backend.as_mut(), backend.as_mut(),
sender.as_mut(), sender.as_mut(),
&folder, &folder,
tpl, tpl,
); )
.await?;
return Ok(());
} }
_ => (), _ => (),
}, },
Some(email::args::Cmd::Write(headers, body)) => { Some(email::args::Cmd::Write(headers, body)) => {
let mut backend = backend_builder.build()?; let mut backend = backend_builder.build().await?;
let mut sender = sender_builder.build()?; let mut sender = sender_builder.build().await?;
return email::handlers::write( email::handlers::write(
&account_config, &account_config,
&mut printer, &mut printer,
backend.as_mut(), backend.as_mut(),
sender.as_mut(), sender.as_mut(),
headers, headers,
body, body,
); )
.await?;
return Ok(());
} }
_ => (), _ => (),
} }

View file

@ -1,5 +1,5 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use pimalaya_email::Tpl; use pimalaya_email::email::Tpl;
use crate::printer::WriteColor; use crate::printer::WriteColor;

View file

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use pimalaya_email::EmailTextPlainFormat; use pimalaya_email::email::EmailTextPlainFormat;
use std::io; use std::io;
use termcolor::{self, StandardStream}; use termcolor::{self, StandardStream};

View file

@ -1,8 +1,10 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use log::debug; use log::debug;
use pimalaya_email::{ use pimalaya_email::{
email::{local_draft_path, remove_local_draft}, account::AccountConfig,
AccountConfig, Backend, Flag, Flags, Sender, Tpl, backend::Backend,
email::{local_draft_path, remove_local_draft, Flag, Flags, Tpl},
sender::Sender,
}; };
use std::{env, fs, process::Command}; use std::{env, fs, process::Command};
@ -37,7 +39,7 @@ pub fn open_with_local_draft() -> Result<Tpl> {
open_with_tpl(Tpl::from(content)) open_with_tpl(Tpl::from(content))
} }
pub fn edit_tpl_with_editor<P: Printer>( pub async fn edit_tpl_with_editor<P: Printer>(
config: &AccountConfig, config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &mut dyn Backend, backend: &mut dyn Backend,
@ -76,13 +78,16 @@ pub fn edit_tpl_with_editor<P: Printer>(
let email = tpl let email = tpl
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone())
.compile()? .compile()
.await?
.write_to_vec()?; .write_to_vec()?;
sender.send(&email)?; sender.send(&email).await?;
if config.email_sending_save_copy { if config.email_sending_save_copy {
let sent_folder = config.sent_folder_alias()?; let sent_folder = config.sent_folder_alias()?;
printer.print_log(format!("Adding email to the {} folder…", sent_folder))?; 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()?; remove_local_draft()?;
printer.print("Done!")?; printer.print("Done!")?;
@ -101,13 +106,16 @@ pub fn edit_tpl_with_editor<P: Printer>(
let email = tpl let email = tpl
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone())
.compile()? .compile()
.await?
.write_to_vec()?; .write_to_vec()?;
backend.add_email( backend
&draft_folder, .add_email(
&email, &draft_folder,
&Flags::from_iter([Flag::Seen, Flag::Draft]), &email,
)?; &Flags::from_iter([Flag::Seen, Flag::Draft]),
)
.await?;
remove_local_draft()?; remove_local_draft()?;
printer.print(format!("Email successfully saved to {}", draft_folder))?; printer.print(format!("Email successfully saved to {}", draft_folder))?;
break; break;

View file

@ -6,7 +6,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use log::trace; use log::trace;
use pimalaya_email::EmailTextPlainFormat; use pimalaya_email::email::EmailTextPlainFormat;
use termcolor::{Color, ColorSpec}; use termcolor::{Color, ColorSpec};
use terminal_size::terminal_size; use terminal_size::terminal_size;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
@ -267,6 +267,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pimalaya_email::email::EmailTextPlainFormat;
use std::io; use std::io;
use super::*; use super::*;