MailBackend: change more methods to Futures
parent
03522c0298
commit
e06308fed2
|
@ -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."))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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,19 +743,25 @@ 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();
|
||||
|
||||
Ok(Box::pin(async move {
|
||||
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)?;
|
||||
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()?;
|
||||
let uid_index = uid_store.uid_index.lock()?;
|
||||
return Ok(SmallVec::from_iter(
|
||||
l["* SEARCH".len()..]
|
||||
.trim()
|
||||
|
@ -768,7 +774,7 @@ impl MailBackend for ImapType {
|
|||
}
|
||||
}
|
||||
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(
|
||||
|
|
|
@ -508,7 +508,6 @@ impl ImapConnection {
|
|||
if required_responses.check(l) || !self.process_untagged(l).await? {
|
||||
ret.push_str(l);
|
||||
}
|
||||
ret.push_str(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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(());
|
||||
}
|
||||
|
||||
|
|
|
@ -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."))
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -687,7 +687,13 @@ impl ListingTrait for ConversationsListing {
|
|||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
||||
fn filter(&mut self, filter_term: &str, context: &Context) {
|
||||
fn filter(
|
||||
&mut self,
|
||||
filter_term: String,
|
||||
results: Result<SmallVec<[EnvelopeHash; 512]>>,
|
||||
context: &Context,
|
||||
) {
|
||||
/*
|
||||
if filter_term.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -706,6 +712,7 @@ impl ListingTrait for ConversationsListing {
|
|||
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) {
|
||||
|
@ -749,6 +756,7 @@ impl ListingTrait for ConversationsListing {
|
|||
Box::new(self.filtered_selection.clone().into_iter())
|
||||
as Box<dyn Iterator<Item = ThreadHash>>,
|
||||
);
|
||||
*/
|
||||
}
|
||||
Err(e) => {
|
||||
self.cursor_pos.2 = 0;
|
||||
|
@ -781,6 +789,7 @@ impl ListingTrait for ConversationsListing {
|
|||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -581,7 +581,13 @@ impl ListingTrait for PlainListing {
|
|||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
||||
fn filter(&mut self, filter_term: &str, context: &Context) {
|
||||
fn filter(
|
||||
&mut self,
|
||||
filter_term: String,
|
||||
results: Result<SmallVec<[EnvelopeHash; 512]>>,
|
||||
context: &Context,
|
||||
) {
|
||||
/*
|
||||
if filter_term.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -600,6 +606,7 @@ impl ListingTrait for PlainListing {
|
|||
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;
|
||||
|
@ -632,6 +639,7 @@ impl ListingTrait for PlainListing {
|
|||
Box::new(self.filtered_selection.clone().into_iter())
|
||||
as Box<dyn Iterator<Item = EnvelopeHash>>,
|
||||
);
|
||||
*/
|
||||
}
|
||||
Err(e) => {
|
||||
self.cursor_pos.2 = 0;
|
||||
|
@ -664,6 +672,7 @@ impl ListingTrait for PlainListing {
|
|||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,6 +908,7 @@ impl Account {
|
|||
}
|
||||
MailboxStatus::None => {
|
||||
if self.settings.conf.is_async {
|
||||
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,
|
||||
|
@ -890,10 +917,16 @@ impl Account {
|
|||
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
|
||||
let (mailbox_hash, mut mailboxes) = futures::executor::block_on(
|
||||
self.backend
|
||||
.write()
|
||||
.unwrap()
|
||||
.create_mailbox(path.to_string())?;
|
||||
.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" {
|
||||
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
|
||||
use melib::parsec::Parser;
|
||||
let query = melib::search::query().parse(search_term)?.1;
|
||||
return self
|
||||
.backend
|
||||
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",
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
.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),
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||
StatusEvent::JobFinished(job_id.clone()),
|
||||
)))
|
||||
.expect("Could not send event on main channel");
|
||||
}
|
||||
.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)) => {
|
||||
JobRequest::Search => {
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::Notification(
|
||||
Some(format!("{}: could not perform search", &self.name)),
|
||||
err.to_string(),
|
||||
Some(crate::types::NotificationType::ERROR),
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||
StatusEvent::JobFinished(job_id.clone()),
|
||||
)))
|
||||
.expect("Could not send event on main channel");
|
||||
}
|
||||
Some(Ok(v)) => unimplemented!(),
|
||||
None => {}
|
||||
}
|
||||
.unwrap();
|
||||
}
|
||||
JobRequest::SetMailboxPermissions(_, mut chan) => {
|
||||
let r = chan.try_recv().unwrap();
|
||||
|
|
|
@ -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>>>> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue