mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-24 20:10:23 +00:00
bump lib with backend features
This commit is contained in:
parent
56fc31b367
commit
cec658aff4
26 changed files with 2622 additions and 1805 deletions
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