diff --git a/melib/src/mailbox/backends/maildir/backend.rs b/melib/src/mailbox/backends/maildir/backend.rs index 53138899a..1d23b980f 100644 --- a/melib/src/mailbox/backends/maildir/backend.rs +++ b/melib/src/mailbox/backends/maildir/backend.rs @@ -24,7 +24,7 @@ extern crate fnv; extern crate notify; extern crate xdg; -use super::{MaildirFolder, MaildirOp}; +use super::{MaildirFolder, MaildirOp, NotifyFn}; use async::*; use conf::AccountSettings; use error::Result; @@ -125,8 +125,8 @@ impl MailBackend for MaildirType { fn folders(&self) -> Vec { self.folders.iter().map(|f| f.clone()).collect() } - fn get(&mut self, folder: &Folder) -> Async>> { - self.multicore(4, folder) + fn get(&mut self, folder: &Folder, notify_fn: Arc) -> Async>> { + self.multicore(4, folder, notify_fn) } fn watch(&self, sender: RefreshEventConsumer) -> Result<()> { let (tx, rx) = channel(); @@ -345,7 +345,12 @@ impl MaildirType { .0 } - pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async>> { + pub fn multicore( + &mut self, + cores: usize, + folder: &Folder, + notify_fn: Arc, + ) -> Async>> { let mut w = AsyncBuilder::new(); let root_path = self.path.to_path_buf(); let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap(); @@ -483,6 +488,7 @@ impl MaildirType { (*map).insert(e.hash(), y); } tx.send(AsyncStatus::Finished); + notify_fn.notify(); Ok(r) }) .unwrap() diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index 819551e84..a125d5952 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -31,6 +31,7 @@ use mailbox::backends::maildir::MaildirType; use mailbox::email::{Envelope, EnvelopeHash, Flag}; use std::fmt; use std::fmt::Debug; +use std::sync::Arc; extern crate fnv; use self::fnv::FnvHashMap; @@ -117,8 +118,33 @@ impl RefreshEventConsumer { self.0(r); } } + +pub struct NotifyFn(Box ()>); +unsafe impl Send for NotifyFn {} +unsafe impl Sync for NotifyFn {} + +impl fmt::Debug for NotifyFn { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "NotifyFn Box") + } +} + +impl From ()>> for NotifyFn { + fn from(kind: Box ()>) -> Self { + NotifyFn(kind) + } +} + +impl NotifyFn { + pub fn new(b: Box ()>) -> Self { + NotifyFn(b) + } + pub fn notify(&self) -> () { + self.0(); + } +} pub trait MailBackend: ::std::fmt::Debug { - fn get(&mut self, folder: &Folder) -> Async>>; + fn get(&mut self, folder: &Folder, notify_fn: Arc) -> Async>>; fn watch(&self, sender: RefreshEventConsumer) -> Result<()>; fn folders(&self) -> Vec; fn operation(&self, hash: EnvelopeHash) -> Box; diff --git a/melib/src/mailbox/collection.rs b/melib/src/mailbox/collection.rs index e87ea6c2a..316381148 100644 --- a/melib/src/mailbox/collection.rs +++ b/melib/src/mailbox/collection.rs @@ -1,6 +1,12 @@ +extern crate bincode; +extern crate xdg; + use super::*; use std::collections::BTreeMap; +use std::fs; +use std::io; use std::ops::{Deref, DerefMut}; +use std::result; extern crate fnv; use self::fnv::FnvHashMap; @@ -15,7 +21,7 @@ pub struct Collection { } impl Collection { - pub fn new(vec: Vec) -> Collection { + pub fn new(vec: Vec, name: &str) -> Collection { let mut envelopes: FnvHashMap = FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default()); for e in vec { @@ -24,7 +30,19 @@ impl Collection { let date_index = BTreeMap::new(); let subject_index = None; - let threads = Threads::new(&mut envelopes); // sent_folder); + let cache_dir = xdg::BaseDirectories::with_profile("meli", name).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); + if let Ok(mut cached_t) = result { + cached_t.update(&mut envelopes); + cached_t + } else { + Threads::new(&mut envelopes) // sent_folder); + } + } else { + Threads::new(&mut envelopes) // sent_folder); + }; Collection { envelopes, date_index, diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index 11eb5ba9f..9c8e26832 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -71,7 +71,7 @@ impl Mailbox { ) -> Result { let mut envelopes: Vec = envelopes?; envelopes.sort_by(|a, b| a.date().cmp(&b.date())); - let collection = Collection::new(envelopes); + let collection = Collection::new(envelopes, folder.name()); Ok(Mailbox { folder: (*folder).clone(), collection, diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index e9063e260..a90240440 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -28,17 +28,18 @@ use mailbox::email::*; extern crate fnv; use self::fnv::{FnvHashMap, FnvHashSet}; use std::cell::RefCell; +use std::iter::FromIterator; use std::ops::Index; use std::result::Result as StdResult; use std::str::FromStr; -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Copy, Deserialize)] pub enum SortOrder { Asc, Desc, } -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Copy, Deserialize)] pub enum SortField { Subject, Date, @@ -78,7 +79,7 @@ impl FromStr for SortOrder { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize)] pub struct ThreadNode { message: Option, parent: Option, @@ -157,7 +158,7 @@ impl ThreadNode { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Deserialize)] pub struct Threads { thread_nodes: Vec, root_set: Vec, @@ -226,6 +227,16 @@ impl Threads { t } + pub fn update(&mut self, collection: &mut FnvHashMap) { + let new_hash_set = FnvHashSet::from_iter(collection.keys().cloned()); + + let difference: Vec = + new_hash_set.difference(&self.hash_set).cloned().collect(); + for h in difference { + self.insert(collection.entry(h).or_default()); + } + } + pub fn insert(&mut self, envelope: &mut Envelope) { link_envelope( &mut self.thread_nodes, diff --git a/src/bin.rs b/src/bin.rs index d97af8dd4..e7e544f49 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -147,7 +147,6 @@ fn main() { break 'inner; // `goto` 'reap loop, and wait on child. } ThreadEvent::UIEvent(UIEventType::StartupCheck) => { - let mut flag = false; let mut render_flag = false; for idx_a in 0..state.context.accounts.len() { let len = state.context.accounts[idx_a].len(); @@ -156,16 +155,10 @@ fn main() { Ok(true) => { render_flag = true; }, - Ok(false) => {}, - Err(_) => { - flag |= true; - } + Ok(false) | Err(_) => {} } } } - if !flag { - state.finish_startup(); - } if render_flag { state.render(); } diff --git a/ui/src/state.rs b/ui/src/state.rs index ef2f655ac..e0b42caaf 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -29,14 +29,13 @@ */ use super::*; -use melib::backends::FolderHash; +use melib::backends::{FolderHash, NotifyFn}; use chan::{Receiver, Sender}; use fnv::FnvHashMap; use std::io::Write; use std::result; use std::thread; -use std::time; use termion::raw::IntoRawMode; use termion::screen::AlternateScreen; use termion::{clear, cursor, style}; @@ -122,9 +121,6 @@ pub struct State { pub mode: UIMode, entities: Vec, pub context: Context, - - startup_thread: Option>, - threads: FnvHashMap, thread::JoinHandle<()>)>, } @@ -174,34 +170,19 @@ impl State { let mut accounts: Vec = settings .accounts .iter() - .map(|(n, a_s)| Account::new(n.to_string(), a_s.clone(), &backends)) + .map(|(n, a_s)| { + let sender = sender.clone(); + Account::new( + n.to_string(), + a_s.clone(), + &backends, + NotifyFn::new(Box::new(move || { + sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)) + })), + ) + }) .collect(); accounts.sort_by(|a, b| a.name().cmp(&b.name())); - let (startup_tx, startup_rx) = chan::async(); - let startup_thread = { - let sender = sender.clone(); - let startup_rx = startup_rx.clone(); - - thread::Builder::new() - .name("startup-thread".to_string()) - .spawn(move || { - let dur = time::Duration::from_millis(800); - loop { - chan_select! { - default => {}, - startup_rx.recv() -> _ => { - sender.send(ThreadEvent::ThreadJoin(thread::current().id())); - break; - } - } - sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)); - thread::sleep(dur); - } - startup_rx.recv(); - return; - }) - .unwrap() - }; let mut s = State { cols, rows, @@ -228,13 +209,8 @@ impl State { tx: input_thread.0, }, }, - startup_thread: Some(startup_tx.clone()), threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()), }; - s.threads.insert( - startup_thread.thread().id(), - (startup_tx.clone(), startup_thread), - ); write!( s.stdout(), "{}{}{}", @@ -271,34 +247,6 @@ impl State { event_type: notification, }); } - - let (startup_tx, startup_rx) = chan::async(); - let startup_thread = { - let sender = self.context.sender.clone(); - let startup_rx = startup_rx.clone(); - - thread::Builder::new() - .name("startup-thread".to_string()) - .spawn(move || { - let dur = time::Duration::from_millis(100); - loop { - chan_select! { - default => {}, - startup_rx.recv() -> _ => { - sender.send(ThreadEvent::UIEvent(UIEventType::MailboxUpdate((idxa,idxm)))); - sender.send(ThreadEvent::ThreadJoin(thread::current().id())); - return; - } - } - sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)); - thread::sleep(dur); - } - }) - .expect("Failed to spawn startup-thread in hash_to_folder()") - }; - self.startup_thread = Some(startup_tx.clone()); - self.threads - .insert(startup_thread.thread().id(), (startup_tx, startup_thread)); } else { eprintln!( "BUG: mailbox with hash {} not found in mailbox_hashes.", @@ -315,19 +263,6 @@ impl State { handle.join().unwrap(); } - /// If startup has finished, inform startup thread that it doesn't need to tick us with startup - /// check reminders and let it die. - pub fn finish_startup(&mut self) { - // TODO: Encode startup process with the type system if possible - if self.startup_thread.is_none() { - return; - } - { - let tx = self.startup_thread.take().unwrap(); - tx.send(true); - } - } - /// Switch back to the terminal's main screen (The command line the user sees before opening /// the application) pub fn switch_to_main_screen(&mut self) { diff --git a/ui/src/types/accounts.rs b/ui/src/types/accounts.rs index eeeec60bf..f411249e9 100644 --- a/ui/src/types/accounts.rs +++ b/ui/src/types/accounts.rs @@ -26,12 +26,13 @@ use async::*; use conf::AccountConf; use mailbox::backends::{ - Backends, Folder, MailBackend, RefreshEvent, RefreshEventConsumer, RefreshEventKind, + Backends, Folder, MailBackend, NotifyFn, RefreshEvent, RefreshEventConsumer, RefreshEventKind, }; use mailbox::*; use melib::error::Result; use std::ops::{Index, IndexMut}; use std::result; +use std::sync::Arc; use types::UIEventType::{self, Notification}; pub type Worker = Option>>>; @@ -48,10 +49,11 @@ pub struct Account { pub settings: AccountConf, pub runtime_settings: AccountConf, pub backend: Box, + notify_fn: Arc, } impl Account { - pub fn new(name: String, settings: AccountConf, map: &Backends) -> Self { + pub fn new(name: String, settings: AccountConf, map: &Backends, notify_fn: NotifyFn) -> Self { let mut backend = map.get(settings.account().format())(settings.account()); let ref_folders: Vec = backend.folders(); let mut folders: Vec>> = Vec::with_capacity(ref_folders.len()); @@ -59,9 +61,10 @@ impl Account { let sent_folder = ref_folders .iter() .position(|x: &Folder| x.name() == settings.account().sent_folder); + let notify_fn = Arc::new(notify_fn); for f in ref_folders { folders.push(None); - let handle = backend.get(&f); + let handle = backend.get(&f, notify_fn.clone()); workers.push(Some(handle)); } Account { @@ -72,6 +75,7 @@ impl Account { settings: settings.clone(), runtime_settings: settings, backend, + notify_fn, } } pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option { @@ -99,7 +103,7 @@ impl Account { } RefreshEventKind::Rescan => { let ref_folders: Vec = self.backend.folders(); - let handle = self.backend.get(&ref_folders[idx]); + let handle = self.backend.get(&ref_folders[idx], self.notify_fn.clone()); self.workers[idx] = Some(handle); } }