MailBackend: change more methods to Futures

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

View File

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

View File

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

View File

@ -508,7 +508,6 @@ impl ImapConnection {
if required_responses.check(l) || !self.process_untagged(l).await? { if required_responses.check(l) || !self.process_untagged(l).await? {
ret.push_str(l); 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.no_select = false;
f.is_subscribed = false; f.is_subscribed = false;
for p in properties.split(|&b| b == b' ') { for p in properties.split(|&b| b == b' ') {
use crate::backends::SpecialUsageMailbox;
if p.eq_ignore_ascii_case(b"\\NoSelect") { if p.eq_ignore_ascii_case(b"\\NoSelect") {
f.no_select = true; f.no_select = true;
} else if p.eq_ignore_ascii_case(b"\\Sent") { } 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::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp; use crate::backends::*;
use crate::backends::MailboxHash;
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::conf::AccountSettings; use crate::conf::AccountSettings;
use crate::email::*; use crate::email::*;
use crate::error::{MeliError, Result}; 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.")) Err(MeliError::new("Unimplemented."))
} }
fn as_any(&self) -> &dyn::std::any::Any { fn as_any(&self) -> &dyn ::std::any::Any {
self self
} }

View File

@ -178,6 +178,10 @@ impl MailBackend for MaildirType {
Ok(()) Ok(())
} }
fn is_online_async(&self) -> ResultFuture<()> {
Ok(Box::pin(async { Ok(()) }))
}
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> { fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
Ok(self Ok(self
.mailboxes .mailboxes
@ -185,6 +189,10 @@ impl MailBackend for MaildirType {
.map(|(h, f)| (*h, BackendMailbox::clone(f))) .map(|(h, f)| (*h, BackendMailbox::clone(f)))
.collect()) .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>>> { fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, mailbox) self.multicore(4, mailbox)
@ -670,18 +678,26 @@ impl MailBackend for MaildirType {
))) )))
} }
fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> { fn save(
MaildirType::save_to_mailbox(self.mailboxes[&mailbox_hash].fs_path.clone(), bytes, flags) &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 self
} }
fn create_mailbox( fn create_mailbox(
&mut self, &mut self,
new_path: String, new_path: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> { ) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(&new_path); path.push(&new_path);
if !path.starts_with(&self.path) { if !path.starts_with(&self.path) {
@ -720,21 +736,30 @@ impl MailBackend for MaildirType {
}; };
self.mailboxes.insert(mailbox_hash, new_mailbox); 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( fn delete_mailbox(
&mut self, &mut self,
_mailbox_hash: MailboxHash, _mailbox_hash: MailboxHash,
) -> Result<HashMap<MailboxHash, Mailbox>> { ) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented.")) 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.")) 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.")) Err(MeliError::new("Unimplemented."))
} }
@ -742,7 +767,7 @@ impl MailBackend for MaildirType {
&mut self, &mut self,
_mailbox_hash: MailboxHash, _mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions, _val: crate::backends::MailboxPermissions,
) -> Result<()> { ) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented.")) Err(MeliError::new("Unimplemented."))
} }
} }
@ -1084,7 +1109,7 @@ impl MaildirType {
w.build(handle) 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"] { for d in &["cur", "new", "tmp"] {
path.push(d); path.push(d);
if !path.is_dir() { if !path.is_dir() {
@ -1149,7 +1174,7 @@ impl MaildirType {
file.set_permissions(permissions)?; file.set_permissions(permissions)?;
let mut writer = io::BufWriter::new(file); let mut writer = io::BufWriter::new(file);
writer.write_all(bytes).unwrap(); writer.write_all(&bytes).unwrap();
return Ok(()); 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.")) 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 let path = self
.save_messages_to .save_messages_to
.as_ref() .as_ref()
.unwrap_or(&self.path) .unwrap_or(&self.path)
.to_path_buf(); .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 { 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 set_coordinates(&mut self, _: (usize, MailboxHash));
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context); 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 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); fn set_movement(&mut self, mvm: PageMovement);
} }

View File

@ -22,6 +22,7 @@
use super::EntryStrings; use super::EntryStrings;
use super::*; use super::*;
use crate::components::utilities::PageMovement; use crate::components::utilities::PageMovement;
use crate::jobs1::{oneshot, JobId};
use std::cmp; use std::cmp;
use std::convert::TryInto; use std::convert::TryInto;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -61,6 +62,11 @@ pub struct CompactListing {
rows_drawn: SegmentTree, rows_drawn: SegmentTree,
rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>,
search_job: Option<(
String,
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
JobId,
)>,
filter_term: String, filter_term: String,
filtered_selection: Vec<ThreadHash>, filtered_selection: Vec<ThreadHash>,
filtered_order: HashMap<ThreadHash, usize>, filtered_order: HashMap<ThreadHash, usize>,
@ -724,21 +730,22 @@ impl ListingTrait for CompactListing {
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn filter(&mut self, filter_term: &str, context: &Context) { fn filter(
if filter_term.is_empty() { &mut self,
return; filter_term: String,
} results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
self.order.clear(); self.order.clear();
self.selection.clear(); self.selection.clear();
self.length = 0; self.length = 0;
self.filtered_selection.clear(); self.filtered_selection.clear();
self.filtered_order.clear(); self.filtered_order.clear();
self.filter_term = filter_term.to_string(); self.filter_term = filter_term;
self.row_updates.clear(); self.row_updates.clear();
let account = &context.accounts[self.cursor_pos.0]; let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { match results {
Ok(results) => { Ok(results) => {
let threads = &account.collection.threads[&self.cursor_pos.1]; let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results { for env_hash in results {
@ -841,6 +848,7 @@ impl CompactListing {
subsort: (SortField::Date, SortOrder::Desc), subsort: (SortField::Date, SortOrder::Desc),
all_threads: HashSet::default(), all_threads: HashSet::default(),
order: HashMap::default(), order: HashMap::default(),
search_job: None,
filter_term: String::new(), filter_term: String::new(),
filtered_selection: Vec::new(), filtered_selection: Vec::new(),
filtered_order: HashMap::default(), filtered_order: HashMap::default(),
@ -1520,8 +1528,41 @@ impl Component for CompactListing {
return true; return true;
} }
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => { UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
self.filter(filter_term, context); match context.accounts[self.cursor_pos.0].search(
self.dirty = true; 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); context.dirty_areas.push_back(area);
} }
fn filter(&mut self, filter_term: &str, context: &Context) { fn filter(
if filter_term.is_empty() { &mut self,
return; filter_term: String,
} results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
/*
if filter_term.is_empty() {
return;
}
self.order.clear(); self.order.clear();
self.selection.clear(); self.selection.clear();
self.length = 0; self.length = 0;
self.filtered_selection.clear(); self.filtered_selection.clear();
self.filtered_order.clear(); self.filtered_order.clear();
self.filter_term = filter_term.to_string(); self.filter_term = filter_term.to_string();
self.row_updates.clear(); self.row_updates.clear();
for v in self.selection.values_mut() { for v in self.selection.values_mut() {
*v = false; *v = false;
} }
let account = &context.accounts[self.cursor_pos.0]; let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => { Ok(results) => {
let threads = &account.collection.threads[&self.cursor_pos.1]; /*
for env_hash in results { let threads = &account.collection.threads[&self.cursor_pos.1];
if !account.collection.contains_key(&env_hash) { for env_hash in results {
continue; 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 !self.filtered_selection.is_empty() {
if !threads.thread_nodes.contains_key(&env_thread_node_hash) { threads.group_inner_sort_by(
continue; &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 = self.redraw_threads_list(
threads.find_group(threads.thread_nodes[&env_thread_node_hash].group); context,
if self.filtered_order.contains_key(&thread) { Box::new(self.filtered_selection.clone().into_iter())
continue; as Box<dyn Iterator<Item = ThreadHash>>,
} );
if self.all_threads.contains(&thread) { */
self.filtered_selection.push(thread); }
self.filtered_order Err(e) => {
.insert(thread, self.filtered_selection.len() - 1); self.cursor_pos.2 = 0;
} self.new_cursor_pos.2 = 0;
} let message = format!(
if !self.filtered_selection.is_empty() { "Encountered an error while searching for `{}`: {}.",
threads.group_inner_sort_by( self.filter_term, e
&mut self.filtered_selection, );
self.sort, log(
&context.accounts[self.cursor_pos.0].collection.envelopes, 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 default_cell = {
let mut ret = Cell::with_char(' '); let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg) 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); .set_attrs(self.color_cache.theme_default.attrs);
ret 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) { fn set_movement(&mut self, mvm: PageMovement) {
@ -1336,7 +1345,7 @@ impl Component for ConversationsListing {
} }
UIEvent::Action(ref action) => match action { UIEvent::Action(ref action) => match action {
Action::Listing(Search(ref filter_term)) if !self.unfocused => { Action::Listing(Search(ref filter_term)) if !self.unfocused => {
self.filter(filter_term, context); //self.filter(filter_term, context);
self.dirty = true; self.dirty = true;
return true; return true;
} }

View File

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

View File

@ -581,42 +581,77 @@ impl ListingTrait for PlainListing {
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn filter(&mut self, filter_term: &str, context: &Context) { fn filter(
if filter_term.is_empty() { &mut self,
return; filter_term: String,
} results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &Context,
) {
/*
if filter_term.is_empty() {
return;
}
self.order.clear(); self.order.clear();
self.selection.clear(); self.selection.clear();
self.length = 0; self.length = 0;
self.filtered_selection.clear(); self.filtered_selection.clear();
self.filtered_order.clear(); self.filtered_order.clear();
self.filter_term = filter_term.to_string(); self.filter_term = filter_term.to_string();
self.row_updates.clear(); self.row_updates.clear();
for v in self.selection.values_mut() { for v in self.selection.values_mut() {
*v = false; *v = false;
} }
let account = &context.accounts[self.cursor_pos.0]; let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => { Ok(results) => {
for env_hash in results { /*
if !account.collection.contains_key(&env_hash) { for env_hash in results {
continue; 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) { if !self.filtered_selection.is_empty() {
continue; self.new_cursor_pos.2 =
} std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
if self.all_envelopes.contains(&env_hash) { } else {
self.filtered_selection.push(env_hash); let default_cell = {
self.filtered_order let mut ret = Cell::with_char(' ');
.insert(env_hash, self.filtered_selection.len() - 1); 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() { Err(e) => {
self.new_cursor_pos.2 = self.cursor_pos.2 = 0;
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); self.new_cursor_pos.2 = 0;
} else { 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 default_cell = {
let mut ret = Cell::with_char(' '); let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg) ret.set_fg(self.color_cache.theme_default.fg)
@ -625,45 +660,19 @@ impl ListingTrait for PlainListing {
ret ret
}; };
self.data_columns.columns[0] = 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) { fn set_movement(&mut self, mvm: PageMovement) {
@ -1267,7 +1276,7 @@ impl Component for PlainListing {
return true; return true;
} }
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => { UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
self.filter(filter_term, context); //self.filter(filter_term, context);
self.dirty = true; self.dirty = true;
} }
_ => {} _ => {}

View File

@ -30,6 +30,7 @@ pub use self::widgets::*;
mod layouts; mod layouts;
pub use self::layouts::*; pub use self::layouts::*;
use crate::jobs1::JobId;
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -636,6 +637,8 @@ pub struct StatusBar {
height: usize, height: usize,
dirty: bool, dirty: bool,
id: ComponentId, id: ComponentId,
progress_spinner: ProgressSpinner,
in_progress_jobs: HashSet<JobId>,
auto_complete: AutoComplete, auto_complete: AutoComplete,
cmd_history: Vec<String>, cmd_history: Vec<String>,
@ -661,7 +664,8 @@ impl StatusBar {
height: 1, height: 1,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
auto_complete: AutoComplete::new(Vec::new()), auto_complete: AutoComplete::new(Vec::new()),
progress_spinner: ProgressSpinner::new(3),
in_progress_jobs: HashSet::default(),
cmd_history: crate::execute::history::old_cmd_history(), cmd_history: crate::execute::history::old_cmd_history(),
} }
} }
@ -755,6 +759,17 @@ impl Component for StatusBar {
context, 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() { if self.mode != UIMode::Execute && !self.is_dirty() {
return; return;
} }
@ -1148,12 +1163,30 @@ impl Component for StatusBar {
self.status = format!("{} | {}", self.mode, std::mem::replace(s, String::new())); self.status = format!("{} | {}", self.mode, std::mem::replace(s, String::new()));
self.dirty = true; 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 false
} }
fn is_dirty(&self) -> bool { 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) { fn set_dirty(&mut self, value: bool) {
self.dirty = value; self.dirty = value;

View File

@ -992,6 +992,8 @@ impl ScrollBar {
#[derive(Debug)] #[derive(Debug)]
pub struct ProgressSpinner { pub struct ProgressSpinner {
//total_work: usize,
//finished: usize,
timer: crate::timer::PosixTimer, timer: crate::timer::PosixTimer,
stage: usize, stage: usize,
kind: 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 { pub fn new(kind: usize) -> Self {
let timer = crate::timer::PosixTimer::new_with_signal( let timer = crate::timer::PosixTimer::new_with_signal(
std::time::Duration::from_millis(50), std::time::Duration::from_millis(0),
std::time::Duration::from_millis(500), std::time::Duration::from_millis(0),
nix::sys::signal::Signal::SIGALRM, nix::sys::signal::Signal::SIGALRM,
) )
.unwrap(); .unwrap();
@ -1033,6 +1038,20 @@ impl ProgressSpinner {
id: ComponentId::new_v4(), 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 { 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::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use melib::backends::{ use melib::backends::{
AccountHash, BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp, AccountHash, BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp,
RefreshEvent, RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox, RefreshEvent, RefreshEventConsumer, RefreshEventKind, ResultFuture, SpecialUsageMailbox,
}; };
use melib::email::*; use melib::email::*;
use melib::error::{MeliError, Result}; use melib::error::{MeliError, Result};
@ -43,6 +43,7 @@ use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate
use crate::{StatusEvent, ThreadEvent}; use crate::{StatusEvent, ThreadEvent};
use crossbeam::Sender; use crossbeam::Sender;
use futures::channel::oneshot; use futures::channel::oneshot;
use futures::future::FutureExt;
pub use futures::stream::Stream; pub use futures::stream::Stream;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -131,13 +132,13 @@ pub struct Account {
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>, pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
pub job_executor: Arc<JobExecutor>, pub job_executor: Arc<JobExecutor>,
active_jobs: HashMap<JobId, JobRequest>, pub active_jobs: HashMap<JobId, JobRequest>,
sender: Sender<ThreadEvent>, sender: Sender<ThreadEvent>,
event_queue: VecDeque<(MailboxHash, RefreshEvent)>, event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
notify_fn: Arc<NotifyFn>, notify_fn: Arc<NotifyFn>,
} }
enum JobRequest { pub enum JobRequest {
Mailboxes(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>), Mailboxes(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>),
Get( Get(
MailboxHash, MailboxHash,
@ -154,11 +155,7 @@ enum JobRequest {
CreateMailbox(oneshot::Receiver<Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)>>), CreateMailbox(oneshot::Receiver<Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)>>),
DeleteMailbox(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>), DeleteMailbox(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>),
//RenameMailbox, //RenameMailbox,
Search( Search,
crate::search::Query,
Option<MailboxHash>,
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
),
SetMailboxPermissions(MailboxHash, oneshot::Receiver<Result<()>>), SetMailboxPermissions(MailboxHash, oneshot::Receiver<Result<()>>),
SetMailboxSubscription(MailboxHash, oneshot::Receiver<Result<()>>), SetMailboxSubscription(MailboxHash, oneshot::Receiver<Result<()>>),
Watch(JoinHandle), Watch(JoinHandle),
@ -177,7 +174,7 @@ impl core::fmt::Debug for JobRequest {
JobRequest::CreateMailbox(_) => write!(f, "{}", "JobRequest::CreateMailbox"), JobRequest::CreateMailbox(_) => write!(f, "{}", "JobRequest::CreateMailbox"),
JobRequest::DeleteMailbox(_) => write!(f, "{}", "JobRequest::DeleteMailbox"), JobRequest::DeleteMailbox(_) => write!(f, "{}", "JobRequest::DeleteMailbox"),
//JobRequest::RenameMailbox, //JobRequest::RenameMailbox,
JobRequest::Search(_, _, _) => write!(f, "{}", "JobRequest::Search"), JobRequest::Search => write!(f, "{}", "JobRequest::Search"),
JobRequest::SetMailboxPermissions(_, _) => { JobRequest::SetMailboxPermissions(_, _) => {
write!(f, "{}", "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 { impl Drop for Account {
fn drop(&mut self) { fn drop(&mut self) {
if let Ok(data_dir) = xdg::BaseDirectories::with_profile("meli", &self.name) { if let Ok(data_dir) = xdg::BaseDirectories::with_profile("meli", &self.name) {
@ -294,14 +307,17 @@ impl Account {
let mut active_jobs = HashMap::default(); let mut active_jobs = HashMap::default();
if settings.conf.is_async { 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() { if let Ok(online_job) = backend.is_online_async() {
let (rcvr, job_id) = job_executor.spawn_specialized(online_job); let (rcvr, job_id) = job_executor.spawn_specialized(online_job);
active_jobs.insert(job_id, JobRequest::IsOnline(rcvr)); 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 { Ok(Account {
@ -460,6 +476,11 @@ impl Account {
if let Ok(mailbox_job) = self.backend.write().unwrap().get_async(&f) { if let Ok(mailbox_job) = self.backend.write().unwrap().get_async(&f) {
let mailbox_job = mailbox_job.into_future(); let mailbox_job = mailbox_job.into_future();
let (rcvr, job_id) = self.job_executor.spawn_specialized(mailbox_job); 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)); self.active_jobs.insert(job_id, JobRequest::Get(*h, rcvr));
} }
} else { } else {
@ -790,6 +811,11 @@ impl Account {
if self.settings.conf.is_async { if self.settings.conf.is_async {
if let Ok(refresh_job) = self.backend.write().unwrap().refresh_async(mailbox_hash, r) { 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); 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 self.active_jobs
.insert(job_id, JobRequest::Refresh(mailbox_hash, rcvr)); .insert(job_id, JobRequest::Refresh(mailbox_hash, rcvr));
} }
@ -882,18 +908,25 @@ impl Account {
} }
MailboxStatus::None => { MailboxStatus::None => {
if self.settings.conf.is_async { if self.settings.conf.is_async {
if let Ok(mailbox_job) = if !self.active_jobs.values().any(|j| j.is_get(mailbox_hash)) {
self.backend.write().unwrap().get_async( if let Ok(mailbox_job) =
&&self.mailbox_entries[&mailbox_hash].ref_mailbox, self.backend.write().unwrap().get_async(
) &&self.mailbox_entries[&mailbox_hash].ref_mailbox,
{ )
let mailbox_job = mailbox_job.into_future(); {
let (rcvr, job_id) = let mailbox_job = mailbox_job.into_future();
self.job_executor.spawn_specialized(mailbox_job); let (rcvr, job_id) =
self.active_jobs self.job_executor.spawn_specialized(mailbox_job);
.insert(job_id, JobRequest::Get(mailbox_hash, rcvr)); 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( let handle = Account::new_worker(
self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(), self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(),
&mut self.backend, &mut self.backend,
@ -991,7 +1024,7 @@ impl Account {
} }
pub fn save_special( pub fn save_special(
&self, &mut self,
bytes: &[u8], bytes: &[u8],
mailbox_type: SpecialUsageMailbox, mailbox_type: SpecialUsageMailbox,
flags: Flag, 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() { if self.settings.account.read_only() {
return Err(MeliError::new(format!( return Err(MeliError::new(format!(
"Account {} is read-only.", "Account {} is read-only.",
self.name.as_str() self.name.as_str()
))); )));
} }
self.backend let job = self
.backend
.write() .write()
.unwrap() .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() { if self.settings.account.read_only() {
return Err(MeliError::new(format!( return Err(MeliError::new(format!(
"Account {} is read-only.", "Account {} is read-only.",
self.name.as_str() 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 { pub fn contains_key(&self, h: EnvelopeHash) -> bool {
@ -1091,11 +1152,12 @@ impl Account {
} }
match op { match op {
MailboxOperation::Create(path) => { MailboxOperation::Create(path) => {
let (mailbox_hash, mut mailboxes) = self let (mailbox_hash, mut mailboxes) = futures::executor::block_on(
.backend self.backend
.write() .write()
.unwrap() .unwrap()
.create_mailbox(path.to_string())?; .create_mailbox(path.to_string())?,
)?;
self.sender self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate(( .send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
self.index, self.index,
@ -1159,7 +1221,9 @@ impl Account {
return Err(MeliError::new("Cannot delete only mailbox.")); return Err(MeliError::new("Cannot delete only mailbox."));
} }
let mailbox_hash = self.mailbox_by_path(&path)?; 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 self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete(( .send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
self.index, self.index,
@ -1254,13 +1318,7 @@ impl Account {
if self.is_online { if self.is_online {
return Ok(()); return Ok(());
} }
if !self.active_jobs.values().any(|j| { if !self.active_jobs.values().any(JobRequest::is_online) {
if let JobRequest::IsOnline(_) = j {
true
} else {
false
}
}) {
if let Ok(online_job) = self.backend.read().unwrap().is_online_async() { if let Ok(online_job) = self.backend.read().unwrap().is_online_async() {
let (rcvr, job_id) = self.job_executor.spawn_specialized(online_job); let (rcvr, job_id) = self.job_executor.spawn_specialized(online_job);
self.active_jobs.insert(job_id, JobRequest::IsOnline(rcvr)); self.active_jobs.insert(job_id, JobRequest::IsOnline(rcvr));
@ -1270,7 +1328,7 @@ impl Account {
} else { } else {
let ret = self.backend.read().unwrap().is_online(); let ret = self.backend.read().unwrap().is_online();
if ret.is_ok() != self.is_online && ret.is_ok() { if ret.is_ok() != self.is_online && ret.is_ok() {
//self.init()?; self.init(None)?;
} }
self.is_online = ret.is_ok(); self.is_online = ret.is_ok();
if !self.is_online { if !self.is_online {
@ -1285,33 +1343,14 @@ impl Account {
search_term: &str, search_term: &str,
sort: (SortField, SortOrder), sort: (SortField, SortOrder),
mailbox_hash: MailboxHash, mailbox_hash: MailboxHash,
) -> Result<SmallVec<[EnvelopeHash; 512]>> { ) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
if self.settings.account().format() == "imap" { use melib::parsec::Parser;
use melib::parsec::Parser; let query = melib::search::query().parse(search_term)?.1;
let query = melib::search::query().parse(search_term)?.1; self.backend
return self .read()
.backend .unwrap()
.read() .search(query, Some(mailbox_hash))
.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",
))
};
}
}
#[cfg(feature = "sqlite3")] #[cfg(feature = "sqlite3")]
{ {
crate::sqlite3::search(search_term, sort) crate::sqlite3::search(search_term, sort)
@ -1346,6 +1385,7 @@ impl Account {
} }
Ok(ret) Ok(ret)
} }
*/
} }
pub fn mailbox_by_path(&self, path: &str) -> Result<MailboxHash> { pub fn mailbox_by_path(&self, path: &str) -> Result<MailboxHash> {
@ -1379,6 +1419,12 @@ impl Account {
} }
} }
JobRequest::Get(mailbox_hash, mut chan) => { 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>>>, _) = let (payload, rest): (Option<Result<Vec<Envelope>>>, _) =
chan.try_recv().unwrap().unwrap(); chan.try_recv().unwrap().unwrap();
debug!("got payload in status for {}", mailbox_hash); debug!("got payload in status for {}", mailbox_hash);
@ -1398,8 +1444,12 @@ impl Account {
.unwrap(); .unwrap();
return true; return true;
} }
let (rcvr, job_id) = self.job_executor.spawn_specialized(rest.into_future()); 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 self.active_jobs
.insert(job_id, JobRequest::Get(mailbox_hash, rcvr)); .insert(job_id, JobRequest::Get(mailbox_hash, rcvr));
let payload = payload.unwrap(); let payload = payload.unwrap();
@ -1465,18 +1515,11 @@ impl Account {
if r.is_some() && r.unwrap().is_ok() { if r.is_some() && r.unwrap().is_ok() {
self.is_online = true; self.is_online = true;
} }
} self.sender
JobRequest::Refresh(_, mut chan) => { .send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
let r = chan.try_recv().unwrap(); StatusEvent::JobFinished(job_id.clone()),
if let Some(Err(err)) = r { )))
self.sender .unwrap();
.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");
}
} }
JobRequest::SetFlags(_, mut chan) => { JobRequest::SetFlags(_, mut chan) => {
let r = chan.try_recv().unwrap(); let r = chan.try_recv().unwrap();
@ -1558,21 +1601,12 @@ impl Account {
} }
} }
//JobRequest::RenameMailbox, //JobRequest::RenameMailbox,
JobRequest::Search(_, _, mut chan) => { JobRequest::Search => {
let r = chan.try_recv().unwrap(); self.sender
match r { .send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
Some(Err(err)) => { StatusEvent::JobFinished(job_id.clone()),
self.sender )))
.send(ThreadEvent::UIEvent(UIEvent::Notification( .unwrap();
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::SetMailboxPermissions(_, mut chan) => { JobRequest::SetMailboxPermissions(_, mut chan) => {
let r = chan.try_recv().unwrap(); 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.")) Err(MeliError::new("Unimplemented."))
} }
fn create_mailbox( fn create_mailbox(
&mut self, &mut self,
_name: String, _name: String,
) -> Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)> { ) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
Err(MeliError::new("Unimplemented.")) Err(MeliError::new("Unimplemented."))
} }
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> { fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {

View File

@ -53,6 +53,8 @@ pub enum StatusEvent {
BufClear, BufClear,
BufSet(String), BufSet(String),
UpdateStatus(String), UpdateStatus(String),
NewJob(JobId),
JobFinished(JobId),
} }
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads /// `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 /// 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.value = value;
self
} }
/// Sets interval without arming timer /// 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.interval = interval;
self
} }
pub fn new_with_signal( pub fn new_with_signal(