MailBackend: change more methods to Futures

master
Manos Pitsidianakis 2020-06-30 11:40:26 +03:00
parent 03522c0298
commit e06308fed2
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
21 changed files with 611 additions and 392 deletions

View File

@ -295,11 +295,11 @@ impl NotifyFn {
}
}
pub type ResultFuture<T> = Result<Pin<Box<dyn Future<Output = Result<T>> + Send + 'static>>>;
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
fn is_online(&self) -> Result<()>;
fn is_online_async(
&self,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>> {
fn is_online_async(&self) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn connect(&mut self) {}
@ -321,7 +321,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
&mut self,
_mailbox_hash: MailboxHash,
_sender: RefreshEventConsumer,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>> {
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn watch(
@ -330,15 +330,18 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
work_context: WorkContext,
) -> Result<std::thread::ThreadId>;
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>>;
fn mailboxes_async(
&self,
) -> Result<Pin<Box<dyn Future<Output = Result<HashMap<MailboxHash, Mailbox>>> + Send>>> {
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented."))
}
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>>;
fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()>;
fn delete(&self, _env_hash: EnvelopeHash, _mailbox_hash: MailboxHash) -> Result<()> {
fn save(
&self,
bytes: Vec<u8>,
mailbox_hash: MailboxHash,
flags: Option<Flag>,
) -> ResultFuture<()>;
fn delete(&self, _env_hash: EnvelopeHash, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
@ -353,22 +356,30 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
fn create_mailbox(
&mut self,
_path: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
Err(MeliError::new("Unimplemented."))
}
fn delete_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
) -> Result<HashMap<MailboxHash, Mailbox>> {
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented."))
}
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
fn set_mailbox_subscription(
&mut self,
_mailbox_hash: MailboxHash,
_val: bool,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
fn rename_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
_new_path: String,
) -> ResultFuture<Mailbox> {
Err(MeliError::new("Unimplemented."))
}
@ -376,7 +387,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
&mut self,
_mailbox_hash: MailboxHash,
_val: MailboxPermissions,
) -> Result<()> {
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
@ -384,7 +395,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
&self,
_query: crate::search::Query,
_mailbox_hash: Option<MailboxHash>,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
Err(MeliError::new("Unimplemented."))
}
}
@ -469,18 +480,10 @@ impl BackendOp for ReadOnlyOp {
fn fetch_flags(&self) -> Result<Flag> {
self.op.fetch_flags()
}
fn set_flag(
&mut self,
_flag: Flag,
_value: bool,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
fn set_flag(&mut self, _flag: Flag, _value: bool) -> ResultFuture<()> {
Err(MeliError::new("read-only set."))
}
fn set_tag(
&mut self,
_tag: String,
_value: bool,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
fn set_tag(&mut self, _tag: String, _value: bool) -> ResultFuture<()> {
Err(MeliError::new("read-only set."))
}
}

View File

@ -37,11 +37,10 @@ pub mod managesieve;
mod untagged;
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp;
use crate::backends::RefreshEvent;
use crate::backends::RefreshEventKind::{self, *};
use crate::backends::{AccountHash, MailboxHash};
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::backends::{
RefreshEventKind::{self, *},
*,
};
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result, ResultIntoMeliError};
@ -665,7 +664,12 @@ impl MailBackend for ImapType {
)))
}
fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> {
fn save(
&self,
bytes: Vec<u8>,
mailbox_hash: MailboxHash,
flags: Option<Flag>,
) -> ResultFuture<()> {
let path = {
let mailboxes = self.uid_store.mailboxes.read().unwrap();
@ -696,16 +700,16 @@ impl MailBackend for ImapType {
)?;
// wait for "+ Ready for literal data" reply
conn.wait_for_continuation_request()?;
conn.send_literal(bytes)?;
conn.send_literal(&bytes)?;
conn.read_response(&mut response, RequiredResponses::empty())?;
Ok(())
Ok(Box::pin(async { Ok(()) }))
}
fn as_any(&self) -> &dyn::std::any::Any {
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn::std::any::Any {
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
self
}
@ -720,7 +724,7 @@ impl MailBackend for ImapType {
fn create_mailbox(
&mut self,
mut path: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
/* Must transform path to something the IMAP server will accept
*
* Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
@ -768,13 +772,15 @@ impl MailBackend for ImapType {
let new_hash = get_path_hash!(path.as_str());
mailboxes.clear();
drop(mailboxes);
Ok((new_hash, self.mailboxes().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
let ret =
Ok((new_hash, self.mailboxes().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?));
Ok(Box::pin(async { ret }))
}
fn delete_mailbox(
&mut self,
mailbox_hash: MailboxHash,
) -> Result<HashMap<MailboxHash, Mailbox>> {
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
let mailboxes = self.uid_store.mailboxes.read().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.delete_mailbox {
@ -812,13 +818,19 @@ impl MailBackend for ImapType {
let mut mailboxes = self.uid_store.mailboxes.write().unwrap();
mailboxes.clear();
drop(mailboxes);
self.mailboxes().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
let res = self.mailboxes().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into());
Ok(Box::pin(async { res }))
}
fn set_mailbox_subscription(&mut self, mailbox_hash: MailboxHash, new_val: bool) -> Result<()> {
fn set_mailbox_subscription(
&mut self,
mailbox_hash: MailboxHash,
new_val: bool,
) -> ResultFuture<()> {
let mut mailboxes = self.uid_store.mailboxes.write().unwrap();
if mailboxes[&mailbox_hash].is_subscribed() == new_val {
return Ok(());
return Ok(Box::pin(async { Ok(()) }));
}
let mut response = String::with_capacity(8 * 1024);
@ -842,14 +854,14 @@ impl MailBackend for ImapType {
let _ = entry.set_is_subscribed(new_val);
});
}
ret
Ok(Box::pin(async { ret }))
}
fn rename_mailbox(
&mut self,
mailbox_hash: MailboxHash,
mut new_path: String,
) -> Result<Mailbox> {
) -> ResultFuture<Mailbox> {
let mut mailboxes = self.uid_store.mailboxes.write().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.delete_mailbox {
@ -880,30 +892,31 @@ impl MailBackend for ImapType {
mailboxes.clear();
drop(mailboxes);
self.mailboxes().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
Ok(BackendMailbox::clone(
let ret = Ok(BackendMailbox::clone(
&self.uid_store.mailboxes.read().unwrap()[&new_hash],
))
));
Ok(Box::pin(async { ret }))
}
fn set_mailbox_permissions(
&mut self,
mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions,
) -> Result<()> {
_val: MailboxPermissions,
) -> ResultFuture<()> {
let mailboxes = self.uid_store.mailboxes.write().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.change_permissions {
return Err(MeliError::new(format!("You do not have permission to change permissions for mailbox `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
}
Err(MeliError::new("Unimplemented."))
Ok(Box::pin(async { Err(MeliError::new("Unimplemented.")) }))
}
fn search(
&self,
query: crate::search::Query,
mailbox_hash: Option<MailboxHash>,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
if mailbox_hash.is_none() {
return Err(MeliError::new(
"Cannot search without specifying mailbox on IMAP",
@ -1011,7 +1024,7 @@ impl MailBackend for ImapType {
if l.starts_with("* SEARCH") {
use std::iter::FromIterator;
let uid_index = self.uid_store.uid_index.lock()?;
return Ok(SmallVec::from_iter(
let ret = Ok(SmallVec::from_iter(
l["* SEARCH".len()..]
.trim()
.split_whitespace()
@ -1020,6 +1033,7 @@ impl MailBackend for ImapType {
.filter_map(|uid| uid_index.get(&(mailbox_hash, uid)))
.map(|env_hash_ref| *env_hash_ref),
));
return Ok(Box::pin(async { ret }));
}
}
Err(MeliError::new(response))

View File

@ -355,7 +355,6 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
f.no_select = false;
f.is_subscribed = false;
for p in properties.split(|&b| b == b' ') {
use crate::backends::SpecialUsageMailbox;
if p.eq_ignore_ascii_case(b"\\NoSelect") {
f.no_select = true;
} else if p.eq_ignore_ascii_case(b"\\Sent") {

View File

@ -37,11 +37,10 @@ pub mod managesieve;
mod untagged;
use crate::async_workers::{Async, WorkContext};
use crate::backends::BackendOp;
use crate::backends::RefreshEvent;
use crate::backends::RefreshEventKind::{self, *};
use crate::backends::{AccountHash, MailboxHash};
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::backends::{
RefreshEventKind::{self, *},
*,
};
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result, ResultIntoMeliError};
@ -204,7 +203,7 @@ impl MailBackend for ImapType {
&mut self,
mailbox_hash: MailboxHash,
sender: RefreshEventConsumer,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>> {
) -> ResultFuture<()> {
let inbox = self
.uid_store
.mailboxes
@ -223,11 +222,7 @@ impl MailBackend for ImapType {
}))
}
fn mailboxes_async(
&self,
) -> Result<
core::pin::Pin<Box<dyn Future<Output = Result<HashMap<MailboxHash, Mailbox>>> + Send>>,
> {
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
let uid_store = self.uid_store.clone();
let connection = self.connection.clone();
Ok(Box::pin(async move {
@ -279,9 +274,7 @@ impl MailBackend for ImapType {
}))
}
fn is_online_async(
&self,
) -> Result<core::pin::Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
fn is_online_async(&self) -> ResultFuture<()> {
let connection = self.connection.clone();
Ok(Box::pin(async move {
debug!("INSIDE is_online_async()");
@ -392,7 +385,12 @@ impl MailBackend for ImapType {
)))
}
fn save(&self, _bytes: &[u8], _mailbox_hash: MailboxHash, _flags: Option<Flag>) -> Result<()> {
fn save(
&self,
_bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
_flags: Option<Flag>,
) -> ResultFuture<()> {
unimplemented!()
/*
let path = {
@ -450,7 +448,7 @@ impl MailBackend for ImapType {
fn create_mailbox(
&mut self,
_path: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
unimplemented!()
/*
/* Must transform path to something the IMAP server will accept
@ -507,7 +505,7 @@ impl MailBackend for ImapType {
fn delete_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
) -> Result<HashMap<MailboxHash, Mailbox>> {
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
unimplemented!()
/*
let mailboxes = self.uid_store.mailboxes.read().unwrap();
@ -553,7 +551,7 @@ impl MailBackend for ImapType {
&mut self,
_mailbox_hash: MailboxHash,
_new_val: bool,
) -> Result<()> {
) -> ResultFuture<()> {
unimplemented!()
/*
let mut mailboxes = self.uid_store.mailboxes.write().unwrap();
@ -586,7 +584,11 @@ impl MailBackend for ImapType {
*/
}
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
fn rename_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
_new_path: String,
) -> ResultFuture<Mailbox> {
unimplemented!()
/*
let mut mailboxes = self.uid_store.mailboxes.write().unwrap();
@ -629,7 +631,7 @@ impl MailBackend for ImapType {
&mut self,
_mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions,
) -> Result<()> {
) -> ResultFuture<()> {
unimplemented!()
/*
let mailboxes = self.uid_store.mailboxes.write().unwrap();
@ -644,11 +646,9 @@ impl MailBackend for ImapType {
fn search(
&self,
_query: crate::search::Query,
_mailbox_hash: Option<MailboxHash>,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
unimplemented!()
/*
query: crate::search::Query,
mailbox_hash: Option<MailboxHash>,
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
if mailbox_hash.is_none() {
return Err(MeliError::new(
"Cannot search without specifying mailbox on IMAP",
@ -743,32 +743,38 @@ impl MailBackend for ImapType {
}
let mut query_str = String::new();
rec(&query, &mut query_str);
let connection = self.connection.clone();
let uid_store = self.uid_store.clone();
let mut response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
conn.examine_mailbox(mailbox_hash, &mut response)?;
conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())?;
conn.read_response(&mut response, RequiredResponses::SEARCH)?;
debug!(&response);
Ok(Box::pin(async move {
let mut response = String::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
conn.examine_mailbox(mailbox_hash, &mut response, false)
.await?;
conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())
.await?;
conn.read_response(&mut response, RequiredResponses::SEARCH)
.await?;
debug!(&response);
let mut lines = response.lines();
for l in lines.by_ref() {
if l.starts_with("* SEARCH") {
use std::iter::FromIterator;
let uid_index = self.uid_store.uid_index.lock()?;
return Ok(SmallVec::from_iter(
l["* SEARCH".len()..]
.trim()
.split_whitespace()
.map(usize::from_str)
.filter_map(std::result::Result::ok)
.filter_map(|uid| uid_index.get(&(mailbox_hash, uid)))
.map(|env_hash_ref| *env_hash_ref),
));
let mut lines = response.lines();
for l in lines.by_ref() {
if l.starts_with("* SEARCH") {
use std::iter::FromIterator;
let uid_index = uid_store.uid_index.lock()?;
return Ok(SmallVec::from_iter(
l["* SEARCH".len()..]
.trim()
.split_whitespace()
.map(usize::from_str)
.filter_map(std::result::Result::ok)
.filter_map(|uid| uid_index.get(&(mailbox_hash, uid)))
.map(|env_hash_ref| *env_hash_ref),
));
}
}
}
Err(MeliError::new(response))
*/
Err(MeliError::new(response))
}))
}
}
@ -1206,6 +1212,7 @@ async fn get_hlpr(
h.write_usize(uid);
h.write(mailbox_path.as_bytes());
env.set_hash(h.finish());
/*
debug!(
"env hash {} {} UID = {} MSN = {}",
env.hash(),
@ -1213,6 +1220,7 @@ async fn get_hlpr(
uid,
message_sequence_number
);
*/
valid_hash_set.insert(env.hash());
let mut tag_lck = uid_store.tag_index.write().unwrap();
if let Some((flags, keywords)) = flags {
@ -1273,7 +1281,6 @@ async fn get_hlpr(
kind: RefreshEventKind::Remove(env_hash),
});
}
drop(conn);
unseen
.lock()
.unwrap()
@ -1282,9 +1289,10 @@ async fn get_hlpr(
.lock()
.unwrap()
.insert_existing_set(envelopes.iter().map(|(_, env)| env.hash()).collect::<_>());
drop(conn);
payload.extend(envelopes.into_iter().map(|(_, env)| env));
}
*max_uid = if max_uid_left == 1 {
*max_uid = if max_uid_left <= 1 {
Some(0)
} else {
Some(std::cmp::max(

View File

@ -508,7 +508,6 @@ impl ImapConnection {
if required_responses.check(l) || !self.process_untagged(l).await? {
ret.push_str(l);
}
ret.push_str(l);
}
}
}

View File

@ -355,7 +355,6 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
f.no_select = false;
f.is_subscribed = false;
for p in properties.split(|&b| b == b' ') {
use crate::backends::SpecialUsageMailbox;
if p.eq_ignore_ascii_case(b"\\NoSelect") {
f.no_select = true;
} else if p.eq_ignore_ascii_case(b"\\Sent") {

View File

@ -20,9 +20,7 @@
*/
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp;
use crate::backends::MailboxHash;
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::backends::*;
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result};
@ -262,11 +260,16 @@ impl MailBackend for JmapType {
)))
}
fn save(&self, _bytes: &[u8], _mailbox_hash: MailboxHash, _flags: Option<Flag>) -> Result<()> {
fn save(
&self,
_bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
_flags: Option<Flag>,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn as_any(&self) -> &dyn::std::any::Any {
fn as_any(&self) -> &dyn ::std::any::Any {
self
}

View File

@ -178,6 +178,10 @@ impl MailBackend for MaildirType {
Ok(())
}
fn is_online_async(&self) -> ResultFuture<()> {
Ok(Box::pin(async { Ok(()) }))
}
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
Ok(self
.mailboxes
@ -185,6 +189,10 @@ impl MailBackend for MaildirType {
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
.collect())
}
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
let res = self.mailboxes();
Ok(Box::pin(async { res }))
}
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, mailbox)
@ -670,18 +678,26 @@ impl MailBackend for MaildirType {
)))
}
fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> {
MaildirType::save_to_mailbox(self.mailboxes[&mailbox_hash].fs_path.clone(), bytes, flags)
fn save(
&self,
bytes: Vec<u8>,
mailbox_hash: MailboxHash,
flags: Option<Flag>,
) -> ResultFuture<()> {
let path = self.mailboxes[&mailbox_hash].fs_path.clone();
Ok(Box::pin(async move {
MaildirType::save_to_mailbox(path, bytes, flags)
}))
}
fn as_any(&self) -> &dyn::std::any::Any {
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
fn create_mailbox(
&mut self,
new_path: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
let mut path = self.path.clone();
path.push(&new_path);
if !path.starts_with(&self.path) {
@ -720,21 +736,30 @@ impl MailBackend for MaildirType {
};
self.mailboxes.insert(mailbox_hash, new_mailbox);
Ok((mailbox_hash, self.mailboxes()?))
let ret = Ok((mailbox_hash, self.mailboxes()?));
Ok(Box::pin(async { ret }))
}
fn delete_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
) -> Result<HashMap<MailboxHash, Mailbox>> {
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented."))
}
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
fn set_mailbox_subscription(
&mut self,
_mailbox_hash: MailboxHash,
_val: bool,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
fn rename_mailbox(
&mut self,
_mailbox_hash: MailboxHash,
_new_path: String,
) -> ResultFuture<Mailbox> {
Err(MeliError::new("Unimplemented."))
}
@ -742,7 +767,7 @@ impl MailBackend for MaildirType {
&mut self,
_mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions,
) -> Result<()> {
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
}
@ -1084,7 +1109,7 @@ impl MaildirType {
w.build(handle)
}
pub fn save_to_mailbox(mut path: PathBuf, bytes: &[u8], flags: Option<Flag>) -> Result<()> {
pub fn save_to_mailbox(mut path: PathBuf, bytes: Vec<u8>, flags: Option<Flag>) -> Result<()> {
for d in &["cur", "new", "tmp"] {
path.push(d);
if !path.is_dir() {
@ -1149,7 +1174,7 @@ impl MaildirType {
file.set_permissions(permissions)?;
let mut writer = io::BufWriter::new(file);
writer.write_all(bytes).unwrap();
writer.write_all(&bytes).unwrap();
return Ok(());
}

View File

@ -902,7 +902,12 @@ impl MailBackend for MboxType {
)))
}
fn save(&self, _bytes: &[u8], _mailbox_hash: MailboxHash, _flags: Option<Flag>) -> Result<()> {
fn save(
&self,
_bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
_flags: Option<Flag>,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}

View File

@ -611,13 +611,19 @@ impl MailBackend for NotmuchDb {
}))
}
fn save(&self, bytes: &[u8], _mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> {
fn save(
&self,
bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
flags: Option<Flag>,
) -> ResultFuture<()> {
let path = self
.save_messages_to
.as_ref()
.unwrap_or(&self.path)
.to_path_buf();
MaildirType::save_to_mailbox(path, bytes, flags)
MaildirType::save_to_mailbox(path, bytes, flags)?;
Ok(Box::pin(async { Ok(()) }))
}
fn as_any(&self) -> &dyn ::std::any::Any {

View File

@ -356,7 +356,13 @@ pub trait ListingTrait: Component {
fn set_coordinates(&mut self, _: (usize, MailboxHash));
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
fn filter(
&mut self,
_filter_term: String,
_results: Result<SmallVec<[EnvelopeHash; 512]>>,
_context: &Context,
) {
}
fn set_movement(&mut self, mvm: PageMovement);
}

View File

@ -22,6 +22,7 @@
use super::EntryStrings;
use super::*;
use crate::components::utilities::PageMovement;
use crate::jobs1::{oneshot, JobId};
use std::cmp;
use std::convert::TryInto;
use std::iter::FromIterator;
@ -61,6 +62,11 @@ pub struct CompactListing {
rows_drawn: SegmentTree,
rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>,
search_job: Option<(
String,
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
JobId,
)>,
filter_term: String,
filtered_selection: Vec<ThreadHash>,
filtered_order: HashMap<ThreadHash, usize>,
@ -724,21 +730,22 @@ impl ListingTrait for CompactListing {
context.dirty_areas.push_back(area);
}
fn filter(&mut self, filter_term: &str, context: &Context) {
if filter_term.is_empty() {
return;
}
fn filter(
&mut self,
filter_term: String,
results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
self.order.clear();
self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term.to_string();
self.filter_term = filter_term;
self.row_updates.clear();
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
match results {
Ok(results) => {
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
@ -841,6 +848,7 @@ impl CompactListing {
subsort: (SortField::Date, SortOrder::Desc),
all_threads: HashSet::default(),
order: HashMap::default(),
search_job: None,
filter_term: String::new(),
filtered_selection: Vec::new(),
filtered_order: HashMap::default(),
@ -1520,8 +1528,41 @@ impl Component for CompactListing {
return true;
}
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
self.filter(filter_term, context);
self.dirty = true;
match context.accounts[self.cursor_pos.0].search(
filter_term,
self.sort,
self.cursor_pos.1,
) {
Ok(job) => {
let (chan, job_id) = context.accounts[self.cursor_pos.0]
.job_executor
.spawn_specialized(job);
context.accounts[self.cursor_pos.0]
.active_jobs
.insert(job_id.clone(), crate::conf::accounts::JobRequest::Search);
self.search_job = Some((filter_term.to_string(), chan, job_id));
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::ERROR),
));
}
};
self.set_dirty(true);
}
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
if self
.search_job
.as_ref()
.map(|(_, _, j)| j == job_id)
.unwrap_or(false) =>
{
let (filter_term, mut rcvr, job_id) = self.search_job.take().unwrap();
let results = rcvr.try_recv().unwrap().unwrap();
self.filter(filter_term, results, context);
self.set_dirty(true);
}
_ => {}
}

View File

@ -687,54 +687,88 @@ impl ListingTrait for ConversationsListing {
context.dirty_areas.push_back(area);
}
fn filter(&mut self, filter_term: &str, context: &Context) {
if filter_term.is_empty() {
return;
}
fn filter(
&mut self,
filter_term: String,
results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
/*
if filter_term.is_empty() {
return;
}
self.order.clear();
self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term.to_string();
self.row_updates.clear();
for v in self.selection.values_mut() {
*v = false;
}
self.order.clear();
self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term.to_string();
self.row_updates.clear();
for v in self.selection.values_mut() {
*v = false;
}
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
/*
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
}
let env_thread_node_hash = account.collection.get_env(env_hash).thread();
if !threads.thread_nodes.contains_key(&env_thread_node_hash) {
continue;
}
let thread =
threads.find_group(threads.thread_nodes[&env_thread_node_hash].group);
if self.filtered_order.contains_key(&thread) {
continue;
}
if self.all_threads.contains(&thread) {
self.filtered_selection.push(thread);
self.filtered_order
.insert(thread, self.filtered_selection.len() - 1);
}
}
let env_thread_node_hash = account.collection.get_env(env_hash).thread();
if !threads.thread_nodes.contains_key(&env_thread_node_hash) {
continue;
if !self.filtered_selection.is_empty() {
threads.group_inner_sort_by(
&mut self.filtered_selection,
self.sort,
&context.accounts[self.cursor_pos.0].collection.envelopes,
);
self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.content = CellBuffer::new_with_context(0, 0, default_cell, context);
}
let thread =
threads.find_group(threads.thread_nodes[&env_thread_node_hash].group);
if self.filtered_order.contains_key(&thread) {
continue;
}
if self.all_threads.contains(&thread) {
self.filtered_selection.push(thread);
self.filtered_order
.insert(thread, self.filtered_selection.len() - 1);
}
}
if !self.filtered_selection.is_empty() {
threads.group_inner_sort_by(
&mut self.filtered_selection,
self.sort,
&context.accounts[self.cursor_pos.0].collection.envelopes,
self.redraw_threads_list(
context,
Box::new(self.filtered_selection.clone().into_iter())
as Box<dyn Iterator<Item = ThreadHash>>,
);
*/
}
Err(e) => {
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
let message = format!(
"Encountered an error while searching for `{}`: {}.",
self.filter_term, e
);
log(
format!("Failed to search for term {}: {}", self.filter_term, e),
ERROR,
);
self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
@ -742,45 +776,20 @@ impl ListingTrait for ConversationsListing {
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.content = CellBuffer::new_with_context(0, 0, default_cell, context);
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
&mut self.content,
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
((0, 0), (message.len() - 1, 0)),
None,
);
}
self.redraw_threads_list(
context,
Box::new(self.filtered_selection.clone().into_iter())
as Box<dyn Iterator<Item = ThreadHash>>,
);
}
Err(e) => {
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
let message = format!(
"Encountered an error while searching for `{}`: {}.",
self.filter_term, e
);
log(
format!("Failed to search for term {}: {}", self.filter_term, e),
ERROR,
);
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
&mut self.content,
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
((0, 0), (message.len() - 1, 0)),
None,
);
}
}
*/
}
fn set_movement(&mut self, mvm: PageMovement) {
@ -1336,7 +1345,7 @@ impl Component for ConversationsListing {
}
UIEvent::Action(ref action) => match action {
Action::Listing(Search(ref filter_term)) if !self.unfocused => {
self.filter(filter_term, context);
//self.filter(filter_term, context);
self.dirty = true;
return true;
}

View File

@ -74,8 +74,6 @@ impl ListingTrait for OfflineListing {
fn draw_list(&mut self, _: &mut CellBuffer, _: Area, _: &mut Context) {}
fn filter(&mut self, _: &str, _: &Context) {}
fn set_movement(&mut self, _: PageMovement) {}
}

View File

@ -581,42 +581,77 @@ impl ListingTrait for PlainListing {
context.dirty_areas.push_back(area);
}
fn filter(&mut self, filter_term: &str, context: &Context) {
if filter_term.is_empty() {
return;
}
fn filter(
&mut self,
filter_term: String,
results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
/*
if filter_term.is_empty() {
return;
}
self.order.clear();
self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term.to_string();
self.row_updates.clear();
for v in self.selection.values_mut() {
*v = false;
}
self.order.clear();
self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term.to_string();
self.row_updates.clear();
for v in self.selection.values_mut() {
*v = false;
}
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
/*
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
}
if self.filtered_order.contains_key(&env_hash) {
continue;
}
if self.all_envelopes.contains(&env_hash) {
self.filtered_selection.push(env_hash);
self.filtered_order
.insert(env_hash, self.filtered_selection.len() - 1);
}
}
if self.filtered_order.contains_key(&env_hash) {
continue;
}
if self.all_envelopes.contains(&env_hash) {
self.filtered_selection.push(env_hash);
self.filtered_order
.insert(env_hash, self.filtered_selection.len() - 1);
if !self.filtered_selection.is_empty() {
self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] =
CellBuffer::new_with_context(0, 0, default_cell, context);
}
self.redraw_list(
context,
Box::new(self.filtered_selection.clone().into_iter())
as Box<dyn Iterator<Item = EnvelopeHash>>,
);
*/
}
if !self.filtered_selection.is_empty() {
self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else {
Err(e) => {
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
let message = format!(
"Encountered an error while searching for `{}`: {}.",
&self.filter_term, e
);
log(
format!("Failed to search for term {}: {}", &self.filter_term, e),
ERROR,
);
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
@ -625,45 +660,19 @@ impl ListingTrait for PlainListing {
ret
};
self.data_columns.columns[0] =
CellBuffer::new_with_context(0, 0, default_cell, context);
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
&mut self.data_columns.columns[0],
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
((0, 0), (message.len() - 1, 0)),
None,
);
}
self.redraw_list(
context,
Box::new(self.filtered_selection.clone().into_iter())
as Box<dyn Iterator<Item = EnvelopeHash>>,
);
}
Err(e) => {
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
let message = format!(
"Encountered an error while searching for `{}`: {}.",
&self.filter_term, e
);
log(
format!("Failed to search for term {}: {}", &self.filter_term, e),
ERROR,
);
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
&mut self.data_columns.columns[0],
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
((0, 0), (message.len() - 1, 0)),
None,
);
}
}
*/
}
fn set_movement(&mut self, mvm: PageMovement) {
@ -1267,7 +1276,7 @@ impl Component for PlainListing {
return true;
}
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
self.filter(filter_term, context);
//self.filter(filter_term, context);
self.dirty = true;
}
_ => {}

View File

@ -30,6 +30,7 @@ pub use self::widgets::*;
mod layouts;
pub use self::layouts::*;
use crate::jobs1::JobId;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy)]
@ -636,6 +637,8 @@ pub struct StatusBar {
height: usize,
dirty: bool,
id: ComponentId,
progress_spinner: ProgressSpinner,
in_progress_jobs: HashSet<JobId>,
auto_complete: AutoComplete,
cmd_history: Vec<String>,
@ -661,7 +664,8 @@ impl StatusBar {
height: 1,
id: ComponentId::new_v4(),
auto_complete: AutoComplete::new(Vec::new()),
progress_spinner: ProgressSpinner::new(3),
in_progress_jobs: HashSet::default(),
cmd_history: crate::execute::history::old_cmd_history(),
}
}
@ -755,6 +759,17 @@ impl Component for StatusBar {
context,
);
if self.progress_spinner.is_dirty() {
self.progress_spinner.draw(
grid,
(
(get_x(bottom_right).saturating_sub(1), get_y(bottom_right)),
bottom_right,
),
context,
);
}
if self.mode != UIMode::Execute && !self.is_dirty() {
return;
}
@ -1148,12 +1163,30 @@ impl Component for StatusBar {
self.status = format!("{} | {}", self.mode, std::mem::replace(s, String::new()));
self.dirty = true;
}
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id)) => {
self.in_progress_jobs.remove(job_id);
if self.in_progress_jobs.is_empty() {
self.progress_spinner.stop();
self.set_dirty(true);
}
}
UIEvent::StatusEvent(StatusEvent::NewJob(ref job_id)) => {
if self.in_progress_jobs.is_empty() {
self.progress_spinner.start();
}
self.in_progress_jobs.insert(job_id.clone());
}
UIEvent::Timer(_) => {
if self.progress_spinner.process_event(event, context) {
return true;
}
}
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty || self.container.is_dirty()
self.dirty || self.container.is_dirty() || self.progress_spinner.is_dirty()
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;

