diff --git a/melib/src/mailbox/backends/maildir/backend.rs b/melib/src/mailbox/backends/maildir/backend.rs index 9e3b3523..3b57c697 100644 --- a/melib/src/mailbox/backends/maildir/backend.rs +++ b/melib/src/mailbox/backends/maildir/backend.rs @@ -29,7 +29,7 @@ use async::*; use conf::AccountSettings; use error::Result; use mailbox::backends::{ - BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer, + BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer, RefreshEventKind::*, }; use mailbox::email::{Envelope, EnvelopeHash}; @@ -49,19 +49,39 @@ use std::ffi::OsStr; use std::fs; use std::hash::{Hash, Hasher}; use std::io; -use std::io::Read; +use std::ops::{Deref, DerefMut}; use std::path::{Component, Path, PathBuf}; use std::result; use std::sync::{Arc, Mutex}; -type HashIndex = Arc>>; +#[derive(Debug, Default)] +pub struct HashIndex { + index: FnvHashMap, + hash: FolderHash, +} + +impl Deref for HashIndex { + type Target = FnvHashMap; + fn deref(&self) -> &FnvHashMap { + &self.index + } +} + +impl DerefMut for HashIndex { + fn deref_mut(&mut self) -> &mut FnvHashMap { + &mut self.index + } +} + +pub type HashIndexes = Arc>>; /// Maildir backend https://cr.yp.to/proto/maildir.html #[derive(Debug)] pub struct MaildirType { name: String, folders: Vec, - hash_index: HashIndex, + //folder_index: FnvHashMap, + hash_indexes: HashIndexes, path: PathBuf, } @@ -80,18 +100,18 @@ macro_rules! path_is_new { } macro_rules! get_path_hash { ($path:expr) => {{ - if $path.is_dir() { - if $path.ends_with("cur") | $path.ends_with("new") { - $path.pop(); + let mut path = $path.clone(); + if path.is_dir() { + if path.ends_with("cur") | path.ends_with("new") { + path.pop(); } } else { - $path.pop(); - $path.pop(); + path.pop(); + path.pop(); }; - eprintln!(" got event in {}", $path.display()); let mut hasher = DefaultHasher::new(); - $path.hash(&mut hasher); + path.hash(&mut hasher); hasher.finish() }}; } @@ -111,7 +131,7 @@ fn get_file_hash(file: &Path) -> EnvelopeHash { hasher.finish() } -fn move_to_cur(p: PathBuf) { +fn move_to_cur(p: PathBuf) -> PathBuf { let mut new = p.clone(); { let file_name = p.file_name().unwrap(); @@ -121,7 +141,8 @@ fn move_to_cur(p: PathBuf) { new.push("cur"); new.push(file_name); } - fs::rename(p, new).unwrap(); + fs::rename(p, &new).unwrap(); + new } impl MailBackend for MaildirType { @@ -148,7 +169,7 @@ impl MailBackend for MaildirType { p.push("new"); watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); } - let hash_index = self.hash_index.clone(); + let hash_indexes = self.hash_indexes.clone(); thread::Builder::new() .name("folder watch".to_string()) .spawn(move || { @@ -169,25 +190,26 @@ impl MailBackend for MaildirType { Ok(event) => match event { /* Create */ DebouncedEvent::Create(mut pathbuf) => { - if path_is_new!(pathbuf) { - move_to_cur(pathbuf); - continue; - } + let folder_hash = get_path_hash!(pathbuf); let file_name = pathbuf .as_path() .strip_prefix(&root_path) .unwrap() .to_path_buf(); if let Some(env) = add_path_to_index( - &hash_index, + &hash_indexes, + folder_hash, pathbuf.as_path(), &cache_dir, file_name, ) { sender.send(RefreshEvent { - hash: get_path_hash!(pathbuf), + hash: folder_hash, kind: Create(Box::new(env)), }); + if path_is_new!(pathbuf) { + move_to_cur(pathbuf); + } } else { continue; } @@ -195,6 +217,9 @@ impl MailBackend for MaildirType { /* Update */ DebouncedEvent::NoticeWrite(mut pathbuf) | DebouncedEvent::Write(mut pathbuf) => { + let folder_hash = get_path_hash!(pathbuf); + let mut hash_indexes_lock = hash_indexes.lock().unwrap(); + let index_lock = &mut hash_indexes_lock.entry(folder_hash).or_default(); let file_name = pathbuf .as_path() .strip_prefix(&root_path) @@ -202,7 +227,6 @@ impl MailBackend for MaildirType { .to_path_buf(); /* Linear search in hash_index to find old hash */ let old_hash: EnvelopeHash = { - let mut index_lock = hash_index.lock().unwrap(); if let Some((k, v)) = index_lock.iter_mut().find(|(_, v)| v.1 == pathbuf) { @@ -212,13 +236,14 @@ impl MailBackend for MaildirType { /* Did we just miss a Create event? In any case, create * envelope. */ if let Some(env) = add_path_to_index( - &hash_index, + &hash_indexes, + folder_hash, pathbuf.as_path(), &cache_dir, file_name, ) { sender.send(RefreshEvent { - hash: get_path_hash!(pathbuf), + hash: folder_hash, kind: Create(Box::new(env)), }); } @@ -226,25 +251,28 @@ impl MailBackend for MaildirType { } }; let new_hash: EnvelopeHash = get_file_hash(pathbuf.as_path()); - let mut index_lock = hash_index.lock().unwrap(); if index_lock.get_mut(&new_hash).is_none() { - let op = Box::new(MaildirOp::new(new_hash, hash_index.clone())); + let op = Box::new(MaildirOp::new(new_hash, hash_indexes.clone(), folder_hash)); if let Some(env) = Envelope::from_token(op, new_hash) { index_lock.insert(new_hash, (0, pathbuf.clone())); /* Send Write notice */ sender.send(RefreshEvent { - hash: get_path_hash!(pathbuf), + hash: folder_hash, kind: Update(old_hash, Box::new(env)), }); + } else { + eprintln!("DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`", new_hash, pathbuf.as_path().display()); } } } /* Remove */ DebouncedEvent::NoticeRemove(mut pathbuf) | DebouncedEvent::Remove(mut pathbuf) => { - let index_lock = hash_index.lock().unwrap(); + let folder_hash = get_path_hash!(pathbuf); + let hash_indexes_lock = hash_indexes.lock().unwrap(); + let index_lock = &hash_indexes_lock[&folder_hash]; let hash: EnvelopeHash = if let Some((k, _)) = index_lock.iter().find(|(_, v)| v.1 == pathbuf) { @@ -254,14 +282,16 @@ impl MailBackend for MaildirType { }; sender.send(RefreshEvent { - hash: get_path_hash!(pathbuf), + hash: folder_hash, kind: Remove(hash), }); } /* Envelope hasn't changed, so handle this here */ - DebouncedEvent::Rename(src, mut dest) => { + DebouncedEvent::Rename(mut src, mut dest) => { + let folder_hash = get_path_hash!(src); let old_hash: EnvelopeHash = get_file_hash(src.as_path()); - let mut index_lock = hash_index.lock().unwrap(); + let mut hash_indexes_lock = hash_indexes.lock().unwrap(); + let mut index_lock = hash_indexes_lock.entry(folder_hash).or_default(); if let Some(v) = index_lock.get_mut(&old_hash) { v.1 = dest; } else { @@ -285,8 +315,8 @@ impl MailBackend for MaildirType { })?; Ok(()) } - fn operation(&self, hash: EnvelopeHash) -> Box { - Box::new(MaildirOp::new(hash, self.hash_index.clone())) + fn operation(&self, hash: EnvelopeHash, folder_hash: FolderHash) -> Box { + Box::new(MaildirOp::new(hash, self.hash_indexes.clone(), folder_hash)) } } @@ -329,13 +359,26 @@ impl MaildirType { } } folders[0].children = recurse_folders(&mut folders, &path); + let hash_indexes = Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher( + folders.len(), + Default::default(), + ))); + { + let mut hash_indexes = hash_indexes.lock().unwrap(); + for f in &folders { + hash_indexes.insert( + f.hash(), + HashIndex { + index: FnvHashMap::with_capacity_and_hasher(0, Default::default()), + hash: f.hash(), + }, + ); + } + } MaildirType { name: f.name().to_string(), folders, - hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher( - 0, - Default::default(), - ))), + hash_indexes, path: PathBuf::from(f.root_folder()), } } @@ -357,14 +400,20 @@ impl MaildirType { let mut w = AsyncBuilder::new(); let root_path = self.path.to_path_buf(); let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap(); + { + let mut hash_index = self.hash_indexes.lock().unwrap(); + let index_lock = hash_index.entry(folder.hash()).or_default(); + index_lock.clear(); + } let handle = { let tx = w.tx(); // TODO: Avoid clone let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)]; + let folder_hash = folder.hash(); let mut path: PathBuf = folder.path().into(); let name = format!("parsing {:?}", folder.name()); - let map = self.hash_index.clone(); - let map2 = self.hash_index.clone(); + let map = self.hash_indexes.clone(); + let map2 = self.hash_indexes.clone(); thread::Builder::new() .name(name.clone()) @@ -412,10 +461,10 @@ impl MaildirType { Envelope, > = Vec::with_capacity(chunk.len()); for c in chunk.chunks(size) { + //thread::yield_now(); 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 */ @@ -423,7 +472,7 @@ impl MaildirType { .strip_prefix(&root_path) .unwrap() .to_path_buf(); - let hash = if let Some(cached) = + if let Some(cached) = cache_dir.find_cache_file(&file_name) { /* Cached struct exists, try to load it */ @@ -433,43 +482,25 @@ impl MaildirType { let result: result::Result = bincode::deserialize_from(reader); if let Ok(env) = result { let mut map = map.lock().unwrap(); + let mut map = map.entry(folder_hash).or_default();; let hash = env.hash(); - if (*map).contains_key(&hash) { - continue; - } - - (*map).insert(hash, (0, file.clone())); + 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) { - continue; - } - (*map).insert(hash, (0, PathBuf::from(file))); - } - let op = - Box::new(MaildirOp::new(hash, map.clone())); - if let Some(mut e) = Envelope::from_token(op, hash) - { - if let Ok(cached) = - cache_dir.place_cache_file(file_name) + let mut map = map.lock().unwrap(); + let mut map = map.entry(folder_hash).or_default(); + (*map).insert(hash, (0, PathBuf::from(file))); + } + let op = + Box::new(MaildirOp::new(hash, map.clone(), folder_hash)); + if let Some(mut e) = Envelope::from_token(op, hash) + { + if let Ok(cached) = + cache_dir.place_cache_file(file_name) { /* place result in cache directory */ let f = match fs::File::create(cached) { @@ -482,10 +513,10 @@ impl MaildirType { bincode::serialize_into(writer, &e) .unwrap(); } - local_r.push(e); - } else { - continue; - } + local_r.push(e); + } else { + eprintln!("DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`", hash, file.as_path().display()); + continue; } } tx.send(AsyncStatus::ProgressReport(len)); @@ -501,6 +532,7 @@ impl MaildirType { r.append(&mut result); } let mut map = map2.lock().unwrap(); + let map = map.entry(folder_hash).or_default(); for (idx, e) in r.iter().enumerate() { let mut y = (*map)[&e.hash()].clone(); y.0 = idx; @@ -517,7 +549,8 @@ impl MaildirType { } fn add_path_to_index( - hash_index: &HashIndex, + hash_index: &HashIndexes, + folder_hash: FolderHash, path: &Path, cache_dir: &xdg::BaseDirectories, file_name: PathBuf, @@ -525,13 +558,14 @@ fn add_path_to_index( let env: Envelope; let hash = get_file_hash(path); { - let mut index_lock = hash_index.lock().unwrap(); - if (*index_lock).contains_key(&hash) { + let mut hash_index = hash_index.lock().unwrap(); + let index_lock = hash_index.entry(folder_hash).or_default(); + if index_lock.contains_key(&hash) { return None; } - (*index_lock).insert(hash, (0, path.to_path_buf())); + index_lock.insert(hash, (0, path.to_path_buf())); } - let op = Box::new(MaildirOp::new(hash, hash_index.clone())); + let op = Box::new(MaildirOp::new(hash, hash_index.clone(), folder_hash)); if let Some(e) = Envelope::from_token(op, hash) { if let Ok(cached) = cache_dir.place_cache_file(file_name) { /* place result in cache directory */ @@ -546,6 +580,11 @@ fn add_path_to_index( } env = e; } else { + eprintln!( + "DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`", + hash, + path.display() + ); return None; } Some(env) diff --git a/melib/src/mailbox/backends/maildir/mod.rs b/melib/src/mailbox/backends/maildir/mod.rs index 402721e9..723d3dbc 100644 --- a/melib/src/mailbox/backends/maildir/mod.rs +++ b/melib/src/mailbox/backends/maildir/mod.rs @@ -20,7 +20,6 @@ */ extern crate fnv; -use self::fnv::FnvHashMap; mod backend; pub use self::backend::*; @@ -35,12 +34,12 @@ use std::collections::hash_map::DefaultHasher; use std::fs; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; /// `BackendOp` implementor for Maildir #[derive(Debug)] pub struct MaildirOp { - hash_index: Arc>>, + hash_index: HashIndexes, + folder_hash: FolderHash, hash: EnvelopeHash, slice: Option, } @@ -49,6 +48,7 @@ impl Clone for MaildirOp { fn clone(&self) -> Self { MaildirOp { hash_index: self.hash_index.clone(), + folder_hash: self.folder_hash, hash: self.hash, slice: None, } @@ -56,19 +56,17 @@ impl Clone for MaildirOp { } impl MaildirOp { - pub fn new( - hash: EnvelopeHash, - hash_index: Arc>>, - ) -> Self { + pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, folder_hash: FolderHash) -> Self { MaildirOp { hash_index, + folder_hash, hash, slice: None, } } fn path(&self) -> PathBuf { - let hash_index = self.hash_index.clone(); - let map = hash_index.lock().unwrap(); + let map = self.hash_index.lock().unwrap(); + let map = &map[&self.folder_hash]; map.get(&self.hash).unwrap().1.clone() } } @@ -154,6 +152,7 @@ impl<'a> BackendOp for MaildirOp { let hash = envelope.hash(); let hash_index = self.hash_index.clone(); let mut map = hash_index.lock().unwrap(); + let map = map.entry(self.folder_hash).or_default(); map.get_mut(&hash).unwrap().1 = PathBuf::from(new_name); Ok(()) } diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index f86511e0..d254af72 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::ops::Deref; use std::sync::Arc; extern crate fnv; @@ -147,7 +148,7 @@ pub trait MailBackend: ::std::fmt::Debug { 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; + fn operation(&self, hash: EnvelopeHash, folder_hash: FolderHash) -> Box; //login function } @@ -265,4 +266,16 @@ pub fn folder_default() -> Folder { } pub type FolderHash = u64; -pub type Folder = Box; +pub type Folder = Box; + +impl Clone for Folder { + fn clone(&self) -> Self { + BackendFolder::clone(self.deref()) + } +} + +impl Default for Folder { + fn default() -> Self { + folder_default() + } +} diff --git a/melib/src/mailbox/collection.rs b/melib/src/mailbox/collection.rs index cbdb321c..4174867f 100644 --- a/melib/src/mailbox/collection.rs +++ b/melib/src/mailbox/collection.rs @@ -14,12 +14,32 @@ use self::fnv::FnvHashMap; /// `Mailbox` represents a folder of mail. #[derive(Debug, Clone, Default)] pub struct Collection { + folder: Folder, pub envelopes: FnvHashMap, date_index: BTreeMap, subject_index: Option>, pub threads: Threads, } +impl Drop for Collection { + fn drop(&mut self) { + let cache_dir = + xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", self.folder.hash())) + .unwrap(); + 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, &self.threads).unwrap(); + } + } +} + impl Collection { pub fn new(vec: Vec, folder: &Folder) -> Collection { let mut envelopes: FnvHashMap = @@ -36,26 +56,26 @@ impl Collection { 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 { + let ret = if let Ok(mut cached_t) = result { cached_t.update(&mut envelopes); cached_t } else { - 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 + Threads::new(&mut envelopes) + }; + 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 { - let ret = Threads::new(&mut envelopes); // sent_folder); + let ret = Threads::new(&mut envelopes); if let Ok(cached) = cache_dir.place_cache_file("threads") { /* place result in cache directory */ let f = match fs::File::create(cached) { @@ -70,6 +90,7 @@ impl Collection { ret }; Collection { + folder: folder.clone(), envelopes, date_index, subject_index, @@ -94,7 +115,8 @@ impl Collection { } } pub(crate) fn insert_reply(&mut self, envelope: Envelope) { - self.threads.insert_reply(envelope, &mut self.envelopes); + self.insert(envelope); + //self.threads.insert_reply(envelope, &mut self.envelopes); } } diff --git a/melib/src/mailbox/email/attachment_types.rs b/melib/src/mailbox/email/attachment_types.rs index 3f5271c3..179735d7 100644 --- a/melib/src/mailbox/email/attachment_types.rs +++ b/melib/src/mailbox/email/attachment_types.rs @@ -58,7 +58,7 @@ impl<'a> From<&'a [u8]> for Charset { b"utf-16" | b"UTF-16" => Charset::UTF16, b"iso-8859-1" | b"ISO-8859-1" => Charset::ISO8859_1, b"iso-8859-2" | b"ISO-8859-2" => Charset::ISO8859_2, - b"iso-8859-7" | b"ISO-8859-7" => Charset::ISO8859_7, + b"iso-8859-7" | b"ISO-8859-7" | b"iso8859-7" => Charset::ISO8859_7, b"iso-8859-15" | b"ISO-8859-15" => Charset::ISO8859_15, b"windows-1251" | b"Windows-1251" => Charset::Windows1251, b"windows-1252" | b"Windows-1252" => Charset::Windows1252, diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index 8daf75ae..0acefffe 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -209,12 +209,18 @@ impl fmt::Debug for MessageID { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] struct References { raw: Vec, refs: Vec, } +impl fmt::Debug for References { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#?}", self.refs) + } +} + bitflags! { #[derive(Default, Serialize, Deserialize)] pub struct Flag: u8 { diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index 0e78afdf..2c26ff2f 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -30,7 +30,7 @@ pub use self::email::*; /* Mail backends. Currently only maildir is supported */ pub mod backends; use error::Result; -use mailbox::backends::{folder_default, Folder}; +use mailbox::backends::Folder; pub mod thread; pub use mailbox::thread::{SortField, SortOrder, ThreadNode, Threads}; @@ -40,7 +40,7 @@ pub use self::collection::*; use std::option::Option; /// `Mailbox` represents a folder of mail. -#[derive(Debug)] +#[derive(Debug, Clone, Default)] pub struct Mailbox { pub folder: Folder, name: String, @@ -48,27 +48,6 @@ pub struct Mailbox { has_sent: bool, } -impl Clone for Mailbox { - fn clone(&self) -> Self { - Mailbox { - folder: self.folder.clone(), - collection: self.collection.clone(), - has_sent: self.has_sent, - name: self.name.clone(), - } - } -} -impl Default for Mailbox { - fn default() -> Self { - Mailbox { - folder: folder_default(), - collection: Collection::default(), - has_sent: false, - name: String::new(), - } - } -} - impl Mailbox { pub fn new(folder: Folder, envelopes: Result>) -> Result { let mut envelopes: Vec = envelopes?; diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index b8aa4478..13791d9a 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -719,6 +719,21 @@ impl Threads { } } + pub fn update_envelope(&mut self, old_hash: EnvelopeHash, envelope: &Envelope) { + /* must update: + * - hash_set + * - message fields in thread_nodes + */ + self.hash_set.remove(&old_hash); + self.hash_set.insert(envelope.hash()); + let node = self + .thread_nodes + .iter_mut() + .find(|n| n.message.map(|n| n == old_hash).unwrap_or(false)) + .unwrap(); + node.message = Some(envelope.hash()); + } + pub fn update(&mut self, collection: &mut FnvHashMap) { let new_hash_set = FnvHashSet::from_iter(collection.keys().cloned()); @@ -732,6 +747,18 @@ impl Threads { self.insert(&mut (*env), collection); } } + + let difference: Vec = + self.hash_set.difference(&new_hash_set).cloned().collect(); + for h in difference { + self.hash_set.remove(&h); + let node = self + .thread_nodes + .iter_mut() + .find(|n| n.message.map(|n| n == h).unwrap_or(false)) + .unwrap(); + node.message = None; + } } pub fn insert( @@ -787,29 +814,34 @@ impl Threads { stack.push(node_idx); } - /* Trace path from root ThreadTree to the envelope's parent */ - let mut tree = self.tree.get_mut(); - for &s in stack.iter().rev() { - /* Borrow checker is being a tad silly here, so the following - * is basically this: - * - * let tree = &mut tree[s].children; - */ - let temp_tree = tree; - 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; + { + /* Trace path from root ThreadTree to the envelope's parent */ + let mut tree = self.tree.get_mut(); + for &s in stack.iter().rev() { + /* Borrow checker is being a tad silly here, so the following + * is basically this: + * + * let tree = &mut tree[s].children; + */ + let temp_tree = tree; + 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 */ + let tree_node = ThreadTree::new(id); + tree.push(tree_node); + let new_id = tree.len() - 1; + node_build(&mut tree[new_id], id, &mut self.thread_nodes, 1, collection); } - /* Add new child */ - let tree_node = ThreadTree::new(id); - tree.push(tree_node); - let new_id = tree.len() - 1; - node_build(&mut tree[new_id], id, &mut self.thread_nodes, 1, collection); + // FIXME: use insertion according to self.sort etc instead of sorting everytime + self.inner_sort_by(*self.sort.borrow(), collection); + self.inner_subsort_by(*self.subsort.borrow(), collection); } /* @@ -1073,26 +1105,6 @@ 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, @@ -1104,6 +1116,10 @@ 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 { + /* decide if the subject should be shown in the UI. + * If parent subject is Foobar and reply is `Re: Foobar` + * then showing the reply's subject can be reduntant + */ let mut subject = collection[&hash].subject(); let mut subject = subject.to_mut().as_bytes(); let subject = subject.strip_prefixes(); diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index d432de8c..9026ea19 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -114,7 +114,7 @@ impl Composer { let parent_message = &mailbox.collection[&p.message().unwrap()]; let mut op = context.accounts[coordinates.0] .backend - .operation(parent_message.hash()); + .operation(parent_message.hash(), mailbox.folder.hash()); let parent_bytes = op.as_bytes(); ret.draft = Draft::new_reply(parent_message, parent_bytes.unwrap()); @@ -456,12 +456,11 @@ impl Component for Composer { } fn is_dirty(&self) -> bool { - self.dirty || self.pager.is_dirty() - || self - .reply_context - .as_ref() - .map(|(_, p)| p.is_dirty()) - .unwrap_or(false) + self.dirty || self.pager.is_dirty() || self + .reply_context + .as_ref() + .map(|(_, p)| p.is_dirty()) + .unwrap_or(false) } fn set_dirty(&mut self) { diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 3106c083..3d339737 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -113,7 +113,6 @@ impl CompactListing { }); // Get mailbox as a reference. // - // TODO: Show progress visually match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { Ok(_) => {} Err(_) => { @@ -160,6 +159,17 @@ impl CompactListing { } threads.thread_nodes()[iter_ptr].message().unwrap() }; + if !mailbox.collection.contains_key(&i) { + eprintln!("key = {}", i); + eprintln!( + "name = {} {}", + mailbox.name(), + context.accounts[self.cursor_pos.0].name() + ); + eprintln!("{:#?}", context.accounts); + + panic!(); + } let root_envelope: &Envelope = &mailbox.collection[&i]; let fg_color = if thread_node.has_unseen() { Color::Byte(0) @@ -292,6 +302,10 @@ impl CompactListing { } else if self.cursor_pos != self.new_cursor_pos { self.cursor_pos = self.new_cursor_pos; } + if self.new_cursor_pos.2 >= self.length { + self.new_cursor_pos.2 = self.length - 1; + self.cursor_pos.2 = self.new_cursor_pos.2; + } /* Page_no has changed, so draw new page */ copy_area( @@ -441,12 +455,13 @@ impl Component for CompactListing { return true; } UIEventType::RefreshMailbox(_) => { - self.dirty = true; self.view = None; + self.dirty = true; } UIEventType::MailboxUpdate((ref idxa, ref idxf)) => { if *idxa == self.new_cursor_pos.0 && *idxf == self.new_cursor_pos.1 { self.refresh_mailbox(context); + self.set_dirty(); } } UIEventType::ChangeMode(UIMode::Normal) => { diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs index 0e49d6f8..90c10045 100644 --- a/ui/src/components/mail/listing/plain.rs +++ b/ui/src/components/mail/listing/plain.rs @@ -342,9 +342,13 @@ impl Component for PlainListing { (envelope.hash(), envelope.is_seen()) }; if !is_seen { + let folder_hash = { + let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); + mailbox.folder.hash() + }; let op = { let backend = &account.backend; - backend.operation(hash) + backend.operation(hash, folder_hash) }; let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); let envelope: &mut Envelope = &mut mailbox diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs index 91ebbbcb..efc3a291 100644 --- a/ui/src/components/mail/listing/thread.rs +++ b/ui/src/components/mail/listing/thread.rs @@ -415,9 +415,13 @@ impl Component for ThreadListing { (envelope.hash(), envelope.is_seen()) }; if !is_seen { + let folder_hash = { + let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); + mailbox.folder.hash() + }; let op = { let backend = &account.backend; - backend.operation(hash) + backend.operation(hash, folder_hash) }; let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); let envelope: &mut Envelope = mailbox.thread_to_mail_mut(idx); diff --git a/ui/src/components/mail/view/mod.rs b/ui/src/components/mail/view/mod.rs index 53e58537..b2de65d6 100644 --- a/ui/src/components/mail/view/mod.rs +++ b/ui/src/components/mail/view/mod.rs @@ -305,7 +305,7 @@ impl Component for MailView { let envelope: &Envelope = &mailbox.collection[&mailbox_idx.2]; let op = context.accounts[mailbox_idx.0] .backend - .operation(envelope.hash()); + .operation(envelope.hash(), mailbox.folder.hash()); let body = envelope.body(op); match self.mode { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { @@ -422,7 +422,7 @@ impl Component for MailView { let envelope: &Envelope = &mailbox.collection[&self.coordinates.2]; let op = context.accounts[self.coordinates.0] .backend - .operation(envelope.hash()); + .operation(envelope.hash(), mailbox.folder.hash()); if let Some(u) = envelope.body(op).attachments().get(lidx) { match u.content_type() { ContentType::MessageRfc822 => { @@ -518,7 +518,7 @@ impl Component for MailView { let finder = LinkFinder::new(); let op = context.accounts[self.coordinates.0] .backend - .operation(envelope.hash()); + .operation(envelope.hash(), mailbox.folder.hash()); let mut t = envelope.body(op).text().to_string(); let links: Vec = finder.links(&t).collect(); if let Some(u) = links.get(lidx) { diff --git a/ui/src/state.rs b/ui/src/state.rs index bb39129b..23879f19 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -64,8 +64,7 @@ impl InputHandler { }, &rx, ) - }) - .unwrap(); + }).unwrap(); } fn kill(&self) { self.tx.send(false); @@ -187,8 +186,7 @@ impl State { sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)) })), ) - }) - .collect(); + }).collect(); accounts.sort_by(|a, b| a.name().cmp(&b.name())); let mut s = State { cols, @@ -258,6 +256,10 @@ impl State { id: 0, event_type: notification, }); + self.context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::MailboxUpdate((idxa, idxm)), + }); } } else { eprintln!( diff --git a/ui/src/types/accounts.rs b/ui/src/types/accounts.rs index 0d231796..a8e034ea 100644 --- a/ui/src/types/accounts.rs +++ b/ui/src/types/accounts.rs @@ -30,6 +30,7 @@ use mailbox::backends::{ }; use mailbox::*; use melib::error::Result; +use std::mem; use std::ops::{Index, IndexMut}; use std::result; use std::sync::Arc; @@ -102,45 +103,49 @@ impl Account { tx.send(AsyncStatus::Finished); notify_fn.notify(); ret - }) - .unwrap(), + }).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(); - match kind { - RefreshEventKind::Update(old_hash, envelope) => { - mailbox.update(old_hash, *envelope); - } - RefreshEventKind::Create(envelope) => { - let env: &Envelope = mailbox.insert(*envelope); - let ref_folders: Vec = self.backend.folders(); - return Some(Notification( - Some("new mail".into()), - format!( - "{:.15}:\nSubject: {:.15}\nFrom: {:.15}", - ref_folders[idx].name(), - env.subject(), - env.field_from_to_string() - ), - )); - } - RefreshEventKind::Remove(envelope_hash) => { - mailbox.remove(envelope_hash); - } - RefreshEventKind::Rescan => { - let ref_folders: Vec = self.backend.folders(); - let handle = Account::new_worker( - &self.name, - ref_folders[idx].clone(), - &mut self.backend, - self.notify_fn.clone(), - ); - self.workers[idx] = handle; + { + let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap(); + match kind { + RefreshEventKind::Update(old_hash, envelope) => { + mailbox.update(old_hash, *envelope); + } + RefreshEventKind::Create(envelope) => { + let env: &Envelope = mailbox.insert(*envelope); + let ref_folders: Vec = self.backend.folders(); + return Some(Notification( + Some("new mail".into()), + format!( + "{:.15}:\nSubject: {:.15}\nFrom: {:.15}", + ref_folders[idx].name(), + env.subject(), + env.field_from_to_string() + ), + )); + } + RefreshEventKind::Remove(envelope_hash) => { + mailbox.remove(envelope_hash); + } + RefreshEventKind::Rescan => { + let ref_folders: Vec = self.backend.folders(); + let handle = Account::new_worker( + &self.name, + ref_folders[idx].clone(), + &mut self.backend, + self.notify_fn.clone(), + ); + self.workers[idx] = handle; + } } } + if self.workers[idx].is_some() { + self.folders[idx] = None; + } None } pub fn watch(&self, r: RefreshEventConsumer) -> () { @@ -204,17 +209,15 @@ impl Account { use std::slice::from_raw_parts_mut; ( from_raw_parts_mut(ptr.offset(*sent_index as isize), *sent_index + 1) - [0] - .as_mut() - .unwrap() - .as_mut() - .unwrap(), + [0].as_mut() + .unwrap() + .as_mut() + .unwrap(), from_raw_parts_mut(ptr.offset(folder_index as isize), folder_index + 1) - [0] - .as_mut() - .unwrap() - .as_mut() - .unwrap(), + [0].as_mut() + .unwrap() + .as_mut() + .unwrap(), ) } };