mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
bump lib with backend features
This commit is contained in:
parent
56fc31b367
commit
cec658aff4
2730
Cargo.lock
generated
2730
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
16
Cargo.toml
16
Cargo.toml
|
@ -42,6 +42,9 @@ version = "3.3"
|
|||
[dependencies.anyhow]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.async-trait]
|
||||
version = "0.1"
|
||||
|
||||
[dependencies.atty]
|
||||
version = "0.2"
|
||||
|
||||
|
@ -88,21 +91,23 @@ version = "0.7.0"
|
|||
version = "1.16.0"
|
||||
|
||||
[dependencies.email-lib]
|
||||
version = "=0.15.3"
|
||||
# version = "=0.15.3"
|
||||
default-features = false
|
||||
path = "/home/soywod/sourcehut/pimalaya/email"
|
||||
|
||||
[dependencies.keyring-lib]
|
||||
version = "=0.1.0"
|
||||
|
||||
[dependencies.oauth-lib]
|
||||
version = "=0.1.0"
|
||||
# version = "=0.1.0"
|
||||
path = "/home/soywod/sourcehut/pimalaya/oauth"
|
||||
|
||||
[dependencies.process-lib]
|
||||
version = "=0.1.0"
|
||||
|
||||
[dependencies.mml-lib]
|
||||
version = "=0.5.0"
|
||||
default-features = false
|
||||
# version = "=1.0.1"
|
||||
path = "/home/soywod/sourcehut/pimalaya/mml"
|
||||
|
||||
[dependencies.secret-lib]
|
||||
version = "=0.1.0"
|
||||
|
@ -115,7 +120,8 @@ features = ["derive"]
|
|||
version = "1.0"
|
||||
|
||||
[dependencies.shellexpand-utils]
|
||||
version = "=0.1.0"
|
||||
# version = "=0.1.0"
|
||||
path = "/home/soywod/sourcehut/pimalaya/shellexpand-utils"
|
||||
|
||||
[dependencies.termcolor]
|
||||
version = "1.1"
|
||||
|
|
|
@ -1,54 +1,18 @@
|
|||
display-name = "Display NAME"
|
||||
signature-delim = "~~"
|
||||
signature = "~/.signature"
|
||||
downloads-dir = "~/downloads"
|
||||
folder-listing-page-size = 12
|
||||
email-listing-page-size = 12
|
||||
email-reading-headers = ["From", "To"]
|
||||
email-reading-verify-cmd = "gpg --verify -q"
|
||||
email-reading-decrypt-cmd = "gpg -dq"
|
||||
email-writing-sign-cmd = "gpg -o - -saq"
|
||||
email-writing-encrypt-cmd = "gpg -o - -eqar <recipient>"
|
||||
|
||||
[example]
|
||||
default = true
|
||||
display-name = "Display NAME (gmail)"
|
||||
email = "display.name@gmail.local"
|
||||
|
||||
# The display-name and email are used to build the full email address:
|
||||
# "My example account" <example@localhost>
|
||||
display-name = "My example account"
|
||||
email = "example@localhost"
|
||||
|
||||
# The default backend used for all the features like adding folders,
|
||||
# listing envelopes or copying messages.
|
||||
backend = "imap"
|
||||
imap-host = "imap.gmail.com"
|
||||
imap-login = "display.name@gmail.local"
|
||||
imap-auth = "passwd"
|
||||
imap-passwd.cmd = "pass show gmail"
|
||||
imap-port = 993
|
||||
imap-ssl = true
|
||||
imap-starttls = false
|
||||
imap-notify-cmd = """📫 "<sender>" "<subject>""""
|
||||
imap-notify-query = "NOT SEEN"
|
||||
imap-watch-cmds = ["echo \"received server changes!\""]
|
||||
|
||||
sender = "smtp"
|
||||
smtp-host = "smtp.gmail.com"
|
||||
smtp-login = "display.name@gmail.local"
|
||||
smtp-auth = "passwd"
|
||||
smtp-passwd.cmd = "pass show piana/gmail"
|
||||
smtp-port = 465
|
||||
smtp-ssl = true
|
||||
smtp-starttls = false
|
||||
imap.host = "imap.gmail.com"
|
||||
imap.port = 993
|
||||
imap.login = "example@localhost"
|
||||
imap.auth = "passwd"
|
||||
# imap.Some.passwd.cmd = "pass show gmail"
|
||||
|
||||
sync = true
|
||||
sync-dir = "/tmp/sync/gmail"
|
||||
sync-folders-strategy.include = ["INBOX"]
|
||||
|
||||
[example.folder-aliases]
|
||||
inbox = "INBOX"
|
||||
drafts = "[Gmail]/Drafts"
|
||||
sent = "[Gmail]/Sent Mail"
|
||||
trash = "[Gmail]/Trash"
|
||||
|
||||
[example.email-hooks]
|
||||
pre-send = "echo $1"
|
||||
|
||||
[example.email-reading-format]
|
||||
type = "fixed"
|
||||
width = 64
|
||||
|
|
237
src/backend.rs
Normal file
237
src/backend.rs
Normal file
|
@ -0,0 +1,237 @@
|
|||
use std::{collections::HashMap, ops::Deref};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::imap::{ImapSessionBuilder, ImapSessionSync};
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
use email::smtp::{SmtpClientBuilder, SmtpClientSync};
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
config::Config,
|
||||
folder::list::{imap::ListFoldersImap, maildir::ListFoldersMaildir},
|
||||
maildir::{MaildirSessionBuilder, MaildirSessionSync},
|
||||
sendmail::SendmailContext,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::DeserializedConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BackendKind {
|
||||
#[default]
|
||||
None,
|
||||
Maildir,
|
||||
#[cfg(feature = "imap-backend")]
|
||||
Imap,
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
Notmuch,
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
Smtp,
|
||||
Sendmail,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BackendContextBuilder {
|
||||
account_config: AccountConfig,
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
imap: Option<ImapSessionBuilder>,
|
||||
maildir: Option<MaildirSessionBuilder>,
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
smtp: Option<SmtpClientBuilder>,
|
||||
sendmail: Option<SendmailContext>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl email::backend::BackendContextBuilder for BackendContextBuilder {
|
||||
type Context = BackendContext;
|
||||
|
||||
async fn build(self) -> Result<Self::Context> {
|
||||
let mut ctx = BackendContext::default();
|
||||
|
||||
if let Some(maildir) = self.maildir {
|
||||
ctx.maildir = Some(maildir.build().await?);
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let Some(imap) = self.imap {
|
||||
ctx.imap = Some(imap.build().await?);
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
if let Some(notmuch) = self.notmuch {
|
||||
ctx.notmuch = Some(notmuch.build().await?);
|
||||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let Some(smtp) = self.smtp {
|
||||
ctx.smtp = Some(smtp.build().await?);
|
||||
}
|
||||
|
||||
if let Some(sendmail) = self.sendmail {
|
||||
ctx.sendmail = Some(sendmail.build().await?);
|
||||
}
|
||||
|
||||
Ok(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BackendContext {
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub imap: Option<ImapSessionSync>,
|
||||
pub maildir: Option<MaildirSessionSync>,
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
pub smtp: Option<SmtpClientSync>,
|
||||
pub sendmail: Option<SendmailContext>,
|
||||
}
|
||||
|
||||
pub struct BackendBuilder(pub email::backend::BackendBuilder<BackendContextBuilder>);
|
||||
|
||||
impl BackendBuilder {
|
||||
pub async fn new(config: DeserializedConfig, account_name: Option<&str>) -> Result<Self> {
|
||||
let (account_name, deserialized_account_config) = match account_name {
|
||||
Some("default") | Some("") | None => config
|
||||
.accounts
|
||||
.iter()
|
||||
.find_map(|(name, account)| {
|
||||
account
|
||||
.default
|
||||
.filter(|default| *default == true)
|
||||
.map(|_| (name.to_owned(), account.clone()))
|
||||
})
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => config
|
||||
.accounts
|
||||
.get(name)
|
||||
.map(|account| (name.to_owned(), account.clone()))
|
||||
.ok_or_else(|| anyhow!("cannot find account {name}")),
|
||||
}?;
|
||||
|
||||
let config = Config {
|
||||
display_name: config.display_name,
|
||||
signature_delim: config.signature_delim,
|
||||
signature: config.signature,
|
||||
downloads_dir: config.downloads_dir,
|
||||
|
||||
folder_listing_page_size: config.folder_listing_page_size,
|
||||
folder_aliases: config.folder_aliases,
|
||||
|
||||
email_listing_page_size: config.email_listing_page_size,
|
||||
email_listing_datetime_fmt: config.email_listing_datetime_fmt,
|
||||
email_listing_datetime_local_tz: config.email_listing_datetime_local_tz,
|
||||
email_reading_headers: config.email_reading_headers,
|
||||
email_reading_format: config.email_reading_format,
|
||||
email_writing_headers: config.email_writing_headers,
|
||||
email_sending_save_copy: config.email_sending_save_copy,
|
||||
email_hooks: config.email_hooks,
|
||||
|
||||
accounts: HashMap::from_iter(config.accounts.clone().into_iter().map(
|
||||
|(name, config)| {
|
||||
(
|
||||
name.clone(),
|
||||
AccountConfig {
|
||||
name,
|
||||
email: config.email,
|
||||
display_name: config.display_name,
|
||||
signature_delim: config.signature_delim,
|
||||
signature: config.signature,
|
||||
downloads_dir: config.downloads_dir,
|
||||
|
||||
folder_listing_page_size: config.folder_listing_page_size,
|
||||
folder_aliases: config.folder_aliases.unwrap_or_default(),
|
||||
|
||||
email_listing_page_size: config.email_listing_page_size,
|
||||
email_listing_datetime_fmt: config.email_listing_datetime_fmt,
|
||||
email_listing_datetime_local_tz: config.email_listing_datetime_local_tz,
|
||||
|
||||
email_reading_headers: config.email_reading_headers,
|
||||
email_reading_format: config.email_reading_format.unwrap_or_default(),
|
||||
email_writing_headers: config.email_writing_headers,
|
||||
email_sending_save_copy: config.email_sending_save_copy,
|
||||
email_hooks: config.email_hooks.unwrap_or_default(),
|
||||
|
||||
sync: config.sync,
|
||||
sync_dir: config.sync_dir,
|
||||
sync_folders_strategy: config.sync_folders_strategy.unwrap_or_default(),
|
||||
|
||||
#[cfg(feature = "pgp")]
|
||||
pgp: config.pgp,
|
||||
},
|
||||
)
|
||||
},
|
||||
)),
|
||||
};
|
||||
|
||||
let account_config = config.account(account_name)?;
|
||||
|
||||
let backend_ctx_builder = BackendContextBuilder {
|
||||
account_config: account_config.clone(),
|
||||
maildir: deserialized_account_config
|
||||
.maildir
|
||||
.as_ref()
|
||||
.map(|mdir_config| {
|
||||
MaildirSessionBuilder::new(account_config.clone(), mdir_config.clone())
|
||||
}),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
imap: deserialized_account_config
|
||||
.imap
|
||||
.as_ref()
|
||||
.map(|imap_config| {
|
||||
ImapSessionBuilder::new(account_config.clone(), imap_config.clone())
|
||||
}),
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
smtp: deserialized_account_config
|
||||
.smtp
|
||||
.as_ref()
|
||||
.map(|smtp_config| {
|
||||
SmtpClientBuilder::new(account_config.clone(), smtp_config.clone())
|
||||
}),
|
||||
sendmail: deserialized_account_config
|
||||
.sendmail
|
||||
.as_ref()
|
||||
.map(|sendmail_config| {
|
||||
SendmailContext::new(account_config.clone(), sendmail_config.clone())
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let backend_builder =
|
||||
email::backend::BackendBuilder::new(account_config.clone(), backend_ctx_builder)
|
||||
.with_list_folders(move |ctx| {
|
||||
println!(
|
||||
"deserialized_account_config: {:#?}",
|
||||
deserialized_account_config
|
||||
);
|
||||
match deserialized_account_config.backend {
|
||||
BackendKind::Maildir if ctx.maildir.is_some() => {
|
||||
ListFoldersMaildir::new(ctx.maildir.as_ref().unwrap())
|
||||
}
|
||||
#[cfg(feature = "imap-backend")]
|
||||
BackendKind::Imap if ctx.imap.is_some() => {
|
||||
ListFoldersImap::new(ctx.imap.as_ref().unwrap())
|
||||
}
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
BackendKind::Notmuch if ctx.notmuch.is_some() => {
|
||||
ListFoldersNotmuch::new(ctx.notmuch.as_ref().unwrap())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self(backend_builder))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BackendBuilder {
|
||||
type Target = email::backend::BackendBuilder<BackendContextBuilder>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub type Backend = email::backend::Backend<BackendContext>;
|
73
src/cache/id_mapper.rs
vendored
73
src/cache/id_mapper.rs
vendored
|
@ -1,15 +1,12 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::backend::ImapBackend;
|
||||
use email::account::AccountConfig;
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use email::backend::NotmuchBackend;
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
backend::{Backend, MaildirBackend},
|
||||
};
|
||||
use log::{debug, trace};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::backend::Backend;
|
||||
|
||||
const ID_MAPPER_DB_FILE_NAME: &str = ".id-mapper.sqlite";
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -39,47 +36,45 @@ impl IdMapper {
|
|||
db_path
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
backend: &dyn Backend,
|
||||
account_config: &AccountConfig,
|
||||
folder: &str,
|
||||
) -> Result<Self> {
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if backend.as_any().is::<ImapBackend>() {
|
||||
return Ok(IdMapper::Dummy);
|
||||
}
|
||||
pub fn new(backend: &Backend, account_config: &AccountConfig, folder: &str) -> Result<Self> {
|
||||
Ok(IdMapper::Dummy)
|
||||
|
||||
let mut db_path = PathBuf::new();
|
||||
// #[cfg(feature = "imap-backend")]
|
||||
// if backend.as_any().is::<ImapBackend>() {
|
||||
// return Ok(IdMapper::Dummy);
|
||||
// }
|
||||
|
||||
if let Some(backend) = backend.as_any().downcast_ref::<MaildirBackend>() {
|
||||
db_path = Self::find_closest_db_path(backend.path())
|
||||
}
|
||||
// let mut db_path = PathBuf::new();
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
if let Some(backend) = backend.as_any().downcast_ref::<NotmuchBackend>() {
|
||||
db_path = Self::find_closest_db_path(backend.path())
|
||||
}
|
||||
// if let Some(backend) = backend.as_any().downcast_ref::<MaildirBackend>() {
|
||||
// db_path = Self::find_closest_db_path(backend.path())
|
||||
// }
|
||||
|
||||
let folder = account_config.get_folder_alias(folder)?;
|
||||
let digest = md5::compute(account_config.name.clone() + &folder);
|
||||
let table = format!("id_mapper_{digest:x}");
|
||||
debug!("creating id mapper table {table} at {db_path:?}…");
|
||||
// #[cfg(feature = "notmuch-backend")]
|
||||
// if let Some(backend) = backend.as_any().downcast_ref::<NotmuchBackend>() {
|
||||
// db_path = Self::find_closest_db_path(backend.path())
|
||||
// }
|
||||
|
||||
let conn = rusqlite::Connection::open(&db_path)
|
||||
.with_context(|| format!("cannot open id mapper database at {db_path:?}"))?;
|
||||
// let folder = account_config.get_folder_alias(folder)?;
|
||||
// let digest = md5::compute(account_config.name.clone() + &folder);
|
||||
// let table = format!("id_mapper_{digest:x}");
|
||||
// debug!("creating id mapper table {table} at {db_path:?}…");
|
||||
|
||||
let query = format!(
|
||||
"CREATE TABLE IF NOT EXISTS {table} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
internal_id TEXT UNIQUE
|
||||
)",
|
||||
);
|
||||
trace!("create table query: {query:#?}");
|
||||
// let conn = rusqlite::Connection::open(&db_path)
|
||||
// .with_context(|| format!("cannot open id mapper database at {db_path:?}"))?;
|
||||
|
||||
conn.execute(&query, [])
|
||||
.context("cannot create id mapper table")?;
|
||||
// let query = format!(
|
||||
// "CREATE TABLE IF NOT EXISTS {table} (
|
||||
// id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
// internal_id TEXT UNIQUE
|
||||
// )",
|
||||
// );
|
||||
// trace!("create table query: {query:#?}");
|
||||
|
||||
Ok(Self::Mapper(table, conn))
|
||||
// conn.execute(&query, [])
|
||||
// .context("cannot create id mapper table")?;
|
||||
|
||||
// Ok(Self::Mapper(table, conn))
|
||||
}
|
||||
|
||||
pub fn create_alias<I>(&self, id: I) -> Result<String>
|
||||
|
|
|
@ -6,12 +6,8 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use dialoguer::Confirm;
|
||||
use dirs::{config_dir, home_dir};
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
email::{EmailHooks, EmailTextPlainFormat},
|
||||
};
|
||||
use email::email::{EmailHooks, EmailTextPlainFormat};
|
||||
use log::{debug, trace};
|
||||
use process::Cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs, path::PathBuf, process::exit};
|
||||
use toml;
|
||||
|
@ -39,44 +35,12 @@ pub struct DeserializedConfig {
|
|||
pub email_listing_datetime_fmt: Option<String>,
|
||||
pub email_listing_datetime_local_tz: Option<bool>,
|
||||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailTextPlainFormatDef",
|
||||
skip_serializing_if = "EmailTextPlainFormat::is_default"
|
||||
)]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_verify_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||
#[serde(default, with = "OptionEmailTextPlainFormatDef")]
|
||||
pub email_reading_format: Option<EmailTextPlainFormat>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_sign_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||
pub email_sending_save_copy: Option<bool>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksDef",
|
||||
skip_serializing_if = "EmailHooks::is_empty"
|
||||
)]
|
||||
pub email_hooks: EmailHooks,
|
||||
#[serde(default, with = "OptionEmailHooksDef")]
|
||||
pub email_hooks: Option<EmailHooks>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub accounts: HashMap<String, DeserializedAccountConfig>,
|
||||
|
@ -134,28 +98,6 @@ impl DeserializedConfig {
|
|||
.or_else(|| home_dir().map(|p| p.join(".himalayarc")))
|
||||
.filter(|p| p.exists())
|
||||
}
|
||||
|
||||
pub fn to_account_config(&self, account_name: Option<&str>) -> Result<AccountConfig> {
|
||||
let (account_name, deserialized_account_config) = match account_name {
|
||||
Some("default") | Some("") | None => self
|
||||
.accounts
|
||||
.iter()
|
||||
.find_map(|(name, account)| {
|
||||
account
|
||||
.default
|
||||
.filter(|default| *default == true)
|
||||
.map(|_| (name.clone(), account))
|
||||
})
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => self
|
||||
.accounts
|
||||
.get(name)
|
||||
.map(|account| (name.to_string(), account))
|
||||
.ok_or_else(|| anyhow!(format!("cannot find account {}", name))),
|
||||
}?;
|
||||
|
||||
Ok(deserialized_account_config.to_account_config(account_name, self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -9,15 +9,15 @@ use email::account::{NativePgpConfig, NativePgpSecretKey, SignedSecretKey};
|
|||
#[cfg(feature = "notmuch-backend")]
|
||||
use email::backend::NotmuchConfig;
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::backend::{ImapAuthConfig, ImapConfig};
|
||||
use email::imap::{ImapAuthConfig, ImapConfig};
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
use email::sender::{SmtpAuthConfig, SmtpConfig};
|
||||
use email::smtp::{SmtpAuthConfig, SmtpConfig};
|
||||
use email::{
|
||||
account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig},
|
||||
backend::{BackendConfig, MaildirConfig},
|
||||
email::{EmailHooks, EmailTextPlainFormat},
|
||||
folder::sync::FolderSyncStrategy,
|
||||
sender::{SenderConfig, SendmailConfig},
|
||||
maildir::MaildirConfig,
|
||||
sendmail::SendmailConfig,
|
||||
};
|
||||
use keyring::Entry;
|
||||
use process::{Cmd, Pipeline, SingleCmd};
|
||||
|
@ -108,49 +108,47 @@ pub enum OAuth2MethodDef {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "BackendConfig", tag = "backend", rename_all = "kebab-case")]
|
||||
pub enum BackendConfigDef {
|
||||
#[default]
|
||||
None,
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[serde(with = "ImapConfigDef")]
|
||||
Imap(ImapConfig),
|
||||
#[serde(with = "MaildirConfigDef")]
|
||||
Maildir(MaildirConfig),
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[serde(with = "NotmuchConfigDef")]
|
||||
Notmuch(NotmuchConfig),
|
||||
#[serde(remote = "Option<ImapConfig>", from = "OptionImapConfig")]
|
||||
pub struct OptionImapConfigDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OptionImapConfig {
|
||||
#[serde(default, skip)]
|
||||
is_none: bool,
|
||||
#[serde(flatten, with = "ImapConfigDef")]
|
||||
inner: ImapConfig,
|
||||
}
|
||||
|
||||
impl From<OptionImapConfig> for Option<ImapConfig> {
|
||||
fn from(config: OptionImapConfig) -> Option<ImapConfig> {
|
||||
if config.is_none {
|
||||
None
|
||||
} else {
|
||||
Some(config.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "ImapConfig")]
|
||||
#[serde(remote = "ImapConfig", rename_all = "kebab-case")]
|
||||
pub struct ImapConfigDef {
|
||||
#[serde(rename = "imap-host")]
|
||||
pub host: String,
|
||||
#[serde(rename = "imap-port")]
|
||||
pub port: u16,
|
||||
#[serde(rename = "imap-ssl")]
|
||||
pub ssl: Option<bool>,
|
||||
#[serde(rename = "imap-starttls")]
|
||||
pub starttls: Option<bool>,
|
||||
#[serde(rename = "imap-insecure")]
|
||||
pub insecure: Option<bool>,
|
||||
#[serde(rename = "imap-login")]
|
||||
pub login: String,
|
||||
#[serde(flatten, with = "ImapAuthConfigDef")]
|
||||
pub auth: ImapAuthConfig,
|
||||
#[serde(rename = "imap-notify-cmd")]
|
||||
pub notify_cmd: Option<String>,
|
||||
#[serde(rename = "imap-notify-query")]
|
||||
pub notify_query: Option<String>,
|
||||
#[serde(rename = "imap-watch-cmds")]
|
||||
pub watch_cmds: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "ImapAuthConfig", tag = "imap-auth")]
|
||||
#[serde(remote = "ImapAuthConfig", tag = "auth")]
|
||||
pub enum ImapAuthConfigDef {
|
||||
#[serde(rename = "passwd", alias = "password", with = "ImapPasswdConfigDef")]
|
||||
Passwd(#[serde(default)] PasswdConfig),
|
||||
|
@ -227,6 +225,28 @@ pub enum ImapOAuth2ScopesDef {
|
|||
Scopes(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "Option<MaildirConfig>", from = "OptionMaildirConfig")]
|
||||
pub struct OptionMaildirConfigDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionMaildirConfig {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
Some(#[serde(with = "MaildirConfigDef")] MaildirConfig),
|
||||
}
|
||||
|
||||
impl From<OptionMaildirConfig> for Option<MaildirConfig> {
|
||||
fn from(config: OptionMaildirConfig) -> Option<MaildirConfig> {
|
||||
match config {
|
||||
OptionMaildirConfig::None => None,
|
||||
OptionMaildirConfig::Some(config) => Some(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "MaildirConfig", rename_all = "kebab-case")]
|
||||
pub struct MaildirConfigDef {
|
||||
|
@ -234,6 +254,31 @@ pub struct MaildirConfigDef {
|
|||
pub root_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "Option<NotmuchConfig>", from = "OptionNotmuchConfig")]
|
||||
pub struct OptionNotmuchConfigDef;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionNotmuchConfig {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
Some(#[serde(with = "NotmuchConfigDef")] NotmuchConfig),
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
impl From<OptionNotmuchConfig> for Option<NotmuchConfig> {
|
||||
fn from(config: OptionNotmuchConfig) -> Option<NotmuchConfig> {
|
||||
match config {
|
||||
OptionNotmuchConfig::None => None,
|
||||
OptionNotmuchConfig::Some(config) => Some(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "NotmuchConfig", rename_all = "kebab-case")]
|
||||
|
@ -242,6 +287,35 @@ pub struct NotmuchConfigDef {
|
|||
pub db_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(
|
||||
remote = "Option<EmailTextPlainFormat>",
|
||||
from = "OptionEmailTextPlainFormat"
|
||||
)]
|
||||
pub struct OptionEmailTextPlainFormatDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionEmailTextPlainFormat {
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
#[default]
|
||||
Auto,
|
||||
Flowed,
|
||||
Fixed(usize),
|
||||
}
|
||||
|
||||
impl From<OptionEmailTextPlainFormat> for Option<EmailTextPlainFormat> {
|
||||
fn from(fmt: OptionEmailTextPlainFormat) -> Option<EmailTextPlainFormat> {
|
||||
match fmt {
|
||||
OptionEmailTextPlainFormat::None => None,
|
||||
OptionEmailTextPlainFormat::Auto => Some(EmailTextPlainFormat::Auto),
|
||||
OptionEmailTextPlainFormat::Flowed => Some(EmailTextPlainFormat::Flowed),
|
||||
OptionEmailTextPlainFormat::Fixed(size) => Some(EmailTextPlainFormat::Fixed(size)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(
|
||||
remote = "EmailTextPlainFormat",
|
||||
|
@ -257,15 +331,25 @@ pub enum EmailTextPlainFormatDef {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "SenderConfig", tag = "sender", rename_all = "kebab-case")]
|
||||
pub enum SenderConfigDef {
|
||||
#[serde(remote = "Option<SmtpConfig>", from = "OptionSmtpConfig")]
|
||||
pub struct OptionSmtpConfigDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionSmtpConfig {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
#[serde(with = "SmtpConfigDef")]
|
||||
Smtp(SmtpConfig),
|
||||
#[serde(with = "SendmailConfigDef")]
|
||||
Sendmail(SendmailConfig),
|
||||
Some(#[serde(with = "SmtpConfigDef")] SmtpConfig),
|
||||
}
|
||||
|
||||
impl From<OptionSmtpConfig> for Option<SmtpConfig> {
|
||||
fn from(config: OptionSmtpConfig) -> Option<SmtpConfig> {
|
||||
match config {
|
||||
OptionSmtpConfig::None => None,
|
||||
OptionSmtpConfig::Some(config) => Some(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
|
@ -367,6 +451,28 @@ pub enum SmtpOAuth2ScopesDef {
|
|||
Scopes(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "Option<SendmailConfig>", from = "OptionSendmailConfig")]
|
||||
pub struct OptionSendmailConfigDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionSendmailConfig {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
Some(#[serde(with = "SendmailConfigDef")] SendmailConfig),
|
||||
}
|
||||
|
||||
impl From<OptionSendmailConfig> for Option<SendmailConfig> {
|
||||
fn from(config: OptionSendmailConfig) -> Option<SendmailConfig> {
|
||||
match config {
|
||||
OptionSendmailConfig::None => None,
|
||||
OptionSendmailConfig::Some(config) => Some(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
||||
pub struct SendmailConfigDef {
|
||||
|
@ -382,6 +488,28 @@ fn sendmail_default_cmd() -> Cmd {
|
|||
Cmd::from("/usr/sbin/sendmail")
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "Option<EmailHooks>", from = "OptionEmailHooks")]
|
||||
pub struct OptionEmailHooksDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionEmailHooks {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
Some(#[serde(with = "EmailHooksDef")] EmailHooks),
|
||||
}
|
||||
|
||||
impl From<OptionEmailHooks> for Option<EmailHooks> {
|
||||
fn from(fmt: OptionEmailHooks) -> Option<EmailHooks> {
|
||||
match fmt {
|
||||
OptionEmailHooks::None => None,
|
||||
OptionEmailHooks::Some(hooks) => Some(hooks),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the email hooks. Useful for doing extra email
|
||||
/// processing before or after sending it.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -392,6 +520,31 @@ pub struct EmailHooksDef {
|
|||
pub pre_send: Option<Cmd>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(
|
||||
remote = "Option<FolderSyncStrategy>",
|
||||
from = "OptionFolderSyncStrategy"
|
||||
)]
|
||||
pub struct OptionFolderSyncStrategyDef;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OptionFolderSyncStrategy {
|
||||
#[default]
|
||||
#[serde(skip_serializing)]
|
||||
None,
|
||||
Some(#[serde(with = "FolderSyncStrategyDef")] FolderSyncStrategy),
|
||||
}
|
||||
|
||||
impl From<OptionFolderSyncStrategy> for Option<FolderSyncStrategy> {
|
||||
fn from(config: OptionFolderSyncStrategy) -> Option<FolderSyncStrategy> {
|
||||
match config {
|
||||
OptionFolderSyncStrategy::None => None,
|
||||
OptionFolderSyncStrategy::Some(config) => Some(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(remote = "FolderSyncStrategy", rename_all = "kebab-case")]
|
||||
pub enum FolderSyncStrategyDef {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//! accounts from the config file.
|
||||
|
||||
use anyhow::Result;
|
||||
use email::backend::BackendConfig;
|
||||
use serde::Serialize;
|
||||
use std::{collections::hash_map::Iter, ops::Deref};
|
||||
|
||||
|
@ -40,19 +39,45 @@ impl PrintTable for Accounts {
|
|||
impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
|
||||
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
|
||||
let mut accounts: Vec<_> = map
|
||||
.map(|(name, account)| match &account.backend {
|
||||
BackendConfig::None => Account::new(name, "none", false),
|
||||
BackendConfig::Maildir(_) => {
|
||||
Account::new(name, "maildir", account.default.unwrap_or_default())
|
||||
}
|
||||
.map(|(name, account)| {
|
||||
let mut backends = String::new();
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
BackendConfig::Imap(_) => {
|
||||
Account::new(name, "imap", account.default.unwrap_or_default())
|
||||
if account.imap.is_some() {
|
||||
backends.push_str("imap");
|
||||
}
|
||||
|
||||
if account.maildir.is_some() {
|
||||
if !backends.is_empty() {
|
||||
backends.push_str(", ")
|
||||
}
|
||||
backends.push_str("maildir");
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
BackendConfig::Notmuch(_) => {
|
||||
Account::new(name, "notmuch", account.default.unwrap_or_default())
|
||||
if account.imap.is_some() {
|
||||
if !backends.is_empty() {
|
||||
backends.push_str(", ")
|
||||
}
|
||||
backends.push_str("notmuch");
|
||||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if account.smtp.is_some() {
|
||||
if !backends.is_empty() {
|
||||
backends.push_str(", ")
|
||||
}
|
||||
backends.push_str("smtp");
|
||||
}
|
||||
|
||||
if account.sendmail.is_some() {
|
||||
if !backends.is_empty() {
|
||||
backends.push_str(", ")
|
||||
}
|
||||
backends.push_str("sendmail");
|
||||
}
|
||||
|
||||
Account::new(name, &backends, account.default.unwrap_or_default())
|
||||
})
|
||||
.collect();
|
||||
accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());
|
||||
|
|
|
@ -6,29 +6,27 @@
|
|||
#[cfg(feature = "pgp")]
|
||||
use email::account::PgpConfig;
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::backend::ImapAuthConfig;
|
||||
use email::imap::ImapConfig;
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
use email::sender::SmtpAuthConfig;
|
||||
use email::smtp::SmtpConfig;
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
backend::BackendConfig,
|
||||
email::{EmailHooks, EmailTextPlainFormat},
|
||||
folder::sync::FolderSyncStrategy,
|
||||
sender::SenderConfig,
|
||||
maildir::MaildirConfig,
|
||||
sendmail::SendmailConfig,
|
||||
};
|
||||
|
||||
use process::Cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use crate::config::{prelude::*, DeserializedConfig};
|
||||
use crate::{backend::BackendKind, config::prelude::*};
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(tag = "backend", rename_all = "kebab-case")]
|
||||
pub struct DeserializedAccountConfig {
|
||||
pub email: String,
|
||||
pub default: Option<bool>,
|
||||
|
||||
pub email: String,
|
||||
pub display_name: Option<String>,
|
||||
pub signature_delim: Option<String>,
|
||||
pub signature: Option<String>,
|
||||
|
@ -41,192 +39,39 @@ pub struct DeserializedAccountConfig {
|
|||
pub email_listing_datetime_fmt: Option<String>,
|
||||
pub email_listing_datetime_local_tz: Option<bool>,
|
||||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailTextPlainFormatDef",
|
||||
skip_serializing_if = "EmailTextPlainFormat::is_default"
|
||||
)]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_verify_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||
#[serde(default, with = "OptionEmailTextPlainFormatDef")]
|
||||
pub email_reading_format: Option<EmailTextPlainFormat>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_sign_cmd: Option<Cmd>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "OptionCmdDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||
pub email_sending_save_copy: Option<bool>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksDef",
|
||||
skip_serializing_if = "EmailHooks::is_empty"
|
||||
)]
|
||||
pub email_hooks: EmailHooks,
|
||||
#[serde(default, with = "OptionEmailHooksDef")]
|
||||
pub email_hooks: Option<EmailHooks>,
|
||||
|
||||
pub sync: Option<bool>,
|
||||
pub sync_dir: Option<PathBuf>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "FolderSyncStrategyDef",
|
||||
skip_serializing_if = "FolderSyncStrategy::is_default"
|
||||
)]
|
||||
pub sync_folders_strategy: FolderSyncStrategy,
|
||||
#[serde(default, with = "OptionFolderSyncStrategyDef")]
|
||||
pub sync_folders_strategy: Option<FolderSyncStrategy>,
|
||||
|
||||
#[serde(flatten, with = "BackendConfigDef")]
|
||||
pub backend: BackendConfig,
|
||||
#[serde(flatten, with = "SenderConfigDef")]
|
||||
pub sender: SenderConfig,
|
||||
|
||||
#[cfg(feature = "pgp")]
|
||||
#[serde(default, with = "PgpConfigDef")]
|
||||
pub pgp: PgpConfig,
|
||||
}
|
||||
|
||||
impl DeserializedAccountConfig {
|
||||
pub fn to_account_config(&self, name: String, config: &DeserializedConfig) -> AccountConfig {
|
||||
let mut folder_aliases = config
|
||||
.folder_aliases
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_default();
|
||||
folder_aliases.extend(
|
||||
self.folder_aliases
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
AccountConfig {
|
||||
name: name.clone(),
|
||||
email: self.email.to_owned(),
|
||||
display_name: self
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.display_name.as_ref().map(ToOwned::to_owned)),
|
||||
signature_delim: self
|
||||
.signature_delim
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.signature_delim.as_ref().map(ToOwned::to_owned)),
|
||||
signature: self
|
||||
.signature
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.signature.as_ref().map(ToOwned::to_owned)),
|
||||
downloads_dir: self
|
||||
.downloads_dir
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.downloads_dir.as_ref().map(ToOwned::to_owned)),
|
||||
folder_listing_page_size: self
|
||||
.folder_listing_page_size
|
||||
.or_else(|| config.folder_listing_page_size),
|
||||
folder_aliases,
|
||||
email_listing_page_size: self
|
||||
.email_listing_page_size
|
||||
.or_else(|| config.email_listing_page_size),
|
||||
email_listing_datetime_fmt: self
|
||||
.email_listing_datetime_fmt
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| {
|
||||
config
|
||||
.email_listing_datetime_fmt
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
}),
|
||||
email_listing_datetime_local_tz: self
|
||||
.email_listing_datetime_local_tz
|
||||
.or_else(|| config.email_listing_datetime_local_tz),
|
||||
email_reading_headers: self
|
||||
.email_reading_headers
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.email_reading_headers.as_ref().map(ToOwned::to_owned)),
|
||||
email_reading_format: self.email_reading_format.clone(),
|
||||
email_writing_headers: self
|
||||
.email_writing_headers
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.email_writing_headers.as_ref().map(ToOwned::to_owned)),
|
||||
email_sending_save_copy: self.email_sending_save_copy.unwrap_or(true),
|
||||
email_hooks: EmailHooks {
|
||||
pre_send: self.email_hooks.pre_send.clone(),
|
||||
},
|
||||
sync: self.sync.unwrap_or_default(),
|
||||
sync_dir: self.sync_dir.clone(),
|
||||
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
||||
|
||||
backend: {
|
||||
let mut backend = self.backend.clone();
|
||||
pub backend: BackendKind,
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(config) = &mut backend {
|
||||
match &mut config.auth {
|
||||
ImapAuthConfig::Passwd(secret) => {
|
||||
secret.set_keyring_entry_if_undefined(format!("{name}-imap-passwd"));
|
||||
}
|
||||
ImapAuthConfig::OAuth2(config) => {
|
||||
config.client_secret.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-imap-oauth2-client-secret"
|
||||
));
|
||||
config.access_token.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-imap-oauth2-access-token"
|
||||
));
|
||||
config.refresh_token.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-imap-oauth2-refresh-token"
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
#[serde(default, with = "OptionImapConfigDef")]
|
||||
pub imap: Option<ImapConfig>,
|
||||
|
||||
backend
|
||||
},
|
||||
sender: {
|
||||
let mut sender = self.sender.clone();
|
||||
#[serde(default, with = "OptionMaildirConfigDef")]
|
||||
pub maildir: Option<MaildirConfig>,
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[serde(default, with = "OptionNotmuchConfigDef")]
|
||||
pub notmuch: Option<NotmuchConfig>,
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let SenderConfig::Smtp(config) = &mut sender {
|
||||
match &mut config.auth {
|
||||
SmtpAuthConfig::Passwd(secret) => {
|
||||
secret.set_keyring_entry_if_undefined(format!("{name}-smtp-passwd"));
|
||||
}
|
||||
SmtpAuthConfig::OAuth2(config) => {
|
||||
config.client_secret.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-smtp-oauth2-client-secret"
|
||||
));
|
||||
config.access_token.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-smtp-oauth2-access-token"
|
||||
));
|
||||
config.refresh_token.set_keyring_entry_if_undefined(format!(
|
||||
"{name}-smtp-oauth2-refresh-token"
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
#[serde(default, with = "OptionSmtpConfigDef")]
|
||||
pub smtp: Option<SmtpConfig>,
|
||||
|
||||
#[serde(default, with = "OptionSendmailConfigDef")]
|
||||
pub sendmail: Option<SendmailConfig>,
|
||||
|
||||
sender
|
||||
},
|
||||
#[cfg(feature = "pgp")]
|
||||
pgp: self.pgp.clone(),
|
||||
}
|
||||
}
|
||||
#[serde(default, with = "OptionPgpConfigDef")]
|
||||
pub pgp: Option<PgpConfig>,
|
||||
}
|
||||
|
|
|
@ -2,25 +2,22 @@
|
|||
//!
|
||||
//! This module gathers all account actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::backend::ImapAuthConfig;
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
use email::sender::SmtpAuthConfig;
|
||||
use email::{
|
||||
account::{
|
||||
use anyhow::{Context, Result};
|
||||
use email::account::{
|
||||
sync::{AccountSyncBuilder, AccountSyncProgressEvent},
|
||||
AccountConfig,
|
||||
},
|
||||
backend::BackendConfig,
|
||||
sender::SenderConfig,
|
||||
};
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use email::imap::ImapAuthConfig;
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
use email::smtp::SmtpAuthConfig;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
|
||||
use log::{info, trace, warn};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use crate::{
|
||||
backend::BackendContextBuilder,
|
||||
config::{
|
||||
wizard::{prompt_passwd, prompt_secret},
|
||||
DeserializedConfig,
|
||||
|
@ -48,68 +45,68 @@ const SUB_PROGRESS_DONE_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
|||
pub async fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
|
||||
info!("entering the configure account handler");
|
||||
|
||||
if reset {
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||
let reset = match &imap_config.auth {
|
||||
ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
warn!("error while resetting imap secrets, skipping it");
|
||||
warn!("{err}");
|
||||
}
|
||||
}
|
||||
// if reset {
|
||||
// #[cfg(feature = "imap-backend")]
|
||||
// if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||
// let reset = match &imap_config.auth {
|
||||
// ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
// ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
// };
|
||||
// if let Err(err) = reset {
|
||||
// warn!("error while resetting imap secrets, skipping it");
|
||||
// warn!("{err}");
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||
let reset = match &smtp_config.auth {
|
||||
SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
warn!("error while resetting smtp secrets, skipping it");
|
||||
warn!("{err}");
|
||||
}
|
||||
}
|
||||
// #[cfg(feature = "smtp-sender")]
|
||||
// if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||
// let reset = match &smtp_config.auth {
|
||||
// SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
// SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
// };
|
||||
// if let Err(err) = reset {
|
||||
// warn!("error while resetting smtp secrets, skipping it");
|
||||
// warn!("{err}");
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(feature = "pgp")]
|
||||
config.pgp.reset().await?;
|
||||
}
|
||||
// #[cfg(feature = "pgp")]
|
||||
// config.pgp.reset().await?;
|
||||
// }
|
||||
|
||||
#[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")).await
|
||||
}
|
||||
ImapAuthConfig::OAuth2(oauth2) => {
|
||||
oauth2
|
||||
.configure(|| prompt_secret("IMAP OAuth 2.0 client secret"))
|
||||
.await
|
||||
}
|
||||
}?;
|
||||
}
|
||||
// #[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")).await
|
||||
// }
|
||||
// ImapAuthConfig::OAuth2(oauth2) => {
|
||||
// oauth2
|
||||
// .configure(|| prompt_secret("IMAP OAuth 2.0 client secret"))
|
||||
// .await
|
||||
// }
|
||||
// }?;
|
||||
// }
|
||||
|
||||
#[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")).await
|
||||
}
|
||||
SmtpAuthConfig::OAuth2(oauth2) => {
|
||||
oauth2
|
||||
.configure(|| prompt_secret("SMTP OAuth 2.0 client secret"))
|
||||
.await
|
||||
}
|
||||
}?;
|
||||
}
|
||||
// #[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")).await
|
||||
// }
|
||||
// SmtpAuthConfig::OAuth2(oauth2) => {
|
||||
// oauth2
|
||||
// .configure(|| prompt_secret("SMTP OAuth 2.0 client secret"))
|
||||
// .await
|
||||
// }
|
||||
// }?;
|
||||
// }
|
||||
|
||||
#[cfg(feature = "pgp")]
|
||||
config
|
||||
.pgp
|
||||
.configure(&config.email, || prompt_passwd("PGP secret key password"))
|
||||
.await?;
|
||||
// #[cfg(feature = "pgp")]
|
||||
// config
|
||||
// .pgp
|
||||
// .configure(&config.email, || prompt_passwd("PGP secret key password"))
|
||||
// .await?;
|
||||
|
||||
println!(
|
||||
"Account successfully {}configured!",
|
||||
|
@ -147,7 +144,7 @@ pub fn list<'a, P: Printer>(
|
|||
/// no account given, synchronizes the default one.
|
||||
pub async fn sync<P: Printer>(
|
||||
printer: &mut P,
|
||||
sync_builder: AccountSyncBuilder,
|
||||
sync_builder: AccountSyncBuilder<BackendContextBuilder>,
|
||||
dry_run: bool,
|
||||
) -> Result<()> {
|
||||
info!("entering the sync accounts handler");
|
||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
|
|||
use dialoguer::Input;
|
||||
use email_address::EmailAddress;
|
||||
|
||||
use crate::{backend, config::wizard::THEME, sender};
|
||||
use crate::config::wizard::THEME;
|
||||
|
||||
use super::DeserializedAccountConfig;
|
||||
|
||||
|
@ -31,9 +31,9 @@ pub(crate) async fn configure() -> Result<Option<(String, DeserializedAccountCon
|
|||
.interact()?,
|
||||
);
|
||||
|
||||
config.backend = backend::wizard::configure(&account_name, &config.email).await?;
|
||||
// config.backend = backend::wizard::configure(&account_name, &config.email).await?;
|
||||
|
||||
config.sender = sender::wizard::configure(&account_name, &config.email).await?;
|
||||
// config.sender = sender::wizard::configure(&account_name, &config.email).await?;
|
||||
|
||||
Ok(Some((account_name, config)))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
//! This module gathers all IMAP handlers triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use email::backend::ImapBackend;
|
||||
|
||||
pub async fn notify(imap: &mut ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
|
||||
imap.notify(keepalive, folder).await?;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
pub mod args;
|
||||
pub mod handlers;
|
||||
pub(crate) mod wizard;
|
||||
// pub mod handlers;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub(crate) mod wizard;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -3,4 +3,4 @@ pub mod imap;
|
|||
pub mod maildir;
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
pub mod notmuch;
|
||||
pub(crate) mod wizard;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -2,9 +2,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use atty::Stream;
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
backend::Backend,
|
||||
email::{template::FilterParts, Flag, Flags, Message, MessageBuilder},
|
||||
sender::Sender,
|
||||
email::{envelope::Id, template::FilterParts, Flag, Message, MessageBuilder},
|
||||
};
|
||||
use log::{debug, trace};
|
||||
use std::{
|
||||
|
@ -15,6 +13,7 @@ use url::Url;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
printer::{PrintTableOpts, Printer},
|
||||
ui::editor,
|
||||
Envelopes, IdMapper,
|
||||
|
@ -24,20 +23,20 @@ pub async fn attachments<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
) -> Result<()> {
|
||||
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()).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
let emails = backend.get_messages(&folder, &ids).await?;
|
||||
let mut index = 0;
|
||||
|
||||
let mut emails_count = 0;
|
||||
let mut attachments_count = 0;
|
||||
|
||||
let mut ids = ids.iter();
|
||||
for email in emails.to_vec() {
|
||||
let id = ids.get(index).unwrap();
|
||||
let id = ids.next().unwrap();
|
||||
let attachments = email.attachments()?;
|
||||
|
||||
index = index + 1;
|
||||
|
@ -79,27 +78,27 @@ pub async fn attachments<P: Printer>(
|
|||
pub async fn copy<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
from_folder: &str,
|
||||
to_folder: &str,
|
||||
ids: Vec<&str>,
|
||||
) -> Result<()> {
|
||||
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).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend
|
||||
.copy_messages(&from_folder, &to_folder, &ids)
|
||||
.await?;
|
||||
printer.print("Email(s) successfully copied!")
|
||||
}
|
||||
|
||||
pub async fn delete<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids(ids)?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
backend.delete_emails(&folder, ids).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend.delete_messages(&folder, &ids).await?;
|
||||
printer.print("Email(s) successfully deleted!")
|
||||
}
|
||||
|
||||
|
@ -107,18 +106,15 @@ pub async fn forward<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
id: &str,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
body: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids([id])?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
|
||||
let id = Id::single(id_mapper.get_id(id)?);
|
||||
let tpl = backend
|
||||
.get_emails(&folder, ids)
|
||||
.get_messages(&folder, &id)
|
||||
.await?
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
||||
|
@ -128,7 +124,7 @@ pub async fn forward<P: Printer>(
|
|||
.build()
|
||||
.await?;
|
||||
trace!("initial template: {tpl}");
|
||||
editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?;
|
||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -136,7 +132,7 @@ pub async fn list<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
max_width: Option<usize>,
|
||||
page_size: Option<usize>,
|
||||
|
@ -166,8 +162,7 @@ pub async fn list<P: Printer>(
|
|||
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
||||
pub async fn mailto<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
printer: &mut P,
|
||||
url: &Url,
|
||||
) -> Result<()> {
|
||||
|
@ -190,20 +185,21 @@ pub async fn mailto<P: Printer>(
|
|||
.from_msg_builder(builder)
|
||||
.await?;
|
||||
|
||||
editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await
|
||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await
|
||||
}
|
||||
|
||||
pub async fn move_<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
from_folder: &str,
|
||||
to_folder: &str,
|
||||
ids: Vec<&str>,
|
||||
) -> Result<()> {
|
||||
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).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend
|
||||
.move_messages(&from_folder, &to_folder, &ids)
|
||||
.await?;
|
||||
printer.print("Email(s) successfully moved!")
|
||||
}
|
||||
|
||||
|
@ -211,16 +207,15 @@ pub async fn read<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
text_mime: &str,
|
||||
raw: bool,
|
||||
headers: Vec<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids(ids)?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
let emails = backend.get_emails(&folder, ids).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
let emails = backend.get_messages(&folder, &ids).await?;
|
||||
|
||||
let mut glue = "";
|
||||
let mut bodies = String::default();
|
||||
|
@ -255,19 +250,16 @@ pub async fn reply<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
id: &str,
|
||||
all: bool,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
body: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids([id])?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
|
||||
let id = Id::single(id_mapper.get_id(id)?);
|
||||
let tpl = backend
|
||||
.get_emails(&folder, ids)
|
||||
.get_messages(folder, &id)
|
||||
.await?
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
||||
|
@ -278,17 +270,15 @@ pub async fn reply<P: Printer>(
|
|||
.build()
|
||||
.await?;
|
||||
trace!("initial template: {tpl}");
|
||||
editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?;
|
||||
backend
|
||||
.add_flags(&folder, vec![id], &Flags::from_iter([Flag::Answered]))
|
||||
.await?;
|
||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
||||
backend.add_flag(&folder, &id, Flag::Answered).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn save<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
raw_email: String,
|
||||
) -> Result<()> {
|
||||
|
@ -306,73 +296,74 @@ pub async fn save<P: Printer>(
|
|||
};
|
||||
|
||||
let id = backend
|
||||
.add_email(&folder, raw_email.as_bytes(), &Flags::default())
|
||||
.add_raw_message(&folder, raw_email.as_bytes())
|
||||
.await?;
|
||||
id_mapper.create_alias(id)?;
|
||||
id_mapper.create_alias(&*id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn search<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
folder: &str,
|
||||
query: String,
|
||||
max_width: Option<usize>,
|
||||
page_size: Option<usize>,
|
||||
page: usize,
|
||||
_config: &AccountConfig,
|
||||
_printer: &mut P,
|
||||
_id_mapper: &IdMapper,
|
||||
_backend: &Backend,
|
||||
_folder: &str,
|
||||
_query: String,
|
||||
_max_width: Option<usize>,
|
||||
_page_size: Option<usize>,
|
||||
_page: usize,
|
||||
) -> Result<()> {
|
||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
let envelopes = Envelopes::from_backend(
|
||||
config,
|
||||
id_mapper,
|
||||
backend
|
||||
.search_envelopes(&folder, &query, "", page_size, page)
|
||||
.await?,
|
||||
)?;
|
||||
let opts = PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
};
|
||||
todo!()
|
||||
// let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
// let envelopes = Envelopes::from_backend(
|
||||
// config,
|
||||
// id_mapper,
|
||||
// backend
|
||||
// .search_envelopes(&folder, &query, "", page_size, page)
|
||||
// .await?,
|
||||
// )?;
|
||||
// let opts = PrintTableOpts {
|
||||
// format: &config.email_reading_format,
|
||||
// max_width,
|
||||
// };
|
||||
|
||||
printer.print_table(Box::new(envelopes), opts)
|
||||
// printer.print_table(Box::new(envelopes), opts)
|
||||
}
|
||||
|
||||
pub async fn sort<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
folder: &str,
|
||||
sort: String,
|
||||
query: String,
|
||||
max_width: Option<usize>,
|
||||
page_size: Option<usize>,
|
||||
page: usize,
|
||||
_config: &AccountConfig,
|
||||
_printer: &mut P,
|
||||
_id_mapper: &IdMapper,
|
||||
_backend: &Backend,
|
||||
_folder: &str,
|
||||
_sort: String,
|
||||
_query: String,
|
||||
_max_width: Option<usize>,
|
||||
_page_size: Option<usize>,
|
||||
_page: usize,
|
||||
) -> Result<()> {
|
||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
let envelopes = Envelopes::from_backend(
|
||||
config,
|
||||
id_mapper,
|
||||
backend
|
||||
.search_envelopes(&folder, &query, &sort, page_size, page)
|
||||
.await?,
|
||||
)?;
|
||||
let opts = PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
};
|
||||
todo!()
|
||||
// let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
// let envelopes = Envelopes::from_backend(
|
||||
// config,
|
||||
// id_mapper,
|
||||
// backend
|
||||
// .search_envelopes(&folder, &query, &sort, page_size, page)
|
||||
// .await?,
|
||||
// )?;
|
||||
// let opts = PrintTableOpts {
|
||||
// format: &config.email_reading_format,
|
||||
// max_width,
|
||||
// };
|
||||
|
||||
printer.print_table(Box::new(envelopes), opts)
|
||||
// printer.print_table(Box::new(envelopes), opts)
|
||||
}
|
||||
|
||||
pub async fn send<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
raw_email: String,
|
||||
) -> Result<()> {
|
||||
let folder = config.sent_folder_alias()?;
|
||||
|
@ -389,14 +380,10 @@ pub async fn send<P: Printer>(
|
|||
.join("\r\n")
|
||||
};
|
||||
trace!("raw email: {:?}", raw_email);
|
||||
sender.send(raw_email.as_bytes()).await?;
|
||||
if config.email_sending_save_copy {
|
||||
backend.send_raw_message(raw_email.as_bytes()).await?;
|
||||
if config.email_sending_save_copy.unwrap_or_default() {
|
||||
backend
|
||||
.add_email(
|
||||
&folder,
|
||||
raw_email.as_bytes(),
|
||||
&Flags::from_iter([Flag::Seen]),
|
||||
)
|
||||
.add_raw_message_with_flag(&folder, raw_email.as_bytes(), Flag::Seen)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -405,8 +392,7 @@ pub async fn send<P: Printer>(
|
|||
pub async fn write<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
body: Option<&str>,
|
||||
) -> Result<()> {
|
||||
|
@ -416,6 +402,6 @@ pub async fn write<P: Printer>(
|
|||
.build()
|
||||
.await?;
|
||||
trace!("initial template: {tpl}");
|
||||
editor::edit_tpl_with_editor(config, printer, backend, sender, tpl).await?;
|
||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,46 +1,43 @@
|
|||
use anyhow::Result;
|
||||
use email::{backend::Backend, email::Flags};
|
||||
use email::email::{envelope::Id, Flags};
|
||||
|
||||
use crate::{printer::Printer, IdMapper};
|
||||
use crate::{backend::Backend, printer::Printer, IdMapper};
|
||||
|
||||
pub async fn add<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
flags: &Flags,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids(ids)?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
backend.add_flags(folder, ids, flags).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend.add_flags(folder, &ids, flags).await?;
|
||||
printer.print("Flag(s) successfully added!")
|
||||
}
|
||||
|
||||
pub async fn set<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
flags: &Flags,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids(ids)?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
backend.set_flags(folder, ids, flags).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend.set_flags(folder, &ids, flags).await?;
|
||||
printer.print("Flag(s) successfully set!")
|
||||
}
|
||||
|
||||
pub async fn remove<P: Printer>(
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
ids: Vec<&str>,
|
||||
flags: &Flags,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids(ids)?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
backend.remove_flags(folder, ids, flags).await?;
|
||||
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||
backend.remove_flags(folder, &ids, flags).await?;
|
||||
printer.print("Flag(s) successfully removed!")
|
||||
}
|
||||
|
|
|
@ -4,19 +4,16 @@
|
|||
|
||||
use anyhow::Result;
|
||||
use dialoguer::Confirm;
|
||||
use email::{account::AccountConfig, backend::Backend};
|
||||
use email::account::AccountConfig;
|
||||
use std::process;
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
printer::{PrintTableOpts, Printer},
|
||||
Folders,
|
||||
};
|
||||
|
||||
pub async fn expunge<P: Printer>(
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
pub async fn expunge<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
backend.expunge_folder(folder).await?;
|
||||
printer.print(format!("Folder {folder} successfully expunged!"))
|
||||
}
|
||||
|
@ -24,7 +21,7 @@ pub async fn expunge<P: Printer>(
|
|||
pub async fn list<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
max_width: Option<usize>,
|
||||
) -> Result<()> {
|
||||
let folders: Folders = backend.list_folders().await?.into();
|
||||
|
@ -38,20 +35,12 @@ pub async fn list<P: Printer>(
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn create<P: Printer>(
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
pub async fn create<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
backend.add_folder(folder).await?;
|
||||
printer.print("Folder successfully created!")
|
||||
}
|
||||
|
||||
pub async fn delete<P: Printer>(
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
pub async fn delete<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
if let Some(false) | None = Confirm::new()
|
||||
.with_prompt(format!("Confirm deletion of folder {folder}?"))
|
||||
.default(false)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod sendmail;
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
pub mod smtp;
|
||||
pub(crate) mod wizard;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub(crate) mod wizard;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub(crate) mod wizard;
|
||||
// pub(crate) mod wizard;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use dialoguer::Select;
|
||||
use email::sender::SenderConfig;
|
||||
|
||||
use crate::config::wizard::THEME;
|
||||
|
||||
|
|
|
@ -2,30 +2,27 @@ use anyhow::{anyhow, Result};
|
|||
use atty::Stream;
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
backend::Backend,
|
||||
email::{Flag, Flags, Message},
|
||||
sender::Sender,
|
||||
email::{envelope::Id, Flag, Message},
|
||||
};
|
||||
use mml::MmlCompilerBuilder;
|
||||
use std::io::{stdin, BufRead};
|
||||
|
||||
use crate::{printer::Printer, IdMapper};
|
||||
use crate::{backend::Backend, printer::Printer, IdMapper};
|
||||
|
||||
pub async fn forward<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
id: &str,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
body: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids([id])?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
let ids = Id::multiple(id_mapper.get_ids([id])?);
|
||||
|
||||
let tpl: String = backend
|
||||
.get_emails(folder, ids)
|
||||
.get_messages(folder, &ids)
|
||||
.await?
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
||||
|
@ -43,18 +40,17 @@ pub async fn reply<P: Printer>(
|
|||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
id: &str,
|
||||
all: bool,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
body: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let ids = id_mapper.get_ids([id])?;
|
||||
let ids = ids.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
let ids = Id::multiple(id_mapper.get_ids([id])?);
|
||||
|
||||
let tpl: String = backend
|
||||
.get_emails(folder, ids)
|
||||
.get_messages(folder, &ids)
|
||||
.await?
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
||||
|
@ -73,7 +69,7 @@ pub async fn save<P: Printer>(
|
|||
#[allow(unused_variables)] config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
id_mapper: &IdMapper,
|
||||
backend: &mut dyn Backend,
|
||||
backend: &Backend,
|
||||
folder: &str,
|
||||
tpl: String,
|
||||
) -> Result<()> {
|
||||
|
@ -95,8 +91,8 @@ pub async fn save<P: Printer>(
|
|||
|
||||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||
|
||||
let id = backend.add_email(folder, &email, &Flags::default()).await?;
|
||||
id_mapper.create_alias(id)?;
|
||||
let id = backend.add_raw_message(folder, &email).await?;
|
||||
id_mapper.create_alias(&*id)?;
|
||||
|
||||
printer.print("Template successfully saved!")
|
||||
}
|
||||
|
@ -104,8 +100,7 @@ pub async fn save<P: Printer>(
|
|||
pub async fn send<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
tpl: String,
|
||||
) -> Result<()> {
|
||||
let folder = config.sent_folder_alias()?;
|
||||
|
@ -128,11 +123,11 @@ pub async fn send<P: Printer>(
|
|||
|
||||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||
|
||||
sender.send(&email).await?;
|
||||
backend.send_raw_message(&email).await?;
|
||||
|
||||
if config.email_sending_save_copy {
|
||||
if config.email_sending_save_copy.unwrap_or_default() {
|
||||
backend
|
||||
.add_email(&folder, &email, &Flags::from_iter([Flag::Seen]))
|
||||
.add_raw_message_with_flag(&folder, &email, Flag::Seen)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod backend;
|
||||
pub mod cache;
|
||||
pub mod compl;
|
||||
pub mod config;
|
||||
|
|
291
src/main.rs
291
src/main.rs
|
@ -1,10 +1,4 @@
|
|||
#[cfg(feature = "imap-backend")]
|
||||
use ::email::backend::ImapBackend;
|
||||
use ::email::{
|
||||
account::{sync::AccountSyncBuilder, DEFAULT_INBOX_FOLDER},
|
||||
backend::{BackendBuilder, BackendConfig},
|
||||
sender::SenderBuilder,
|
||||
};
|
||||
use ::email::account::{sync::AccountSyncBuilder, DEFAULT_INBOX_FOLDER};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use clap::Command;
|
||||
use log::{debug, warn};
|
||||
|
@ -14,7 +8,9 @@ use url::Url;
|
|||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya::imap;
|
||||
use himalaya::{
|
||||
account, cache, compl,
|
||||
account,
|
||||
backend::BackendBuilder,
|
||||
cache, compl,
|
||||
config::{self, DeserializedConfig},
|
||||
email, flag, folder, man, output,
|
||||
printer::StdoutPrinter,
|
||||
|
@ -60,21 +56,19 @@ async fn main() -> Result<()> {
|
|||
// checks mailto command before app initialization
|
||||
let raw_args: Vec<String> = env::args().collect();
|
||||
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||
let url = Url::parse(&raw_args[1])?;
|
||||
let config = DeserializedConfig::from_opt_path(None).await?;
|
||||
let account_config = config.to_account_config(None)?;
|
||||
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();
|
||||
// let url = Url::parse(&raw_args[1])?;
|
||||
// let config = DeserializedConfig::from_opt_path(None).await?;
|
||||
// let account_config = config.to_account_config(None)?;
|
||||
// let backend = BackendBuilder::new(account_config.clone()).build().await?;
|
||||
// let mut printer = StdoutPrinter::default();
|
||||
|
||||
email::handlers::mailto(
|
||||
&account_config,
|
||||
backend.as_mut(),
|
||||
sender.as_mut(),
|
||||
&mut printer,
|
||||
&url,
|
||||
)
|
||||
.await?;
|
||||
// email::handlers::mailto(
|
||||
// &account_config,
|
||||
// &backend,
|
||||
// &mut printer,
|
||||
// &url,
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -100,37 +94,33 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m)).await?;
|
||||
let account_config = config.to_account_config(account::args::parse_arg(&m))?;
|
||||
let maybe_account_name = account::args::parse_arg(&m);
|
||||
let folder = folder::args::parse_source_arg(&m);
|
||||
let disable_cache = cache::args::parse_disable_cache_flag(&m);
|
||||
|
||||
// FIXME: find why account config cannot be borrowed
|
||||
// let backend_builder =
|
||||
// BackendBuilder::new(Cow::Borrowed(&account_config)).with_cache_disabled(disable_cache);
|
||||
let backend_builder =
|
||||
BackendBuilder::new(account_config.clone()).with_cache_disabled(disable_cache);
|
||||
let sender_builder = SenderBuilder::new(account_config.clone());
|
||||
let backend_builder = BackendBuilder::new(config.clone(), maybe_account_name).await?;
|
||||
let account_config = &backend_builder.account_config;
|
||||
let mut printer = StdoutPrinter::try_from(&m)?;
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = &account_config.backend {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
match imap::args::matches(&m)? {
|
||||
Some(imap::args::Cmd::Notify(keepalive)) => {
|
||||
let mut backend =
|
||||
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).await?;
|
||||
imap::handlers::watch(&mut backend, &folder, keepalive).await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// #[cfg(feature = "imap-backend")]
|
||||
// if let BackendConfig::Imap(imap_config) = &account_config.backend {
|
||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
// match imap::args::matches(&m)? {
|
||||
// Some(imap::args::Cmd::Notify(keepalive)) => {
|
||||
// let backend =
|
||||
// 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 backend =
|
||||
// ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?;
|
||||
// imap::handlers::watch(&mut backend, &folder, keepalive).await?;
|
||||
// return Ok(());
|
||||
// }
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
|
||||
match account::args::matches(&m)? {
|
||||
Some(account::args::Cmd::List(max_width)) => {
|
||||
|
@ -138,7 +128,7 @@ async fn main() -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
Some(account::args::Cmd::Sync(strategy, dry_run)) => {
|
||||
let sync_builder = AccountSyncBuilder::new(account_config, backend_builder)
|
||||
let sync_builder = AccountSyncBuilder::new(backend_builder.0)
|
||||
.await?
|
||||
.with_some_folders_strategy(strategy)
|
||||
.with_dry_run(dry_run);
|
||||
|
@ -158,26 +148,25 @@ async fn main() -> Result<()> {
|
|||
let folder = folder
|
||||
.ok_or_else(|| anyhow!("the folder argument is missing"))
|
||||
.context("cannot create folder")?;
|
||||
let mut backend = backend_builder.build().await?;
|
||||
folder::handlers::create(&mut printer, backend.as_mut(), &folder).await?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
folder::handlers::create(&mut printer, &backend, &folder).await?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(folder::args::Cmd::List(max_width)) => {
|
||||
let mut backend = backend_builder.build().await?;
|
||||
folder::handlers::list(&account_config, &mut printer, backend.as_mut(), max_width)
|
||||
.await?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
folder::handlers::list(&account_config, &mut printer, &backend, max_width).await?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(folder::args::Cmd::Expunge) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.build().await?;
|
||||
folder::handlers::expunge(&mut printer, backend.as_mut(), &folder).await?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
folder::handlers::expunge(&mut printer, &backend, &folder).await?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(folder::args::Cmd::Delete) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.build().await?;
|
||||
folder::handlers::delete(&mut printer, backend.as_mut(), &folder).await?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
folder::handlers::delete(&mut printer, &backend, &folder).await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
|
@ -187,13 +176,13 @@ async fn main() -> Result<()> {
|
|||
match email::args::matches(&m)? {
|
||||
Some(email::args::Cmd::Attachments(ids)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
email::handlers::attachments(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
ids,
|
||||
)
|
||||
|
@ -202,43 +191,33 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::Copy(ids, to_folder)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::copy(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
to_folder,
|
||||
ids,
|
||||
)
|
||||
email::handlers::copy(&mut printer, &id_mapper, &backend, &folder, to_folder, ids)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Delete(ids)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::delete(&mut printer, &id_mapper, backend.as_mut(), &folder, ids)
|
||||
.await?;
|
||||
email::handlers::delete(&mut printer, &id_mapper, &backend, &folder, ids).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Forward(id, headers, body)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
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_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::forward(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
sender.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
id,
|
||||
headers,
|
||||
|
@ -250,14 +229,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::List(max_width, page_size, page)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::list(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
max_width,
|
||||
page_size,
|
||||
|
@ -269,31 +248,24 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::Move(ids, to_folder)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::move_(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
to_folder,
|
||||
ids,
|
||||
)
|
||||
email::handlers::move_(&mut printer, &id_mapper, &backend, &folder, to_folder, ids)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Read(ids, text_mime, raw, headers)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::read(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
ids,
|
||||
text_mime,
|
||||
|
@ -306,16 +278,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::Reply(id, all, headers, body)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
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_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::reply(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
sender.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
id,
|
||||
all,
|
||||
|
@ -328,30 +298,23 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::Save(raw_email)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::save(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
raw_email,
|
||||
)
|
||||
.await?;
|
||||
email::handlers::save(&mut printer, &id_mapper, &backend, &folder, raw_email).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Search(query, max_width, page_size, page)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::search(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
query,
|
||||
max_width,
|
||||
|
@ -364,14 +327,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
email::handlers::sort(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
criteria,
|
||||
query,
|
||||
|
@ -384,67 +347,38 @@ async fn main() -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Send(raw_email)) => {
|
||||
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?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
email::handlers::send(&account_config, &mut printer, &backend, raw_email).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(email::args::Cmd::Flag(m)) => match m {
|
||||
Some(flag::args::Cmd::Set(ids, ref flags)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
flag::handlers::set(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
ids,
|
||||
flags,
|
||||
)
|
||||
flag::handlers::set(&mut printer, &id_mapper, &backend, &folder, ids, flags)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(flag::args::Cmd::Add(ids, ref flags)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
flag::handlers::add(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
ids,
|
||||
flags,
|
||||
)
|
||||
flag::handlers::add(&mut printer, &id_mapper, &backend, &folder, ids, flags)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(flag::args::Cmd::Remove(ids, ref flags)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
flag::handlers::remove(
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&folder,
|
||||
ids,
|
||||
flags,
|
||||
)
|
||||
flag::handlers::remove(&mut printer, &id_mapper, &backend, &folder, ids, flags)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
|
@ -454,14 +388,14 @@ async fn main() -> Result<()> {
|
|||
Some(email::args::Cmd::Tpl(m)) => match m {
|
||||
Some(tpl::args::Cmd::Forward(id, headers, body)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
tpl::handlers::forward(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
id,
|
||||
headers,
|
||||
|
@ -477,14 +411,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(tpl::args::Cmd::Reply(id, all, headers, body)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
tpl::handlers::reply(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
id,
|
||||
all,
|
||||
|
@ -497,14 +431,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
Some(tpl::args::Cmd::Save(tpl)) => {
|
||||
let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
let mut backend = backend_builder.clone().into_build().await?;
|
||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config, &folder)?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
let id_mapper = IdMapper::new(&backend, &account_config, &folder)?;
|
||||
|
||||
tpl::handlers::save(
|
||||
&account_config,
|
||||
&mut printer,
|
||||
&id_mapper,
|
||||
backend.as_mut(),
|
||||
&backend,
|
||||
&folder,
|
||||
tpl,
|
||||
)
|
||||
|
@ -513,33 +447,16 @@ async fn main() -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
Some(tpl::args::Cmd::Send(tpl)) => {
|
||||
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(),
|
||||
tpl,
|
||||
)
|
||||
.await?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
tpl::handlers::send(&account_config, &mut printer, &backend, tpl).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Some(email::args::Cmd::Write(headers, body)) => {
|
||||
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?;
|
||||
let backend = backend_builder.clone().build().await?;
|
||||
email::handlers::write(&account_config, &mut printer, &backend, headers, body).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use anyhow::{Context, Result};
|
||||
use email::{
|
||||
account::AccountConfig,
|
||||
backend::Backend,
|
||||
email::{local_draft_path, remove_local_draft, Flag, Flags},
|
||||
sender::Sender,
|
||||
};
|
||||
use log::debug;
|
||||
use mml::MmlCompilerBuilder;
|
||||
|
@ -11,6 +9,7 @@ use process::Cmd;
|
|||
use std::{env, fs};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
printer::Printer,
|
||||
ui::choice::{self, PostEditChoice, PreEditChoice},
|
||||
};
|
||||
|
@ -45,8 +44,7 @@ pub async fn open_with_local_draft() -> Result<String> {
|
|||
pub async fn edit_tpl_with_editor<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut dyn Backend,
|
||||
sender: &mut dyn Sender,
|
||||
backend: &Backend,
|
||||
mut tpl: String,
|
||||
) -> Result<()> {
|
||||
let draft = local_draft_path();
|
||||
|
@ -86,13 +84,13 @@ pub async fn edit_tpl_with_editor<P: Printer>(
|
|||
|
||||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||
|
||||
sender.send(&email).await?;
|
||||
backend.send_raw_message(&email).await?;
|
||||
|
||||
if config.email_sending_save_copy {
|
||||
if config.email_sending_save_copy.unwrap_or_default() {
|
||||
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]))
|
||||
.add_raw_message_with_flag(&sent_folder, &email, Flag::Seen)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
@ -117,7 +115,7 @@ pub async fn edit_tpl_with_editor<P: Printer>(
|
|||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||
|
||||
backend
|
||||
.add_email(
|
||||
.add_raw_message_with_flags(
|
||||
"drafts",
|
||||
&email,
|
||||
&Flags::from_iter([Flag::Seen, Flag::Draft]),
|
||||
|
|
Loading…
Reference in a new issue