View File

@ -992,6 +992,8 @@ impl ScrollBar {
#[derive(Debug)]
pub struct ProgressSpinner {
//total_work: usize,
//finished: usize,
timer: crate::timer::PosixTimer,
stage: usize,
kind: usize,
@ -1018,10 +1020,13 @@ impl ProgressSpinner {
&["", ""],
];
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
const VALUE: std::time::Duration = std::time::Duration::from_millis(500);
pub fn new(kind: usize) -> Self {
let timer = crate::timer::PosixTimer::new_with_signal(
std::time::Duration::from_millis(50),
std::time::Duration::from_millis(500),
std::time::Duration::from_millis(0),
std::time::Duration::from_millis(0),
nix::sys::signal::Signal::SIGALRM,
)
.unwrap();
@ -1033,6 +1038,20 @@ impl ProgressSpinner {
id: ComponentId::new_v4(),
}
}
pub fn start(&mut self) {
self.timer
.set_interval(Self::INTERVAL)
.set_value(Self::VALUE)
.rearm()
}
pub fn stop(&mut self) {
self.timer
.set_interval(std::time::Duration::from_millis(0))
.set_value(std::time::Duration::from_millis(0))
.rearm()
}
}
impl fmt::Display for ProgressSpinner {

View File

@ -28,7 +28,7 @@ use crate::jobs1::{JobExecutor, JobId, JoinHandle};
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use melib::backends::{
AccountHash, BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp,
RefreshEvent, RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
RefreshEvent, RefreshEventConsumer, RefreshEventKind, ResultFuture, SpecialUsageMailbox,
};
use melib::email::*;
use melib::error::{MeliError, Result};
@ -43,6 +43,7 @@ use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate
use crate::{StatusEvent, ThreadEvent};
use crossbeam::Sender;
use futures::channel::oneshot;
use futures::future::FutureExt;
pub use futures::stream::Stream;
use futures::stream::StreamExt;
use std::collections::VecDeque;
@ -131,13 +132,13 @@ pub struct Account {
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
pub job_executor: Arc<JobExecutor>,
active_jobs: HashMap<JobId, JobRequest>,
pub active_jobs: HashMap<JobId, JobRequest>,
sender: Sender<ThreadEvent>,
event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
notify_fn: Arc<NotifyFn>,
}
enum JobRequest {
pub enum JobRequest {
Mailboxes(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>),
Get(
MailboxHash,
@ -154,11 +155,7 @@ enum JobRequest {
CreateMailbox(oneshot::Receiver<Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)>>),
DeleteMailbox(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>),
//RenameMailbox,
Search(
crate::search::Query,
Option<MailboxHash>,
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
),
Search,
SetMailboxPermissions(MailboxHash, oneshot::Receiver<Result<()>>),
SetMailboxSubscription(MailboxHash, oneshot::Receiver<Result<()>>),
Watch(JoinHandle),
@ -177,7 +174,7 @@ impl core::fmt::Debug for JobRequest {
JobRequest::CreateMailbox(_) => write!(f, "{}", "JobRequest::CreateMailbox"),
JobRequest::DeleteMailbox(_) => write!(f, "{}", "JobRequest::DeleteMailbox"),
//JobRequest::RenameMailbox,
JobRequest::Search(_, _, _) => write!(f, "{}", "JobRequest::Search"),
JobRequest::Search => write!(f, "{}", "JobRequest::Search"),
JobRequest::SetMailboxPermissions(_, _) => {
write!(f, "{}", "JobRequest::SetMailboxPermissions")
}
@ -189,6 +186,22 @@ impl core::fmt::Debug for JobRequest {
}
}
impl JobRequest {
fn is_get(&self, mailbox_hash: MailboxHash) -> bool {
match self {
JobRequest::Get(h, _) if *h == mailbox_hash => true,
_ => false,
}
}
fn is_online(&self) -> bool {
match self {
JobRequest::IsOnline(_) => true,
_ => false,
}
}
}
impl Drop for Account {
fn drop(&mut self) {
if let Ok(data_dir) = xdg::BaseDirectories::with_profile("meli", &self.name) {
@ -294,14 +307,17 @@ impl Account {
let mut active_jobs = HashMap::default();
if settings.conf.is_async {
if let Ok(mailboxes_job) = backend.mailboxes_async() {
if let Ok(online_job) = backend.is_online_async() {
let (rcvr, job_id) =
job_executor.spawn_specialized(online_job.then(|_| mailboxes_job));
active_jobs.insert(job_id, JobRequest::Mailboxes(rcvr));
}
}
if let Ok(online_job) = backend.is_online_async() {
let (rcvr, job_id) = job_executor.spawn_specialized(online_job);
active_jobs.insert(job_id, JobRequest::IsOnline(rcvr));
}
if let Ok(mailboxes_job) = backend.mailboxes_async() {
let (rcvr, job_id) = job_executor.spawn_specialized(mailboxes_job);
active_jobs.insert(job_id, JobRequest::Mailboxes(rcvr));
}
}
Ok(Account {
@ -460,6 +476,11 @@ impl Account {
if let Ok(mailbox_job) = self.backend.write().unwrap().get_async(&f) {
let mailbox_job = mailbox_job.into_future();
let (rcvr, job_id) = self.job_executor.spawn_specialized(mailbox_job);
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs.insert(job_id, JobRequest::Get(*h, rcvr));
}
} else {
@ -790,6 +811,11 @@ impl Account {
if self.settings.conf.is_async {
if let Ok(refresh_job) = self.backend.write().unwrap().refresh_async(mailbox_hash, r) {
let (rcvr, job_id) = self.job_executor.spawn_specialized(refresh_job);
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs
.insert(job_id, JobRequest::Refresh(mailbox_hash, rcvr));
}
@ -882,18 +908,25 @@ impl Account {
}
MailboxStatus::None => {
if self.settings.conf.is_async {
if let Ok(mailbox_job) =
self.backend.write().unwrap().get_async(
&&self.mailbox_entries[&mailbox_hash].ref_mailbox,
)
{
let mailbox_job = mailbox_job.into_future();
let (rcvr, job_id) =
self.job_executor.spawn_specialized(mailbox_job);
self.active_jobs
.insert(job_id, JobRequest::Get(mailbox_hash, rcvr));
if !self.active_jobs.values().any(|j| j.is_get(mailbox_hash)) {
if let Ok(mailbox_job) =
self.backend.write().unwrap().get_async(
&&self.mailbox_entries[&mailbox_hash].ref_mailbox,
)
{
let mailbox_job = mailbox_job.into_future();
let (rcvr, job_id) =
self.job_executor.spawn_specialized(mailbox_job);
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs
.insert(job_id, JobRequest::Get(mailbox_hash, rcvr));
}
}
} else {
} else if self.mailbox_entries[&mailbox_hash].worker.is_none() {
let handle = Account::new_worker(
self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(),
&mut self.backend,
@ -991,7 +1024,7 @@ impl Account {
}
pub fn save_special(
&self,
&mut self,
bytes: &[u8],
mailbox_type: SpecialUsageMailbox,
flags: Flag,
@ -1042,27 +1075,55 @@ impl Account {
}
}
pub fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> {
pub fn save(
&mut self,
bytes: &[u8],
mailbox_hash: MailboxHash,
flags: Option<Flag>,
) -> Result<()> {
if self.settings.account.read_only() {
return Err(MeliError::new(format!(
"Account {} is read-only.",
self.name.as_str()
)));
}
self.backend
let job = self
.backend
.write()
.unwrap()
.save(bytes, mailbox_hash, flags)
.save(bytes.to_vec(), mailbox_hash, flags)?;
let (rcvr, job_id) = self.job_executor.spawn_specialized(job);
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs
.insert(job_id, JobRequest::SaveMessage(mailbox_hash, rcvr));
Ok(())
}
pub fn delete(&self, env_hash: EnvelopeHash, mailbox_hash: MailboxHash) -> Result<()> {
pub fn delete(&mut self, env_hash: EnvelopeHash, mailbox_hash: MailboxHash) -> Result<()> {
if self.settings.account.read_only() {
return Err(MeliError::new(format!(
"Account {} is read-only.",
self.name.as_str()
)));
}
self.backend.write().unwrap().delete(env_hash, mailbox_hash)
let job = self
.backend
.write()
.unwrap()
.delete(env_hash, mailbox_hash)?;
let (rcvr, job_id) = self.job_executor.spawn_specialized(job);
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs
.insert(job_id, JobRequest::DeleteMessage(env_hash, rcvr));
Ok(())
}
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
@ -1091,11 +1152,12 @@ impl Account {
}
match op {
MailboxOperation::Create(path) => {
let (mailbox_hash, mut mailboxes) = self
.backend
.write()
.unwrap()
.create_mailbox(path.to_string())?;
let (mailbox_hash, mut mailboxes) = futures::executor::block_on(
self.backend
.write()
.unwrap()
.create_mailbox(path.to_string())?,
)?;
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
self.index,
@ -1159,7 +1221,9 @@ impl Account {
return Err(MeliError::new("Cannot delete only mailbox."));
}
let mailbox_hash = self.mailbox_by_path(&path)?;
let mut mailboxes = self.backend.write().unwrap().delete_mailbox(mailbox_hash)?;
let mut mailboxes = futures::executor::block_on(
self.backend.write().unwrap().delete_mailbox(mailbox_hash)?,
)?;
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
self.index,
@ -1254,13 +1318,7 @@ impl Account {
if self.is_online {
return Ok(());
}
if !self.active_jobs.values().any(|j| {
if let JobRequest::IsOnline(_) = j {
true
} else {
false
}
}) {
if !self.active_jobs.values().any(JobRequest::is_online) {
if let Ok(online_job) = self.backend.read().unwrap().is_online_async() {
let (rcvr, job_id) = self.job_executor.spawn_specialized(online_job);
self.active_jobs.insert(job_id, JobRequest::IsOnline(rcvr));
@ -1270,7 +1328,7 @@ impl Account {
} else {
let ret = self.backend.read().unwrap().is_online();
if ret.is_ok() != self.is_online && ret.is_ok() {
//self.init()?;
self.init(None)?;
}
self.is_online = ret.is_ok();
if !self.is_online {
@ -1285,33 +1343,14 @@ impl Account {
search_term: &str,
sort: (SortField, SortOrder),
mailbox_hash: MailboxHash,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
if self.settings.account().format() == "imap" {
use melib::parsec::Parser;
let query = melib::search::query().parse(search_term)?.1;
return self
.backend
.read()
.unwrap()
.search(query, Some(mailbox_hash));
}
#[cfg(feature = "notmuch")]
{
if self.settings.account().format() == "notmuch" {
let backend_lck = self.backend.read().unwrap();
let b = (*backend_lck).as_any();
return if let Some(notmuch_backend) = b.downcast_ref::<melib::backends::NotmuchDb>()
{
notmuch_backend.search(search_term)
} else {
Err(MeliError::new(
"Internal error: Could not downcast backend to NotmuchDb",
))
};
}
}
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
use melib::parsec::Parser;
let query = melib::search::query().parse(search_term)?.1;
self.backend
.read()
.unwrap()
.search(query, Some(mailbox_hash))
/*
#[cfg(feature = "sqlite3")]
{
crate::sqlite3::search(search_term, sort)
@ -1346,6 +1385,7 @@ impl Account {
}
Ok(ret)
}
*/
}
pub fn mailbox_by_path(&self, path: &str) -> Result<MailboxHash> {
@ -1379,6 +1419,12 @@ impl Account {
}
}
JobRequest::Get(mailbox_hash, mut chan) => {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::JobFinished(job_id.clone()),
)))
.unwrap();
let (payload, rest): (Option<Result<Vec<Envelope>>>, _) =
chan.try_recv().unwrap().unwrap();
debug!("got payload in status for {}", mailbox_hash);
@ -1398,8 +1444,12 @@ impl Account {
.unwrap();
return true;
}
let (rcvr, job_id) = self.job_executor.spawn_specialized(rest.into_future());
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::NewJob(job_id.clone()),
)))
.unwrap();
self.active_jobs
.insert(job_id, JobRequest::Get(mailbox_hash, rcvr));
let payload = payload.unwrap();
@ -1465,18 +1515,11 @@ impl Account {
if r.is_some() && r.unwrap().is_ok() {
self.is_online = true;
}
}
JobRequest::Refresh(_, mut chan) => {
let r = chan.try_recv().unwrap();
if let Some(Err(err)) = r {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::Notification(
Some(format!("{} refresh exited with error", &self.name)),
err.to_string(),
Some(crate::types::NotificationType::ERROR),
)))
.expect("Could not send event on main channel");
}
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::JobFinished(job_id.clone()),
)))
.unwrap();
}
JobRequest::SetFlags(_, mut chan) => {
let r = chan.try_recv().unwrap();
@ -1558,21 +1601,12 @@ impl Account {
}
}
//JobRequest::RenameMailbox,
JobRequest::Search(_, _, mut chan) => {
let r = chan.try_recv().unwrap();
match r {
Some(Err(err)) => {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::Notification(
Some(format!("{}: could not perform search", &self.name)),
err.to_string(),
Some(crate::types::NotificationType::ERROR),
)))
.expect("Could not send event on main channel");
}
Some(Ok(v)) => unimplemented!(),
None => {}
}
JobRequest::Search => {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::JobFinished(job_id.clone()),
)))
.unwrap();
}
JobRequest::SetMailboxPermissions(_, mut chan) => {
let r = chan.try_recv().unwrap();

View File

@ -213,13 +213,18 @@ impl MailBackend for PluginBackend {
}))
}
fn save(&self, _bytes: &[u8], _mailbox_hash: MailboxHash, _flags: Option<Flag>) -> Result<()> {
fn save(
&self,
_bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
_flags: Option<Flag>,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn create_mailbox(
&mut self,
_name: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
Err(MeliError::new("Unimplemented."))
}
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {

View File

@ -53,6 +53,8 @@ pub enum StatusEvent {
BufClear,
BufSet(String),
UpdateStatus(String),
NewJob(JobId),
JobFinished(JobId),
}
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads

View File

@ -115,13 +115,15 @@ pub mod timer {
}
/// Sets value without arming timer
pub fn set_value(&mut self, value: Duration) {
pub fn set_value(&mut self, value: Duration) -> &mut Self {
self.value = value;
self
}
/// Sets interval without arming timer
pub fn set_interval(&mut self, interval: Duration) {
pub fn set_interval(&mut self, interval: Duration) -> &mut Self {
self.interval = interval;
self
}
pub fn new_with_signal(