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