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"
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",

View file

@ -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"

View file

@ -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";

View file

@ -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]

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")]
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;

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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<ProgressStyle> = 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<P: Printer>(
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::<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(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;

View file

@ -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(())
}

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -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<P: Printer>(
pub async fn attachments<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -28,7 +31,7 @@ pub fn attachments<P: Printer>(
let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?;
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 emails_count = 0;
@ -57,7 +60,7 @@ pub fn attachments<P: Printer>(
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<P: Printer>(
}
}
pub fn copy<P: Printer>(
pub async fn copy<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -87,11 +90,11 @@ pub fn copy<P: Printer>(
let to_folder = config.folder_alias(to_folder)?;
let ids = id_mapper.get_ids(ids)?;
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!")
}
pub fn delete<P: Printer>(
pub async fn delete<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -102,11 +105,11 @@ pub fn delete<P: Printer>(
let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?;
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!")
}
pub fn forward<P: Printer>(
pub async fn forward<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -123,19 +126,21 @@ pub fn forward<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
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<P: Printer>(
pub async fn list<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -152,7 +157,7 @@ pub fn list<P: Printer>(
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<P: Printer>(
/// Parses and edits a message from a [mailto] URL string.
///
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
pub fn mailto<P: Printer>(
pub async fn mailto<P: Printer>(
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<P: Printer>(
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_<P: Printer>(
pub async fn move_<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -208,11 +214,11 @@ pub fn move_<P: Printer>(
let to_folder = config.folder_alias(to_folder)?;
let ids = id_mapper.get_ids(ids)?;
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!")
}
pub fn read<P: Printer>(
pub async fn read<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -226,7 +232,7 @@ pub fn read<P: Printer>(
let folder = config.folder_alias(folder)?;
let ids = id_mapper.get_ids(ids)?;
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 bodies = String::default();
@ -245,7 +251,8 @@ pub fn read<P: Printer>(
.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<P: Printer>(
printer.print(bodies)
}
pub fn reply<P: Printer>(
pub async fn reply<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -274,21 +281,25 @@ pub fn reply<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
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<P: Printer>(
pub async fn save<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -310,13 +321,15 @@ pub fn save<P: Printer>(
.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<P: Printer>(
pub async fn search<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -332,7 +345,9 @@ pub fn search<P: Printer>(
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<P: Printer>(
printer.print_table(Box::new(envelopes), opts)
}
pub fn sort<P: Printer>(
pub async fn sort<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -359,7 +374,9 @@ pub fn sort<P: Printer>(
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<P: Printer>(
printer.print_table(Box::new(envelopes), opts)
}
pub fn send<P: Printer>(
pub async fn send<P: Printer>(
config: &AccountConfig,
printer: &mut P,
backend: &mut dyn Backend,
@ -390,18 +407,20 @@ pub fn send<P: Printer>(
.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<P: Printer>(
pub async fn write<P: Printer>(
config: &AccountConfig,
printer: &mut P,
backend: &mut dyn Backend,
@ -409,11 +428,12 @@ pub fn write<P: Printer>(
headers: Option<Vec<(&str, &str)>>,
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(())
}

View file

@ -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<Envelopes> {
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";

View file

@ -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;

View file

@ -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()),
}
}
}

View file

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

View file

@ -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<P: Printer>(
pub async fn add<P: Printer>(
printer: &mut P,
id_mapper: &IdMapper,
backend: &mut dyn Backend,
@ -13,11 +13,11 @@ pub fn add<P: Printer>(
) -> Result<()> {
let ids = id_mapper.get_ids(ids)?;
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!")
}
pub fn set<P: Printer>(
pub async fn set<P: Printer>(
printer: &mut P,
id_mapper: &IdMapper,
backend: &mut dyn Backend,
@ -27,11 +27,11 @@ pub fn set<P: Printer>(
) -> Result<()> {
let ids = id_mapper.get_ids(ids)?;
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!")
}
pub fn remove<P: Printer>(
pub async fn remove<P: Printer>(
printer: &mut P,
id_mapper: &IdMapper,
backend: &mut dyn Backend,
@ -41,6 +41,6 @@ pub fn remove<P: Printer>(
) -> Result<()> {
let ids = id_mapper.get_ids(ids)?;
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!")
}

View file

@ -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())
}

View file

@ -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<pimalaya_email::Folders> for Folders {
fn from(folders: pimalaya_email::Folders) -> Self {
impl From<pimalaya_email::folder::Folders> for Folders {
fn from(folders: pimalaya_email::folder::Folders) -> Self {
Folders(folders.iter().map(Folder::from).collect())
}
}

View file

@ -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<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> {
backend.expunge_folder(folder)?;
pub async fn expunge<P: Printer>(
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<P: Printer>(
pub async fn list<P: Printer>(
config: &AccountConfig,
printer: &mut P,
backend: &mut dyn Backend,
max_width: Option<usize>,
) -> 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<P: Printer>(
)
}
pub fn create<P: Printer>(printer: &mut P, backend: &mut dyn Backend, folder: &str) -> Result<()> {
backend.add_folder(folder)?;
pub async fn create<P: Printer>(
printer: &mut P,
backend: &mut dyn Backend,
folder: &str,
) -> Result<()> {
backend.add_folder(folder).await?;
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()
.with_prompt(format!("Confirm deletion of folder {folder}?"))
.default(false)
@ -49,14 +61,18 @@ pub fn delete<P: Printer>(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<Folders> {
async fn list_folders(&mut self) -> pimalaya_email::Result<Folders> {
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<Envelope> {
async fn get_envelope(&mut self, _: &str, _: &str) -> pimalaya_email::Result<Envelope> {
unimplemented!();
}
fn list_envelopes(
async fn list_envelopes(
&mut self,
_: &str,
_: usize,
_: usize,
) -> backend::Result<Envelopes> {
) -> pimalaya_email::Result<Envelopes> {
unimplemented!()
}
fn search_envelopes(
async fn search_envelopes(
&mut self,
_: &str,
_: &str,
_: &str,
_: usize,
_: usize,
) -> backend::Result<Envelopes> {
) -> pimalaya_email::Result<Envelopes> {
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!()
}
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!()
}
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!()
}
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

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -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<P: Printer>(
pub async fn forward<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -19,19 +24,21 @@ pub fn forward<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
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<P: Printer>(
pub async fn reply<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -46,20 +53,22 @@ pub fn reply<P: Printer>(
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
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<P: Printer>(
pub async fn save<P: Printer>(
config: &AccountConfig,
printer: &mut P,
id_mapper: &IdMapper,
@ -79,16 +88,17 @@ pub fn save<P: Printer>(
})
.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<P: Printer>(
pub async fn send<P: Printer>(
config: &AccountConfig,
printer: &mut P,
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_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<P: Printer>(
pub async fn write<P: Printer>(
config: &AccountConfig,
printer: &mut P,
headers: Option<Vec<(&str, &str)>>,
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)

View file

@ -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(());
}
_ => (),
}

View file

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

View file

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

View file

@ -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<Tpl> {
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,
printer: &mut P,
backend: &mut dyn Backend,
@ -76,13 +78,16 @@ pub fn edit_tpl_with_editor<P: Printer>(
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<P: Printer>(
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;

View file

@ -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::*;