From f6533d51c715985326ba5018dd00d06f267ae552 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 17 Sep 2018 07:53:16 +0300 Subject: [PATCH] melib: add async threading closes #43 --- melib/src/mailbox/backends/maildir/backend.rs | 28 +- melib/src/mailbox/backends/maildir/mod.rs | 4 + melib/src/mailbox/backends/mod.rs | 7 +- melib/src/mailbox/collection.rs | 39 ++- melib/src/mailbox/mod.rs | 10 +- melib/src/mailbox/thread.rs | 256 ++++++++++-------- ui/src/types/accounts.rs | 52 +++- 7 files changed, 249 insertions(+), 147 deletions(-) diff --git a/melib/src/mailbox/backends/maildir/backend.rs b/melib/src/mailbox/backends/maildir/backend.rs index c589d99f8..1e30471b7 100644 --- a/melib/src/mailbox/backends/maildir/backend.rs +++ b/melib/src/mailbox/backends/maildir/backend.rs @@ -50,7 +50,6 @@ use std::fs; use std::hash::{Hash, Hasher}; use std::io; use std::io::Read; -use std::os::unix::ffi::OsStrExt; use std::path::{Component, Path, PathBuf}; use std::result; use std::sync::{Arc, Mutex}; @@ -98,12 +97,11 @@ macro_rules! get_path_hash { } fn get_file_hash(file: &Path) -> EnvelopeHash { - let mut buf = Vec::new(); + let mut buf = Vec::with_capacity(2048); let mut f = fs::File::open(&file).unwrap_or_else(|_| panic!("Can't open {}", file.display())); f.read_to_end(&mut buf) .unwrap_or_else(|_| panic!("Can't read {}", file.display())); let mut hasher = FnvHasher::default(); - hasher.write(file.as_os_str().as_bytes()); hasher.write(&buf); hasher.finish() } @@ -412,17 +410,20 @@ impl MaildirType { let map = map.clone(); let len = c.len(); for file in c { + //thread::yield_now(); + /* Check if we have a cache file with this email's * filename */ let file_name = PathBuf::from(file) .strip_prefix(&root_path) .unwrap() .to_path_buf(); - if let Some(cached) = + let hash = if let Some(cached) = cache_dir.find_cache_file(&file_name) { + /* Cached struct exists, try to load it */ let reader = io::BufReader::new( - fs::File::open(cached).unwrap(), + fs::File::open(&cached).unwrap(), ); let result: result::Result = bincode::deserialize_from(reader); if let Ok(env) = result { @@ -431,13 +432,26 @@ impl MaildirType { if (*map).contains_key(&hash) { continue; } + (*map).insert(hash, (0, file.clone())); local_r.push(env); continue; } - } + + let mut reader = io::BufReader::new( + fs::File::open(cached).unwrap(), + ); + let mut buf = Vec::with_capacity(2048); + reader.read_to_end(&mut buf).unwrap_or_else(|_| { + panic!("Can't read {}", file.display()) + }); + let mut hasher = FnvHasher::default(); + hasher.write(&buf); + hasher.finish() + } else { + get_file_hash(file) + }; { - let hash = get_file_hash(file); { let mut map = map.lock().unwrap(); if (*map).contains_key(&hash) { diff --git a/melib/src/mailbox/backends/maildir/mod.rs b/melib/src/mailbox/backends/maildir/mod.rs index 08daea782..402721e92 100644 --- a/melib/src/mailbox/backends/maildir/mod.rs +++ b/melib/src/mailbox/backends/maildir/mod.rs @@ -205,15 +205,19 @@ impl BackendFolder for MaildirFolder { fn hash(&self) -> FolderHash { self.hash } + fn name(&self) -> &str { &self.name } + fn change_name(&mut self, s: &str) { self.name = s.to_string(); } + fn children(&self) -> &Vec { &self.children } + fn clone(&self) -> Folder { Box::new(MaildirFolder { hash: self.hash, diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index e2fda5774..f86511e05 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -242,17 +242,22 @@ impl BackendFolder for DummyFolder { fn hash(&self) -> FolderHash { 0 } + fn name(&self) -> &str { "" } + fn change_name(&mut self, _s: &str) {} + fn clone(&self) -> Folder { folder_default() } + fn children(&self) -> &Vec { &self.v } } + pub fn folder_default() -> Folder { Box::new(DummyFolder { v: Vec::with_capacity(0), @@ -260,4 +265,4 @@ pub fn folder_default() -> Folder { } pub type FolderHash = u64; -pub type Folder = Box; +pub type Folder = Box; diff --git a/melib/src/mailbox/collection.rs b/melib/src/mailbox/collection.rs index 9587fc9e2..f01fa23c6 100644 --- a/melib/src/mailbox/collection.rs +++ b/melib/src/mailbox/collection.rs @@ -21,7 +21,7 @@ pub struct Collection { } impl Collection { - pub fn new(vec: Vec, name: &str) -> Collection { + pub fn new(vec: Vec, folder: &Folder) -> Collection { let mut envelopes: FnvHashMap = FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default()); for e in vec { @@ -30,7 +30,9 @@ impl Collection { let date_index = BTreeMap::new(); let subject_index = None; - let cache_dir = xdg::BaseDirectories::with_profile("meli", name).unwrap(); + let cache_dir = + xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", folder.hash())) + .unwrap(); let threads = if let Some(cached) = cache_dir.find_cache_file("threads") { let reader = io::BufReader::new(fs::File::open(cached).unwrap()); let result: result::Result = bincode::deserialize_from(reader); @@ -38,10 +40,34 @@ impl Collection { cached_t.update(&mut envelopes); cached_t } else { - Threads::new(&mut envelopes) // sent_folder); + let ret = Threads::new(&mut envelopes); // sent_folder); + if let Ok(cached) = cache_dir.place_cache_file("threads") { + /* place result in cache directory */ + let f = match fs::File::create(cached) { + Ok(f) => f, + Err(e) => { + panic!("{}", e); + } + }; + let writer = io::BufWriter::new(f); + bincode::serialize_into(writer, &ret).unwrap(); + } + ret } } else { - Threads::new(&mut envelopes) // sent_folder); + let ret = Threads::new(&mut envelopes); // sent_folder); + if let Ok(cached) = cache_dir.place_cache_file("threads") { + /* place result in cache directory */ + let f = match fs::File::create(cached) { + Ok(f) => f, + Err(e) => { + panic!("{}", e); + } + }; + let writer = io::BufWriter::new(f); + bincode::serialize_into(writer, &ret).unwrap(); + } + ret }; Collection { envelopes, @@ -59,8 +85,9 @@ impl Collection { self.envelopes.is_empty() } - pub fn insert(&mut self, hash: EnvelopeHash, mut envelope: Envelope) { - self.threads.insert(&mut envelope); + pub fn insert(&mut self, mut envelope: Envelope) { + self.threads.insert(&mut envelope, &self.envelopes); + let hash = envelope.hash(); self.envelopes.insert(hash, envelope); } pub(crate) fn insert_reply(&mut self, envelope: Envelope) { diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index b9965d696..2a57208e3 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -67,12 +67,12 @@ impl Default for Mailbox { } impl Mailbox { - pub fn new(folder: &Folder, envelopes: Result>) -> Result { + pub fn new(folder: Folder, envelopes: Result>) -> Result { let mut envelopes: Vec = envelopes?; envelopes.sort_by(|a, b| a.date().cmp(&b.date())); - let collection = Collection::new(envelopes, folder.name()); + let collection = Collection::new(envelopes, &folder); Ok(Mailbox { - folder: (*folder).clone(), + folder, collection, ..Default::default() }) @@ -118,12 +118,12 @@ impl Mailbox { pub fn update(&mut self, old_hash: EnvelopeHash, envelope: Envelope) { self.collection.remove(&old_hash); - self.collection.insert(envelope.hash(), envelope); + self.collection.insert(envelope); } pub fn insert(&mut self, envelope: Envelope) -> &Envelope { let hash = envelope.hash(); - self.collection.insert(hash, envelope); + self.collection.insert(envelope); &self.collection[&hash] } diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index 4c2b8267d..dfb461f34 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -32,11 +32,13 @@ * user having mutable ownership. */ +use mailbox::email::parser::BytesExt; use mailbox::email::*; extern crate fnv; use self::fnv::{FnvHashMap, FnvHashSet}; use std::cell::{Ref, RefCell}; +use std::cmp; use std::cmp::Ordering; use std::iter::FromIterator; use std::mem; @@ -89,21 +91,21 @@ macro_rules! make { } /* Strip common prefixes from subjects */ -trait SubjectPrefix { - fn strip_prefixes(&mut self) -> bool; +trait SubjectPrefix<'a> { + fn strip_prefixes(&'a mut self) -> bool; } -impl SubjectPrefix for String { - fn strip_prefixes(&mut self) -> bool { +impl<'a> SubjectPrefix<'a> for &'a [u8] { + fn strip_prefixes(&'a mut self) -> bool { let mut ret: bool = false; let result = { let mut slice = self.trim(); let mut end_prefix_idx = 0; loop { - if slice.starts_with("RE: ") - || slice.starts_with("Re: ") - || slice.starts_with("FW: ") - || slice.starts_with("Fw: ") + if slice.starts_with(b"RE: ") + || slice.starts_with(b"Re: ") + || slice.starts_with(b"FW: ") + || slice.starts_with(b"Fw: ") { if end_prefix_idx == 0 { ret = true; @@ -112,23 +114,23 @@ impl SubjectPrefix for String { end_prefix_idx += 3; continue; } - if slice.starts_with("FWD: ") - || slice.starts_with("Fwd: ") - || slice.starts_with("fwd: ") + if slice.starts_with(b"FWD: ") + || slice.starts_with(b"Fwd: ") + || slice.starts_with(b"fwd: ") { slice = &slice[4..]; end_prefix_idx += 4; continue; } - if slice.starts_with(' ') || slice.starts_with('\t') || slice.starts_with('\r') { + if slice.starts_with(b" ") || slice.starts_with(b"\t") || slice.starts_with(b"\r") { slice = &slice[1..]; end_prefix_idx += 1; continue; } - if slice.starts_with('[') - && !(slice.starts_with("[PATCH") || slice.starts_with("[RFC")) + if slice.starts_with(b"[") + && !(slice.starts_with(b"[PATCH") || slice.starts_with(b"[RFC")) { - if let Some(pos) = slice.find(']') { + if let Some(pos) = slice.find(b"]") { end_prefix_idx += pos; slice = &slice[pos..]; continue; @@ -139,22 +141,22 @@ impl SubjectPrefix for String { } break; } - slice.to_string() + slice }; - mem::replace(self, result); + *self = result; ret } } /* Sorting states. */ -#[derive(Debug, Clone, PartialEq, Copy, Deserialize)] +#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize)] pub enum SortOrder { Asc, Desc, } -#[derive(Debug, Clone, PartialEq, Copy, Deserialize)] +#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize)] pub enum SortField { Subject, Date, @@ -197,7 +199,7 @@ impl FromStr for SortOrder { /* * The thread tree holds the sorted state of the thread nodes */ -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] struct ThreadTree { id: usize, children: Vec, @@ -262,7 +264,7 @@ impl<'a> Iterator for ThreadIterator<'a> { } } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct ThreadNode { message: Option, parent: Option, @@ -339,13 +341,13 @@ impl ThreadNode { } } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Threads { thread_nodes: Vec, root_set: RefCell>, tree: RefCell>, - message_ids: FnvHashMap, + message_ids: FnvHashMap, usize>, hash_set: FnvHashSet, sort: RefCell<(SortField, SortOrder)>, subsort: RefCell<(SortField, SortOrder)>, @@ -503,7 +505,7 @@ impl Threads { let thread_nodes: Vec = Vec::with_capacity((collection.len() as f64 * 1.2) as usize); /* A hash table of Message IDs */ - let message_ids: FnvHashMap = + let message_ids: FnvHashMap, usize> = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); let hash_set: FnvHashSet = FnvHashSet::with_capacity_and_hasher(collection.len(), Default::default()); @@ -541,20 +543,17 @@ impl Threads { * messages which don't have References headers at all still get threaded (to the extent * possible, at least.)" */ - let mut subject_table: FnvHashMap = + let mut subject_table: FnvHashMap, (bool, usize)> = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); for r in &root_set { /* "Find the subject of that sub-tree": */ - let (mut subject, mut is_re): (String, bool) = if t.thread_nodes[*r].message.is_some() { + let (mut subject, mut is_re): (_, bool) = if t.thread_nodes[*r].message.is_some() { /* "If there is a message in the Container, the subject is the subject of that * message. " */ let msg_idx = t.thread_nodes[*r].message.unwrap(); let envelope = &collection[&msg_idx]; - ( - envelope.subject().to_string(), - !envelope.references().is_empty(), - ) + (envelope.subject(), !envelope.references().is_empty()) } else { /* "If there is no message in the Container, then the Container will have at least * one child Container, and that Container will have a message. Use the subject of @@ -563,24 +562,22 @@ impl Threads { .message .unwrap(); let envelope = &collection[&msg_idx]; - ( - envelope.subject().to_string(), - !envelope.references().is_empty(), - ) + (envelope.subject(), !envelope.references().is_empty()) }; /* "Strip ``Re:'', ``RE:'', ``RE[5]:'', ``Re: Re[4]: Re:'' and so on." */ /* References of this envelope can be empty but if the subject contains a ``Re:`` * prefix, it's a reply */ - is_re |= subject.strip_prefixes(); + let mut stripped_subj = subject.to_mut().as_bytes(); + is_re |= stripped_subj.strip_prefixes(); - if subject.is_empty() { + if stripped_subj.is_empty() { continue; } /* "Add this Container to the subject_table if:" */ - if subject_table.contains_key(&subject) { - let (other_is_re, id) = subject_table[&subject]; + if subject_table.contains_key(stripped_subj) { + let (other_is_re, id) = subject_table[stripped_subj]; /* "This one is an empty container and the old one is not: the empty one is more * interesting as a root, so put it in the table instead." * or @@ -590,11 +587,14 @@ impl Threads { if (!t.thread_nodes[id].has_message() && t.thread_nodes[*r].has_message()) || (other_is_re && !is_re) { - mem::replace(subject_table.entry(subject).or_default(), (is_re, *r)); + mem::replace( + subject_table.entry(stripped_subj.to_vec()).or_default(), + (is_re, *r), + ); } } else { /* "There is no container in the table with this subject" */ - subject_table.insert(subject, (is_re, *r)); + subject_table.insert(stripped_subj.to_vec(), (is_re, *r)); } } @@ -603,30 +603,25 @@ impl Threads { for i in 0..root_set.len() { let r = root_set[i]; /* "Find the subject of this Container (as above.)" */ - let (mut subject, mut is_re): (String, bool) = if t.thread_nodes[r].message.is_some() { + let (mut subject, mut is_re): (_, bool) = if t.thread_nodes[r].message.is_some() { let msg_idx = t.thread_nodes[r].message.unwrap(); let envelope = &collection[&msg_idx]; - ( - envelope.subject().to_string(), - !envelope.references().is_empty(), - ) + (envelope.subject(), !envelope.references().is_empty()) } else { let msg_idx = t.thread_nodes[t.thread_nodes[r].children[0]] .message .unwrap(); let envelope = &collection[&msg_idx]; - ( - envelope.subject().to_string(), - !envelope.references().is_empty(), - ) + (envelope.subject(), !envelope.references().is_empty()) }; + let mut subject = subject.to_mut().as_bytes(); is_re |= subject.strip_prefixes(); if subject.is_empty() { continue; } - let (other_is_re, other_idx) = subject_table[&subject]; + let (other_is_re, other_idx) = subject_table[subject]; /* "If it is null, or if it is this container, continue." */ if !t.thread_nodes[other_idx].has_message() || other_idx == r { continue; @@ -723,12 +718,25 @@ impl Threads { let difference: Vec = new_hash_set.difference(&self.hash_set).cloned().collect(); for h in difference { - self.insert(collection.entry(h).or_default()); + let env = collection.entry(h).or_default() as *mut Envelope; + unsafe { + // `collection` is borrowed immutably and `insert` only changes the envelope's + // `thread` field. + self.insert(&mut (*env), collection); + } } } - pub fn insert(&mut self, envelope: &mut Envelope) { + pub fn insert( + &mut self, + envelope: &mut Envelope, + collection: &FnvHashMap, + ) { self.link_envelope(envelope); + { + let id = self.message_ids[envelope.message_id().raw()]; + self.rebuild_thread(id, collection); + } } pub fn insert_reply( @@ -736,10 +744,10 @@ impl Threads { envelope: Envelope, collection: &mut FnvHashMap, ) -> bool { - return false; + //return false; { - let in_reply_to = envelope.in_reply_to_raw(); - if !self.message_ids.contains_key(in_reply_to.as_ref()) { + let in_reply_to = envelope.in_reply_to_bytes(); + if !self.message_ids.contains_key(in_reply_to) { return false; } } @@ -754,8 +762,8 @@ impl Threads { } let envelope: &Envelope = &collection[&hash]; { - let in_reply_to = envelope.in_reply_to_raw(); - let parent_id = self.message_ids[in_reply_to.as_ref()]; + let in_reply_to = envelope.in_reply_to_bytes(); + let parent_id = self.message_ids[in_reply_to]; self.rebuild_thread(parent_id, collection); } true @@ -781,11 +789,13 @@ impl Threads { * let tree = &mut tree[s].children; */ let temp_tree = tree; - let pos = temp_tree.iter().position(|v| v.id == s).unwrap(); - match temp_tree[pos].children { - ref mut v => { - tree = v; - } + if let Some(pos) = temp_tree.iter().position(|v| v.id == s) { + tree = &mut temp_tree[pos].children; + } else { + let tree_node = ThreadTree::new(s); + temp_tree.push(tree_node); + let new_id = temp_tree.len() - 1; + tree = &mut temp_tree[new_id].children; } } /* Add new child */ @@ -951,38 +961,40 @@ impl Threads { } fn link_envelope(&mut self, envelope: &mut Envelope) { - let m_id = envelope.message_id_raw().to_string(); + let t_idx: usize = { + let m_id = envelope.message_id().raw(); - /* t_idx: The index of this message's ThreadNode in thread_nodes - * - * If id_table contains an empty Container for this ID: - * Store this message in the Container's message slot. - * Else: - * Create a new Container object holding this message; - * Index the Container by Message-ID in id_table. - */ - let t_idx: usize = if self.message_ids.get(&m_id).is_some() { - let node_idx = self.message_ids[&m_id]; - /* the already existing ThreadNote should be empty, since we're - * seeing this message for the first time. otherwise it's a - * duplicate. */ - if self.thread_nodes[node_idx].message.is_some() { - return; + /* t_idx: The index of this message's ThreadNode in thread_nodes + * + * If id_table contains an empty Container for this ID: + * Store this message in the Container's message slot. + * Else: + * Create a new Container object holding this message; + * Index the Container by Message-ID in id_table. + */ + if self.message_ids.get(m_id).is_some() { + let node_idx = self.message_ids[m_id]; + /* the already existing ThreadNote should be empty, since we're + * seeing this message for the first time. otherwise it's a + * duplicate. */ + if self.thread_nodes[node_idx].message.is_some() { + return; + } + node_idx + } else { + /* Create a new ThreadNode object holding this message */ + self.thread_nodes.push(ThreadNode { + message: Some(envelope.hash()), + date: envelope.date(), + ..Default::default() + }); + /* The new thread node's set is just itself */ + let new_id = self.thread_nodes.len() - 1; + self.thread_nodes[new_id].thread_group = new_id; + + self.message_ids.insert(m_id.to_vec(), new_id); + new_id } - node_idx - } else { - /* Create a new ThreadNode object holding this message */ - self.thread_nodes.push(ThreadNode { - message: Some(envelope.hash()), - date: envelope.date(), - ..Default::default() - }); - /* The new thread node's set is just itself */ - let new_id = self.thread_nodes.len() - 1; - self.thread_nodes[new_id].thread_group = new_id; - - self.message_ids.insert(m_id, new_id); - new_id }; self.thread_nodes[t_idx].date = envelope.date(); self.thread_nodes[t_idx].message = Some(envelope.hash()); @@ -1007,9 +1019,9 @@ impl Threads { } for &refn in envelope.references().iter().rev() { - let r_id = String::from_utf8_lossy(refn.raw()).into(); - let parent_id = if self.message_ids.contains_key(&r_id) { - self.message_ids[&r_id] + let r_id = refn.raw(); + let parent_id = if self.message_ids.contains_key(r_id) { + self.message_ids[r_id] } else { /* Create a new ThreadNode object holding this reference */ self.thread_nodes.push(ThreadNode { @@ -1017,7 +1029,7 @@ impl Threads { }); let new_id = self.thread_nodes.len() - 1; self.thread_nodes[new_id].thread_group = new_id; - self.message_ids.insert(r_id, new_id); + self.message_ids.insert(r_id.to_vec(), new_id); new_id }; /* If they are already linked, don't change the existing links. */ @@ -1035,21 +1047,6 @@ impl Threads { self.union(ref_ptr, parent_id); make!((parent_id) parent of (ref_ptr), &mut self.thread_nodes); } - - /* Update thread's date */ - let mut parent_iter = parent_id; - 'date: loop { - let p: &mut ThreadNode = &mut self.thread_nodes[parent_iter]; - if p.date < envelope.date() { - p.date = envelope.date(); - } - if let Some(p) = p.parent { - parent_iter = p; - } else { - break 'date; - } - } - ref_ptr = parent_id; } } @@ -1069,6 +1066,26 @@ impl Index for Threads { .expect("thread index out of bounds") } } +/* + * + * + /* Update thread's date */ + let mut parent_iter = parent_id; + 'date: loop { + let p: &mut ThreadNode = &mut self.thread_nodes[parent_iter]; + if p.date < envelope.date() { + p.date = envelope.date(); + } + if let Some(p) = p.parent { + parent_iter = p; + } else { + break 'date; + } + } + ref_ptr = parent_id; + * + * + * */ fn node_build( tree: &mut ThreadTree, @@ -1080,10 +1097,12 @@ fn node_build( if let Some(hash) = thread_nodes[idx].message { if let Some(parent_id) = thread_nodes[idx].parent { if let Some(parent_hash) = thread_nodes[parent_id].message { - let mut subject = collection[&hash].subject().to_string(); - subject.strip_prefixes(); - let mut parent_subject = collection[&parent_hash].subject().to_string(); - parent_subject.strip_prefixes(); + let mut subject = collection[&hash].subject(); + let mut subject = subject.to_mut().as_bytes(); + let subject = subject.strip_prefixes(); + let mut parent_subject = collection[&parent_hash].subject(); + let mut parent_subject = parent_subject.to_mut().as_bytes(); + let parent_subject = parent_subject.strip_prefixes(); if subject == parent_subject { thread_nodes[idx].show_subject = false; } @@ -1103,15 +1122,16 @@ fn node_build( let mut has_unseen = thread_nodes[idx].has_unseen; let mut child_vec: Vec = Vec::new(); - let mut length = thread_nodes[idx].children.len(); + thread_nodes[idx].len = thread_nodes[idx].children.len(); for c in thread_nodes[idx].children.clone() { let mut new_tree = ThreadTree::new(c); node_build(&mut new_tree, c, thread_nodes, indentation, collection); - length += thread_nodes[c].len; + thread_nodes[idx].len += thread_nodes[c].len; + thread_nodes[idx].date = cmp::max(thread_nodes[idx].date, thread_nodes[c].date); + has_unseen |= thread_nodes[c].has_unseen; child_vec.push(new_tree); } tree.children = child_vec; - thread_nodes[idx].len = length; thread_nodes[idx].has_unseen = has_unseen; } diff --git a/ui/src/types/accounts.rs b/ui/src/types/accounts.rs index c2e9c63f7..1a01dc614 100644 --- a/ui/src/types/accounts.rs +++ b/ui/src/types/accounts.rs @@ -33,9 +33,10 @@ use melib::error::Result; use std::ops::{Index, IndexMut}; use std::result; use std::sync::Arc; +use std::thread; use types::UIEventType::{self, Notification}; -pub type Worker = Option>>>; +pub type Worker = Option>>; #[derive(Debug)] pub struct Account { @@ -64,8 +65,12 @@ impl Account { let notify_fn = Arc::new(notify_fn); for f in ref_folders { folders.push(None); - let handle = backend.get(&f, notify_fn.clone()); - workers.push(Some(handle)); + workers.push(Account::new_worker( + &name, + f, + &mut backend, + notify_fn.clone(), + )); } Account { name, @@ -78,6 +83,30 @@ impl Account { notify_fn, } } + fn new_worker( + name: &str, + folder: Folder, + backend: &mut Box, + notify_fn: Arc, + ) -> Worker { + let mailbox_handle = backend.get(&folder, notify_fn.clone()); + let mut builder = AsyncBuilder::new(); + let tx = builder.tx(); + Some( + builder.build( + thread::Builder::new() + .name(format!("Loading {}", name)) + .spawn(move || { + let envelopes = mailbox_handle.join(); + let ret = Mailbox::new(folder, envelopes); + tx.send(AsyncStatus::Finished); + notify_fn.notify(); + ret + }) + .unwrap(), + ), + ) + } pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option { let kind = event.kind(); let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap(); @@ -103,8 +132,13 @@ impl Account { } RefreshEventKind::Rescan => { let ref_folders: Vec = self.backend.folders(); - let handle = self.backend.get(&ref_folders[idx], self.notify_fn.clone()); - self.workers[idx] = Some(handle); + let handle = Account::new_worker( + &self.name, + ref_folders[idx].clone(), + &mut self.backend, + self.notify_fn.clone(), + ); + self.workers[idx] = handle; } } None @@ -140,17 +174,15 @@ impl Account { &mut self.workers } - fn load_mailbox(&mut self, index: usize, envelopes: Result>) { - let folders = self.backend.folders(); - let folder = &folders[index]; + fn load_mailbox(&mut self, index: usize, mailbox: Result) { if self.sent_folder.is_some() && self.sent_folder.unwrap() == index { - self.folders[index] = Some(Mailbox::new(folder, envelopes)); + self.folders[index] = Some(mailbox); /* Add our replies to other folders */ for id in (0..self.folders.len()).filter(|i| *i != index) { self.add_replies_to_folder(id); } } else { - self.folders[index] = Some(Mailbox::new(folder, envelopes)); + self.folders[index] = Some(mailbox); self.add_replies_to_folder(index); }; }