From 22d868f499bbf617a530fd67754c46a15896c4ec Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 14 May 2019 21:47:47 +0300 Subject: save Account to disk closes #114 --- melib/src/conf.rs | 2 +- melib/src/error.rs | 2 +- melib/src/mailbox.rs | 23 +- melib/src/mailbox/collection.rs | 3 +- melib/src/mailbox/email.rs | 9 +- melib/src/mailbox/thread.rs | 389 +++++++++++++++++------------- ui/src/components/mail.rs | 1 + ui/src/components/mail/compose.rs | 12 +- ui/src/components/mail/listing/compact.rs | 24 +- ui/src/components/mail/listing/thread.rs | 12 +- ui/src/components/mail/view/thread.rs | 51 ++-- ui/src/conf/accounts.rs | 13 +- ui/src/conf/mailer.rs | 2 +- ui/src/conf/pager.rs | 2 +- ui/src/execute/actions.rs | 5 +- 15 files changed, 315 insertions(+), 235 deletions(-) diff --git a/melib/src/conf.rs b/melib/src/conf.rs index 360cd730..02830d26 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -#[derive(Debug, Default, Clone)] +#[derive(Debug, Serialize, Default, Clone)] pub struct AccountSettings { pub name: String, pub root_folder: String, diff --git a/melib/src/error.rs b/melib/src/error.rs index 657dd38d..4e12428d 100644 --- a/melib/src/error.rs +++ b/melib/src/error.rs @@ -35,7 +35,7 @@ use nom; pub type Result = result::Result; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct MeliError { details: String, } diff --git a/melib/src/mailbox.rs b/melib/src/mailbox.rs index ac93cc30..c593bc8a 100644 --- a/melib/src/mailbox.rs +++ b/melib/src/mailbox.rs @@ -31,6 +31,8 @@ pub use self::email::*; pub mod backends; use self::backends::Folder; use crate::error::Result; +use crate::mailbox::thread::ThreadHash; + pub mod thread; pub use self::thread::{SortField, SortOrder, ThreadNode, Threads}; @@ -40,8 +42,9 @@ pub use self::collection::*; use std::option::Option; /// `Mailbox` represents a folder of mail. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Deserialize, Serialize, Clone, Default)] pub struct Mailbox { + #[serde(skip_serializing, skip_deserializing)] pub folder: Folder, name: String, pub collection: Collection, @@ -72,28 +75,28 @@ impl Mailbox { pub fn len(&self) -> usize { self.collection.len() } - pub fn thread_to_mail_mut(&mut self, i: usize) -> &mut Envelope { + pub fn thread_to_mail_mut(&mut self, h: ThreadHash) -> &mut Envelope { self.collection .envelopes - .entry(self.collection.threads.thread_to_mail(i)) + .entry(self.collection.threads.thread_to_mail(h)) .or_default() } - pub fn thread_to_mail(&self, i: usize) -> &Envelope { - &self.collection.envelopes[&self.collection.threads.thread_to_mail(i)] + pub fn thread_to_mail(&self, h: ThreadHash) -> &Envelope { + &self.collection.envelopes[&self.collection.threads.thread_to_mail(h)] } - pub fn threaded_mail(&self, i: usize) -> EnvelopeHash { - self.collection.threads.thread_to_mail(i) + pub fn threaded_mail(&self, h: ThreadHash) -> EnvelopeHash { + self.collection.threads.thread_to_mail(h) } pub fn mail_and_thread(&mut self, i: EnvelopeHash) -> (&mut Envelope, &ThreadNode) { let thread; { let x = &mut self.collection.envelopes.entry(i).or_default(); - thread = &self.collection.threads[x.thread()]; + thread = &self.collection.threads[&x.thread()]; } (self.collection.envelopes.entry(i).or_default(), thread) } - pub fn thread(&self, i: usize) -> &ThreadNode { - &self.collection.threads.thread_nodes()[i] + pub fn thread(&self, h: ThreadHash) -> &ThreadNode { + &self.collection.threads.thread_nodes()[&h] } pub fn insert_sent_folder(&mut self, _sent: &Mailbox) { diff --git a/melib/src/mailbox/collection.rs b/melib/src/mailbox/collection.rs index 4dd3ebc0..b53987ea 100644 --- a/melib/src/mailbox/collection.rs +++ b/melib/src/mailbox/collection.rs @@ -7,8 +7,9 @@ use std::ops::{Deref, DerefMut}; use fnv::FnvHashMap; /// `Mailbox` represents a folder of mail. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Deserialize, Default, Serialize)] pub struct Collection { + #[serde(skip_serializing, skip_deserializing)] folder: Folder, pub envelopes: FnvHashMap, date_index: BTreeMap, diff --git a/melib/src/mailbox/email.rs b/melib/src/mailbox/email.rs index ee42ae50..30306e8c 100644 --- a/melib/src/mailbox/email.rs +++ b/melib/src/mailbox/email.rs @@ -34,6 +34,7 @@ use parser::BytesExt; use super::backends::BackendOp; use crate::error::{MeliError, Result}; +use crate::mailbox::thread::ThreadHash; use std::borrow::Cow; use std::cmp::Ordering; @@ -320,7 +321,7 @@ pub struct Envelope { references: Option, timestamp: UnixTimestamp, - thread: usize, + thread: ThreadHash, hash: EnvelopeHash, @@ -355,7 +356,7 @@ impl Envelope { timestamp: 0, - thread: 0, + thread: ThreadHash::null(), hash, flags: Flag::default(), @@ -731,10 +732,10 @@ impl Envelope { None => Vec::new(), } } - pub fn thread(&self) -> usize { + pub fn thread(&self) -> ThreadHash { self.thread } - pub fn set_thread(&mut self, new_val: usize) { + pub fn set_thread(&mut self, new_val: ThreadHash) { self.thread = new_val; } pub fn set_datetime(&mut self, new_val: chrono::DateTime) { diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index ac12fb60..a4e1c47f 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -34,6 +34,7 @@ use crate::mailbox::email::parser::BytesExt; use crate::mailbox::email::*; +use uuid::Uuid; use fnv::{FnvHashMap, FnvHashSet}; use std::cell::{Ref, RefCell}; @@ -48,46 +49,71 @@ use std::str::FromStr; type Envelopes = FnvHashMap; +#[derive(PartialEq, Hash, Eq, Debug, Copy, Clone, Serialize, Deserialize, Default)] +pub struct ThreadHash(Uuid); + +impl ThreadHash { + fn new() -> Self { + ThreadHash(Uuid::new_v4()) + } + pub fn null() -> Self { + ThreadHash(Uuid::nil()) + } +} + /* Helper macros to avoid repeating ourselves */ -fn rec_change_root_parent(b: &mut Vec, idx: usize, new_root: usize) { - b[idx].thread_group = new_root; - if let Some(p) = b[idx].parent { +fn rec_change_root_parent( + b: &mut FnvHashMap, + idx: ThreadHash, + new_root: ThreadHash, +) { + let entry = b.entry(idx).or_default(); + entry.thread_group = new_root; + if let Some(p) = entry.parent { rec_change_children(b, p, new_root); rec_change_root_parent(b, p, new_root); } } -fn rec_change_children(b: &mut Vec, idx: usize, new_root: usize) { - b[idx].thread_group = new_root; +fn rec_change_children( + b: &mut FnvHashMap, + idx: ThreadHash, + new_root: ThreadHash, +) { + let entry = b.entry(idx).or_default(); + entry.thread_group = new_root; - for c in b[idx].children.clone() { + for c in entry.children.clone() { rec_change_children(b, c, new_root); } } macro_rules! remove_from_parent { - ($buf:expr, $idx:expr) => { - if let Some(p) = $buf[$idx].parent { - if let Some(pos) = $buf[p].children.iter().position(|c| *c == $idx) { - $buf[p].children.remove(pos); + ($buf:expr, $idx:expr) => {{ + let entry = $buf.entry($idx).or_default(); + if let Some(p) = entry.parent { + if let Some(pos) = $buf[&p].children.iter().position(|c| *c == $idx) { + $buf.entry(p).and_modify(|e| { + e.children.remove(pos); + }); } rec_change_root_parent($buf, p, p); } - $buf[$idx].parent = None; + $buf.entry($idx).and_modify(|e| e.parent = None); rec_change_children($buf, $idx, $idx); - $buf[$idx].thread_group = $idx; - }; + $buf.entry($idx).and_modify(|e| e.thread_group = $idx); + }}; } macro_rules! make { (($p:expr)parent of($c:expr), $buf:expr) => { remove_from_parent!($buf, $c); - if !($buf[$p]).children.contains(&$c) { - $buf[$p].children.push($c); + if !($buf[&$p]).children.contains(&$c) { + $buf.entry($p).and_modify(|e| e.children.push($c)); } else { panic!(); } - $buf[$c].parent = Some($p); + $buf.entry($c).and_modify(|e| e.parent = Some($p)); union($buf, $c, $p); }; } @@ -201,7 +227,7 @@ impl FromStr for SortOrder { #[derive(Clone, Deserialize, Serialize)] struct ThreadTree { - id: usize, + id: ThreadHash, children: Vec, } @@ -212,7 +238,7 @@ impl fmt::Debug for ThreadTree { } impl ThreadTree { - fn new(id: usize) -> Self { + fn new(id: ThreadHash) -> Self { ThreadTree { id, children: Vec::new(), @@ -241,8 +267,8 @@ pub struct ThreadsIterator<'a> { tree: Ref<'a, Vec>, } impl<'a> Iterator for ThreadsIterator<'a> { - type Item = (usize, usize, bool); - fn next(&mut self) -> Option<(usize, usize, bool)> { + type Item = (usize, ThreadHash, bool); + fn next(&mut self) -> Option<(usize, ThreadHash, bool)> { { let mut tree = &(*self.tree); for i in &self.stack { @@ -293,8 +319,8 @@ pub struct ThreadIterator<'a> { tree: Ref<'a, Vec>, } impl<'a> Iterator for ThreadIterator<'a> { - type Item = (usize, usize); - fn next(&mut self) -> Option<(usize, usize)> { + type Item = (usize, ThreadHash); + fn next(&mut self) -> Option<(usize, ThreadHash)> { { let mut tree = &(*self.tree); for i in &self.stack { @@ -324,8 +350,8 @@ impl<'a> Iterator for ThreadIterator<'a> { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ThreadNode { message: Option, - parent: Option, - children: Vec, + parent: Option, + children: Vec, date: UnixTimestamp, indentation: usize, show_subject: bool, @@ -334,7 +360,7 @@ pub struct ThreadNode { has_unseen: bool, /* Union/Find set fields */ - thread_group: usize, + thread_group: ThreadHash, rank: i32, } @@ -350,13 +376,19 @@ impl Default for ThreadNode { len: 0, has_unseen: false, - thread_group: 0, + thread_group: ThreadHash::default(), rank: 0, } } } impl ThreadNode { + fn new(thread_group: ThreadHash) -> Self { + ThreadNode { + thread_group, + ..Default::default() + } + } pub fn show_subject(&self) -> bool { self.show_subject } @@ -381,7 +413,7 @@ impl ThreadNode { self.message.is_some() } - pub fn parent(&self) -> Option { + pub fn parent(&self) -> Option { self.parent } @@ -389,7 +421,7 @@ impl ThreadNode { self.parent.is_some() } - pub fn children(&self) -> &[usize] { + pub fn children(&self) -> &[ThreadHash] { &self.children } @@ -400,11 +432,11 @@ impl ThreadNode { #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Threads { - thread_nodes: Vec, - root_set: RefCell>, + thread_nodes: FnvHashMap, + root_set: RefCell>, tree: RefCell>, - message_ids: FnvHashMap, usize>, + message_ids: FnvHashMap, ThreadHash>, pub hash_set: FnvHashSet, sort: RefCell<(SortField, SortOrder)>, subsort: RefCell<(SortField, SortOrder)>, @@ -425,8 +457,8 @@ pub struct RootIterator<'a> { } impl<'a> Iterator for RootIterator<'a> { - type Item = usize; - fn next(&mut self) -> Option { + type Item = ThreadHash; + fn next(&mut self) -> Option { { if self.pos == self.root_tree.len() { return None; @@ -436,15 +468,16 @@ impl<'a> Iterator for RootIterator<'a> { } } } -fn find(buf: &mut Vec, i: usize) -> usize { - if buf[i].thread_group == i { - return i; +fn find(buf: &mut FnvHashMap, h: ThreadHash) -> ThreadHash { + if buf[&h].thread_group == h { + return h; } - let p = buf[i].thread_group; - buf[i].thread_group = find(buf, p); - buf[i].thread_group + let p = buf[&h].thread_group; + let new_group = find(buf, p); + buf.entry(h).and_modify(|e| e.thread_group = new_group); + new_group } -fn union(buf: &mut Vec, x: usize, y: usize) -> usize { +fn union(buf: &mut FnvHashMap, x: ThreadHash, y: ThreadHash) -> ThreadHash { let mut x_root = find(buf, x); let mut y_root = find(buf, y); @@ -453,24 +486,26 @@ fn union(buf: &mut Vec, x: usize, y: usize) -> usize { return x_root; } - if buf[x_root].rank < buf[y_root].rank { + if buf[&x_root].rank < buf[&y_root].rank { mem::swap(&mut x_root, &mut y_root); } // x and y are not in same set, so we merge them // - buf[y_root].thread_group = x_root; - if buf[x_root].rank == buf[y_root].rank { - buf[x_root].rank += 1; + buf.entry(y_root).and_modify(|e| e.thread_group = x_root); + if buf[&x_root].rank == buf[&y_root].rank { + buf.entry(x_root).and_modify(|e| { + e.rank += 1; + }); } x_root } impl Threads { - fn find(&mut self, i: usize) -> usize { + fn find(&mut self, i: ThreadHash) -> ThreadHash { find(&mut self.thread_nodes, i) } - fn union(&mut self, x: usize, y: usize) -> usize { + fn union(&mut self, x: ThreadHash, y: ThreadHash) -> ThreadHash { let mut x_root = self.find(x); let mut y_root = self.find(y); @@ -479,47 +514,51 @@ impl Threads { return x_root; } - if self.thread_nodes[x_root].rank < self.thread_nodes[y_root].rank { + if self.thread_nodes[&x_root].rank < self.thread_nodes[&y_root].rank { mem::swap(&mut x_root, &mut y_root); } // x and y are not in same set, so we merge them // - self.thread_nodes[y_root].thread_group = x_root; - if self.thread_nodes[x_root].rank == self.thread_nodes[y_root].rank { - self.thread_nodes[x_root].rank += 1; + self.thread_nodes + .entry(y_root) + .and_modify(|e| e.thread_group = x_root); + if self.thread_nodes[&x_root].rank == self.thread_nodes[&y_root].rank { + self.thread_nodes.entry(x_root).and_modify(|e| e.rank += 1); } x_root } - fn prune_empty_nodes(&mut self, root_set: &mut Vec) { + fn prune_empty_nodes(&mut self, root_set: &mut Vec) { fn prune( - thread_nodes: &mut Vec, - idx: usize, - root_set: &mut Vec, + thread_nodes: &mut FnvHashMap, + idx: ThreadHash, + root_set: &mut Vec, ) -> bool { /* "If it is an empty container with no children, nuke it." */ - if !thread_nodes[idx].has_message() && thread_nodes[idx].children.is_empty() { + if !thread_nodes[&idx].has_message() && thread_nodes[&idx].children.is_empty() { remove_from_parent!(thread_nodes, idx); return true; } - if !thread_nodes[idx].has_message() && !thread_nodes[idx].has_parent() { - if thread_nodes[idx].children.len() == 1 { + if !thread_nodes[&idx].has_message() && !thread_nodes[&idx].has_parent() { + if thread_nodes[&idx].children.len() == 1 { /* "Do not promote the children if doing so would promote them to the root set * -- unless there is only one child, in which case, do." */ - let child = thread_nodes[idx].children[0]; + let child = thread_nodes[&idx].children[0]; root_set.push(child); remove_from_parent!(thread_nodes, child); return true; // Pruned } - } else if let Some(p) = thread_nodes[idx].parent { - if !thread_nodes[idx].has_message() { - let orphans = thread_nodes[idx].children.clone(); + } else if let Some(p) = thread_nodes[&idx].parent { + if !thread_nodes[&idx].has_message() { + let orphans = thread_nodes[&idx].children.clone(); for c in orphans { make!((p) parent of (c), thread_nodes); } remove_from_parent!(thread_nodes, idx); - thread_nodes[idx].children.clear(); + thread_nodes.entry(idx).and_modify(|e| { + e.children.clear(); + }); return true; // Pruned } } @@ -528,15 +567,15 @@ impl Threads { */ let mut c_idx = 0; loop { - if c_idx == thread_nodes[idx].children.len() { + if c_idx == thread_nodes[&idx].children.len() { break; } - let c = thread_nodes[idx].children[c_idx]; + let c = thread_nodes[&idx].children[c_idx]; if !prune(thread_nodes, c, root_set) { c_idx += 1; } } - !thread_nodes[idx].has_message() && thread_nodes[idx].children.is_empty() + !thread_nodes[&idx].has_message() && thread_nodes[&idx].children.is_empty() } let mut idx = 0; @@ -556,10 +595,12 @@ impl Threads { /* To reconstruct thread information from the mails we need: */ /* a vector to hold thread members */ - let thread_nodes: Vec = - Vec::with_capacity((collection.len() as f64 * 1.2) as usize); + let thread_nodes: FnvHashMap = FnvHashMap::with_capacity_and_hasher( + (collection.len() as f64 * 1.2) as usize, + Default::default(), + ); /* A hash table of Message IDs */ - let message_ids: FnvHashMap, usize> = + let message_ids: FnvHashMap, ThreadHash> = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); let hash_set: FnvHashSet = FnvHashSet::with_capacity_and_hasher(collection.len(), Default::default()); @@ -616,11 +657,11 @@ impl Threads { fn create_root_set(&mut self, collection: &Envelopes) { /* Walk over the elements of message_ids, and gather a list of the ThreadNode objects that * have no parents. These are the root messages of each thread */ - let mut root_set: Vec = Vec::with_capacity(collection.len()); + let mut root_set: Vec = Vec::with_capacity(collection.len()); /* Find the root set */ for v in self.message_ids.values() { - if self.thread_nodes[*v].parent.is_none() { + if self.thread_nodes[v].parent.is_none() { root_set.push(*v); } } @@ -635,22 +676,22 @@ 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, (bool, usize)> = + let mut subject_table: FnvHashMap, (bool, ThreadHash)> = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); for &r in root_set.iter() { /* "Find the subject of that sub-tree": */ - let (mut subject, mut is_re): (_, bool) = if self.thread_nodes[r].message.is_some() { + let (mut subject, mut is_re): (_, bool) = if self.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 = self.thread_nodes[r].message.unwrap(); + let msg_idx = self.thread_nodes[&r].message.unwrap(); let envelope = &collection[&msg_idx]; (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 * that message instead." */ - let msg_idx = self.thread_nodes[self.thread_nodes[r].children[0]] + let msg_idx = self.thread_nodes[&self.thread_nodes[&r].children[0]] .message .unwrap(); let envelope = &collection[&msg_idx]; @@ -677,7 +718,7 @@ impl Threads { * "The container in the table has a ``Re:'' version of this subject, and this * container has a non-``Re:'' version of this subject. The non-re version is the * more interesting of the two." */ - if (!self.thread_nodes[id].has_message() && self.thread_nodes[r].has_message()) + if (!self.thread_nodes[&id].has_message() && self.thread_nodes[&r].has_message()) || (other_is_re && !is_re) { mem::replace( @@ -697,12 +738,12 @@ impl Threads { let r = root_set[i]; /* "Find the subject of this Container (as above.)" */ - let (mut subject, mut is_re): (_, bool) = if self.thread_nodes[r].message.is_some() { - let msg_idx = self.thread_nodes[r].message.unwrap(); + let (mut subject, mut is_re): (_, bool) = if self.thread_nodes[&r].message.is_some() { + let msg_idx = self.thread_nodes[&r].message.unwrap(); let envelope = &collection[&msg_idx]; (envelope.subject(), !envelope.references().is_empty()) } else { - let msg_idx = self.thread_nodes[self.thread_nodes[r].children[0]] + let msg_idx = self.thread_nodes[&self.thread_nodes[&r].children[0]] .message .unwrap(); let envelope = &collection[&msg_idx]; @@ -718,7 +759,7 @@ impl Threads { let (other_is_re, other_idx) = subject_table[subject]; /* "If it is null, or if it is this container, continue." */ - if !self.thread_nodes[other_idx].has_message() || other_idx == r { + if !self.thread_nodes[&other_idx].has_message() || other_idx == r { continue; } @@ -729,8 +770,9 @@ impl Threads { * "If both are dummies, append one's children to the other, and remove the now-empty * container." */ - if !self.thread_nodes[r].has_message() && !self.thread_nodes[other_idx].has_message() { - let children = self.thread_nodes[r].children.clone(); + if !self.thread_nodes[&r].has_message() && !self.thread_nodes[&other_idx].has_message() + { + let children = self.thread_nodes[&r].children.clone(); for c in children { make!((other_idx) parent of (c), &mut self.thread_nodes); } @@ -740,16 +782,16 @@ impl Threads { * of the empty, and a sibling of the other ``real'' messages with the same subject * (the empty's children.)" */ - } else if self.thread_nodes[r].has_message() - && !self.thread_nodes[other_idx].has_message() + } else if self.thread_nodes[&r].has_message() + && !self.thread_nodes[&other_idx].has_message() { make!((other_idx) parent of (r), &mut self.thread_nodes); if !root_set.contains(&other_idx) { root_set.push(other_idx); } roots_to_remove.push(i); - } else if !self.thread_nodes[r].has_message() - && self.thread_nodes[other_idx].has_message() + } else if !self.thread_nodes[&r].has_message() + && self.thread_nodes[&other_idx].has_message() { make!((r) parent of (other_idx), &mut self.thread_nodes); if let Some(pos) = root_set.iter().position(|&i| i == other_idx) { @@ -759,7 +801,7 @@ impl Threads { * "If that container is a non-empty, and that message's subject does not begin with ``Re:'', but this * message's subject does, then make this be a child of the other." */ - } else if self.thread_nodes[other_idx].has_message() && !other_is_re && is_re { + } else if self.thread_nodes[&other_idx].has_message() && !other_is_re && is_re { make!((other_idx) parent of (r), &mut self.thread_nodes); roots_to_remove.push(i); @@ -769,7 +811,7 @@ impl Threads { * without will be in the hash table, regardless of the order in which they were * seen.)" */ - } else if self.thread_nodes[other_idx].has_message() && other_is_re && !is_re { + } else if self.thread_nodes[&other_idx].has_message() && other_is_re && !is_re { make!((r) parent of (other_idx), &mut self.thread_nodes); if let Some(pos) = root_set.iter().position(|r| *r == other_idx) { roots_to_remove.push(pos); @@ -780,9 +822,8 @@ impl Threads { * hierarchical relationship which might not be true." */ } else { - self.thread_nodes.push(Default::default()); - let new_id = self.thread_nodes.len() - 1; - self.thread_nodes[new_id].thread_group = new_id; + let new_id = ThreadHash::new(); + self.thread_nodes.insert(new_id, ThreadNode::new(new_id)); make!((new_id) parent of (r), &mut self.thread_nodes); make!((new_id) parent of (other_idx), &mut self.thread_nodes); root_set[i] = new_id; @@ -827,20 +868,19 @@ impl Threads { * - hash_set * - message fields in thread_nodes */ - let idx = if let Some((idx, node)) = self + let thread_hash = if let Some((key, node)) = self .thread_nodes .iter_mut() - .enumerate() .find(|(_, n)| n.message.map(|n| n == old_hash).unwrap_or(false)) { node.message = Some(new_hash); - idx + *key } else { return Err(()); }; self.hash_set.remove(&old_hash); self.hash_set.insert(new_hash); - self.rebuild_thread(idx, collection); + self.rebuild_thread(thread_hash, collection); Ok(()) } @@ -856,15 +896,15 @@ impl Threads { // debug!("DEBUG: {} in threads is idx= {}", envelope_hash, pos); //} - let t_id: usize; + let t_id: ThreadHash; { - if let Some(pos) = self + if let Some((pos, n)) = self .thread_nodes - .iter() - .position(|n| n.message.map(|n| n == envelope_hash).unwrap_or(false)) + .iter_mut() + .find(|(_, n)| n.message.map(|n| n == envelope_hash).unwrap_or(false)) { - t_id = pos; - self.thread_nodes[pos].message = None; + t_id = *pos; + n.message = None; } else { /* else it was deleted during a thread_rebuild or others */ return; @@ -874,7 +914,7 @@ impl Threads { let mut node_idx = t_id; /* Trace path back to root ThreadNode */ - while let Some(p) = &self.thread_nodes[node_idx].parent { + while let Some(p) = &self.thread_nodes[&node_idx].parent { node_idx = *p; } { @@ -895,7 +935,7 @@ impl Threads { } } - let mut root_set: Vec = self.tree.borrow().iter().map(|t| t.id).collect(); + let mut root_set: Vec = self.tree.borrow().iter().map(|t| t.id).collect(); self.prune_empty_nodes(&mut root_set); self.tree.borrow_mut().retain(|t| root_set.contains(&t.id)); } @@ -922,7 +962,7 @@ impl Threads { } self.create_root_set(collection); - let mut root_set: Vec = self.tree.borrow().iter().map(|t| t.id).collect(); + let mut root_set: Vec = self.tree.borrow().iter().map(|t| t.id).collect(); self.prune_empty_nodes(&mut root_set); let tree = self.tree.get_mut(); tree.retain(|t| root_set.contains(&t.id)); @@ -966,11 +1006,11 @@ impl Threads { } /* Update thread tree information on envelope insertion */ - fn rebuild_thread(&mut self, id: usize, collection: &Envelopes) { + fn rebuild_thread(&mut self, id: ThreadHash, collection: &Envelopes) { let mut node_idx = id; let mut stack = Vec::with_capacity(32); - let no_parent: bool = if let Some(node) = self.thread_nodes.get(node_idx) { + let no_parent: bool = if let Some(node) = self.thread_nodes.get(&node_idx) { node.parent.is_none() } else { false @@ -987,7 +1027,7 @@ impl Threads { } /* Trace path back to root ThreadNode */ - while let Some(p) = &self.thread_nodes[node_idx].parent { + while let Some(p) = &self.thread_nodes[&node_idx].parent { node_idx = *p; stack.push(node_idx); } @@ -1053,18 +1093,18 @@ impl Threads { for t in tree.iter_mut() { t.children.sort_by(|a, b| match subsort { (SortField::Date, SortOrder::Desc) => { - let a = &self.thread_nodes[a.id]; - let b = &self.thread_nodes[b.id]; + let a = &self.thread_nodes[&a.id]; + let b = &self.thread_nodes[&b.id]; b.date.cmp(&a.date) } (SortField::Date, SortOrder::Asc) => { - let a = &self.thread_nodes[a.id]; - let b = &self.thread_nodes[b.id]; + let a = &self.thread_nodes[&a.id]; + let b = &self.thread_nodes[&b.id]; a.date.cmp(&b.date) } (SortField::Subject, SortOrder::Desc) => { - let a = &self.thread_nodes[a.id].message(); - let b = &self.thread_nodes[b.id].message(); + let a = &self.thread_nodes[&a.id].message(); + let b = &self.thread_nodes[&b.id].message(); if a.is_none() || b.is_none() { return Ordering::Equal; @@ -1074,8 +1114,8 @@ impl Threads { ma.subject().cmp(&mb.subject()) } (SortField::Subject, SortOrder::Asc) => { - let a = &self.thread_nodes[a.id].message(); - let b = &self.thread_nodes[b.id].message(); + let a = &self.thread_nodes[&a.id].message(); + let b = &self.thread_nodes[&b.id].message(); if a.is_none() || b.is_none() { return Ordering::Equal; @@ -1092,18 +1132,18 @@ impl Threads { let tree = &mut self.tree.borrow_mut(); tree.sort_by(|a, b| match sort { (SortField::Date, SortOrder::Desc) => { - let a = &self.thread_nodes[a.id]; - let b = &self.thread_nodes[b.id]; + let a = &self.thread_nodes[&a.id]; + let b = &self.thread_nodes[&b.id]; b.date.cmp(&a.date) } (SortField::Date, SortOrder::Asc) => { - let a = &self.thread_nodes[a.id]; - let b = &self.thread_nodes[b.id]; + let a = &self.thread_nodes[&a.id]; + let b = &self.thread_nodes[&b.id]; a.date.cmp(&b.date) } (SortField::Subject, SortOrder::Desc) => { - let a = &self.thread_nodes[a.id].message(); - let b = &self.thread_nodes[b.id].message(); + let a = &self.thread_nodes[&a.id].message(); + let b = &self.thread_nodes[&b.id].message(); if a.is_none() || b.is_none() { return Ordering::Equal; @@ -1113,8 +1153,8 @@ impl Threads { ma.subject().cmp(&mb.subject()) } (SortField::Subject, SortOrder::Asc) => { - let a = &self.thread_nodes[a.id].message(); - let b = &self.thread_nodes[b.id].message(); + let a = &self.thread_nodes[&a.id].message(); + let b = &self.thread_nodes[&b.id].message(); if a.is_none() || b.is_none() { return Ordering::Equal; @@ -1142,12 +1182,12 @@ impl Threads { } } - pub fn thread_to_mail(&self, i: usize) -> EnvelopeHash { - let thread = &self.thread_nodes[i]; + pub fn thread_to_mail(&self, i: ThreadHash) -> EnvelopeHash { + let thread = &self.thread_nodes[&i]; thread.message().unwrap() } - pub fn thread_nodes(&self) -> &Vec { + pub fn thread_nodes(&self) -> &FnvHashMap { &self.thread_nodes } @@ -1159,7 +1199,7 @@ impl Threads { self.tree.borrow().len() } - pub fn root_set(&self, idx: usize) -> usize { + pub fn root_set(&self, idx: usize) -> ThreadHash { self.tree.borrow()[idx].id } @@ -1170,15 +1210,15 @@ impl Threads { } } - pub fn has_sibling(&self, i: usize) -> bool { - if let Some(parent) = self[i].parent { - let children = &self[parent].children; + pub fn has_sibling(&self, h: ThreadHash) -> bool { + if let Some(parent) = self[&h].parent { + let children = &self[&parent].children; if children.is_empty() { return false; } let pos = children .iter() - .position(|&x| x == i) + .position(|&x| x == h) .expect("Did not find node in parent!"); pos != children.len() - 1 } else { @@ -1187,7 +1227,7 @@ impl Threads { } fn link_envelope(&mut self, envelope: &mut Envelope) { - let t_idx: usize = { + let t_idx: ThreadHash = { let m_id = envelope.message_id().raw(); /* t_idx: The index of this message's ThreadNode in thread_nodes @@ -1203,28 +1243,31 @@ impl Threads { /* 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() { + 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 { + /* The new thread node's set is just itself */ + let new_id = ThreadHash::new(); + let node = ThreadNode { message: Some(envelope.hash()), date: envelope.date(), + thread_group: new_id, ..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.thread_nodes.insert(new_id, node); self.message_ids.insert(m_id.to_vec(), new_id); new_id } }; - self.thread_nodes[t_idx].date = envelope.date(); - self.thread_nodes[t_idx].message = Some(envelope.hash()); - self.thread_nodes[t_idx].has_unseen |= !envelope.is_seen(); + self.thread_nodes.entry(t_idx).and_modify(|e| { + e.date = envelope.date(); + e.message = Some(envelope.hash()); + e.has_unseen |= !envelope.is_seen(); + }); envelope.set_thread(t_idx); self.hash_set.insert(envelope.hash()); @@ -1247,17 +1290,20 @@ impl Threads { self.message_ids[r_id] } else { /* Create a new ThreadNode object holding this reference */ - self.thread_nodes.push(ThreadNode { - ..Default::default() - }); - let new_id = self.thread_nodes.len() - 1; - self.thread_nodes[new_id].thread_group = new_id; + let new_id = ThreadHash::new(); + self.thread_nodes.insert( + new_id, + ThreadNode { + thread_group: new_id, + ..Default::default() + }, + ); self.message_ids.insert(r_id.to_vec(), new_id); new_id }; /* If they are already linked, don't change the existing links. */ - if self.thread_nodes[ref_ptr].has_parent() - && self.thread_nodes[ref_ptr].parent.unwrap() != parent_id + if self.thread_nodes[&ref_ptr].has_parent() + && self.thread_nodes[&ref_ptr].parent.unwrap() != parent_id { ref_ptr = parent_id; continue; @@ -1283,10 +1329,10 @@ impl Threads { } } -impl Index for Threads { +impl Index<&ThreadHash> for Threads { type Output = ThreadNode; - fn index(&self, index: usize) -> &ThreadNode { + fn index(&self, index: &ThreadHash) -> &ThreadNode { self.thread_nodes .get(index) .expect("thread index out of bounds") @@ -1295,20 +1341,20 @@ impl Index for Threads { fn node_build( tree: &mut ThreadTree, - idx: usize, - thread_nodes: &mut Vec, + idx: ThreadHash, + thread_nodes: &mut FnvHashMap, indentation: usize, collection: &Envelopes, ) { - if let Some(hash) = thread_nodes[idx].message { + if let Some(hash) = thread_nodes[&idx].message { if !collection.contains_key(&hash) { /* invalidate node */ - // thread_nodes[idx].message = None; - } else if let Some(parent_id) = thread_nodes[idx].parent { - if let Some(parent_hash) = thread_nodes[parent_id].message { + // thread_nodes[&idx].message = None; + } else if let Some(parent_id) = thread_nodes[&idx].parent { + if let Some(parent_hash) = thread_nodes[&parent_id].message { if !collection.contains_key(&parent_hash) { /* invalidate node */ - // thread_nodes[parent_id].message = None; + // thread_nodes[&parent_id].message = None; } else { /* decide if the subject should be shown in the UI. * If parent subject is Foobar and reply is `Re: Foobar` @@ -1321,15 +1367,19 @@ fn node_build( let mut parent_subject = parent_subject.to_mut().as_bytes(); parent_subject.strip_prefixes(); if subject == parent_subject { - thread_nodes[idx].show_subject = false; + thread_nodes.entry(idx).and_modify(|e| { + e.show_subject = false; + }); } } } } } - let indentation = if thread_nodes[idx].has_message() { - thread_nodes[idx].indentation = indentation; + let indentation = if thread_nodes[&idx].has_message() { + thread_nodes + .entry(idx) + .and_modify(|e| e.indentation = indentation); indentation + 1 } else if indentation > 0 { indentation @@ -1337,28 +1387,35 @@ fn node_build( indentation + 1 }; - let mut has_unseen = if let Some(msg) = thread_nodes[idx].message { + let mut has_unseen = if let Some(msg) = thread_nodes[&idx].message { !collection[&msg].is_seen() } else { false }; let mut child_vec: Vec = Vec::new(); - thread_nodes[idx].len = thread_nodes[idx].children.len(); + thread_nodes + .entry(idx) + .and_modify(|e| e.len = e.children.len()); /* No child/parent relationship is mutated at any point and no nodes are added or removed. Only * each node's fields change, so the following is safe. */ - let children = &thread_nodes[idx].children as *const Vec; + let children = &thread_nodes[&idx].children as *const Vec; for &c in unsafe { &(*children) } { let mut new_tree = ThreadTree::new(c); node_build(&mut new_tree, c, thread_nodes, indentation, collection); - thread_nodes[idx].len += thread_nodes[c].len; - thread_nodes[idx].date = cmp::max(thread_nodes[idx].date, thread_nodes[c].date); + let _c = (thread_nodes[&c].len, thread_nodes[&c].date); + thread_nodes.entry(idx).and_modify(|e| { + e.len += _c.0; + e.date = cmp::max(e.date, _c.1); + }); - has_unseen |= thread_nodes[c].has_unseen; + has_unseen |= thread_nodes[&c].has_unseen; child_vec.push(new_tree); } tree.children = child_vec; - thread_nodes[idx].has_unseen = has_unseen; + thread_nodes.entry(idx).and_modify(|e| { + e.has_unseen = has_unseen; + }); } diff --git a/ui/src/components/mail.rs b/ui/src/components/mail.rs index 29281e85..e706982c 100644 --- a/ui/src/components/mail.rs +++ b/ui/src/components/mail.rs @@ -24,6 +24,7 @@ use super::*; use melib::backends::Folder; use melib::backends::FolderHash; +use melib::thread::ThreadHash; pub mod listing; pub use listing::*; diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 9e6bb3d1..554fc896 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -127,14 +127,14 @@ impl Composer { * msg: index of message we reply to in thread_nodes * context: current context */ - pub fn edit(coordinates: (usize, usize, usize), msg: usize, context: &Context) -> Self { + pub fn edit(coordinates: (usize, usize, usize), msg: ThreadHash, context: &Context) -> Self { let mailbox = &context.accounts[coordinates.0][coordinates.1] .as_ref() .unwrap(); let threads = &mailbox.collection.threads; let thread_nodes = &threads.thread_nodes(); let mut ret = Composer::default(); - let message = &mailbox.collection[&thread_nodes[msg].message().unwrap()]; + let message = &mailbox.collection[&thread_nodes[&msg].message().unwrap()]; let op = context.accounts[coordinates.0] .backend .operation(message.hash(), mailbox.folder.hash()); @@ -144,14 +144,18 @@ impl Composer { ret.account_cursor = coordinates.0; ret } - pub fn with_context(coordinates: (usize, usize, usize), msg: usize, context: &Context) -> Self { + pub fn with_context( + coordinates: (usize, usize, usize), + msg: ThreadHash, + context: &Context, + ) -> Self { let mailbox = &context.accounts[coordinates.0][coordinates.1] .as_ref() .unwrap(); let threads = &mailbox.collection.threads; let thread_nodes = &threads.thread_nodes(); let mut ret = Composer::default(); - let p = &thread_nodes[msg]; + let p = &thread_nodes[&msg]; let parent_message = &mailbox.collection[&p.message().unwrap()]; let mut op = context.accounts[coordinates.0] .backend diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 53d7ceff..39978e03 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -207,15 +207,15 @@ impl MailboxView { threads.sort_by(self.sort, self.subsort, &mailbox.collection); for (idx, root_idx) in threads.root_iter().enumerate() { - let thread_node = &threads.thread_nodes()[root_idx]; + let thread_node = &threads.thread_nodes()[&root_idx]; let i = if let Some(i) = thread_node.message() { i } else { let mut iter_ptr = thread_node.children()[0]; - while threads.thread_nodes()[iter_ptr].message().is_none() { - iter_ptr = threads.thread_nodes()[iter_ptr].children()[0]; + while threads.thread_nodes()[&iter_ptr].message().is_none() { + iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; } - threads.thread_nodes()[iter_ptr].message().unwrap() + threads.thread_nodes()[&iter_ptr].message().unwrap() }; if !mailbox.collection.contains_key(&i) { debug!("key = {}", i); @@ -251,15 +251,15 @@ impl MailboxView { }; for ((idx, root_idx), strings) in threads.root_iter().enumerate().zip(rows) { - let thread_node = &threads.thread_nodes()[root_idx]; + let thread_node = &threads.thread_nodes()[&root_idx]; let i = if let Some(i) = thread_node.message() { i } else { let mut iter_ptr = thread_node.children()[0]; - while threads.thread_nodes()[iter_ptr].message().is_none() { - iter_ptr = threads.thread_nodes()[iter_ptr].children()[0]; + while threads.thread_nodes()[&iter_ptr].message().is_none() { + iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; } - threads.thread_nodes()[iter_ptr].message().unwrap() + threads.thread_nodes()[&iter_ptr].message().unwrap() }; if !mailbox.collection.contains_key(&i) { debug!("key = {}", i); @@ -354,15 +354,15 @@ impl MailboxView { } let threads = &mailbox.collection.threads; let thread_node = threads.root_set(idx); - let thread_node = &threads.thread_nodes()[thread_node]; + let thread_node = &threads.thread_nodes()[&thread_node]; let i = if let Some(i) = thread_node.message() { i } else { let mut iter_ptr = thread_node.children()[0]; - while threads.thread_nodes()[iter_ptr].message().is_none() { - iter_ptr = threads.thread_nodes()[iter_ptr].children()[0]; + while threads.thread_nodes()[&iter_ptr].message().is_none() { + iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; } - threads.thread_nodes()[iter_ptr].message().unwrap() + threads.thread_nodes()[&iter_ptr].message().unwrap() }; let root_envelope: &Envelope = &mailbox.collection[&i]; diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs index 6bc19484..0501751f 100644 --- a/ui/src/components/mail/listing/thread.rs +++ b/ui/src/components/mail/listing/thread.rs @@ -151,12 +151,12 @@ impl ThreadListing { /* Draw threaded view. */ let threads = &mailbox.collection.threads; threads.sort_by(self.sort, self.subsort, &mailbox.collection); - let thread_nodes: &Vec = &threads.thread_nodes(); + let thread_nodes: &FnvHashMap = &threads.thread_nodes(); let mut iter = threads.threads_iter().peekable(); /* This is just a desugared for loop so that we can use .peek() */ let mut idx = 0; - while let Some((indentation, i, has_sibling)) = iter.next() { - let thread_node = &thread_nodes[i]; + while let Some((indentation, thread_hash, has_sibling)) = iter.next() { + let thread_node = &thread_nodes[&thread_hash]; if indentation == 0 { thread_idx += 1; @@ -181,7 +181,7 @@ impl ThreadListing { envelope, idx, indentation, - i, + thread_hash, threads, &indentations, has_sibling, @@ -362,13 +362,13 @@ impl ThreadListing { envelope: &Envelope, idx: usize, indent: usize, - node_idx: usize, + node_idx: ThreadHash, threads: &Threads, indentations: &[bool], has_sibling: bool, //op: Box, ) -> String { - let thread_node = &threads[node_idx]; + let thread_node = &threads[&node_idx]; let has_parent = thread_node.has_parent(); let show_subject = thread_node.show_subject(); diff --git a/ui/src/components/mail/view/thread.rs b/ui/src/components/mail/view/thread.rs index 31ab4abd..3f0099c0 100644 --- a/ui/src/components/mail/view/thread.rs +++ b/ui/src/components/mail/view/thread.rs @@ -24,7 +24,7 @@ use std::cmp; #[derive(Debug, Clone)] struct ThreadEntry { - index: (usize, usize, usize), + index: (usize, ThreadHash, usize), /// (indentation, thread_node index, line number in listing) indentation: usize, msg_hash: EnvelopeHash, @@ -57,13 +57,13 @@ impl ThreadView { const DESCRIPTION: &'static str = "thread view"; /* * coordinates: (account index, mailbox index, root set thread_node index) - * expanded_idx: optional position of expanded entry when we render the threadview. Default + * expanded_hash: optional position of expanded entry when we render the threadview. Default * expanded message is the last one. * context: current context */ pub fn new( coordinates: (usize, usize, usize), - expanded_idx: Option, + expanded_hash: Option, context: &Context, ) -> Self { let mut view = ThreadView { @@ -79,7 +79,7 @@ impl ThreadView { id: ComponentId::new_v4(), ..Default::default() }; - view.initiate(expanded_idx, context); + view.initiate(expanded_hash, context); view.new_cursor_pos = view.new_expanded_pos; view } @@ -102,8 +102,8 @@ impl ThreadView { None }; - let expanded_pos = self.expanded_pos; - self.initiate(Some(expanded_pos), context); + let expanded_hash = old_expanded_entry.as_ref().map(|e| e.index.1); + self.initiate(expanded_hash, context); let mut old_cursor = 0; let mut new_cursor = 0; @@ -144,7 +144,7 @@ impl ThreadView { } self.set_dirty(); } - fn initiate(&mut self, expanded_idx: Option, context: &Context) { + fn initiate(&mut self, expanded_hash: Option, context: &Context) { /* stack to push thread messages in order in order to pop and print them later */ let mailbox = &context.accounts[self.coordinates.0][self.coordinates.1] .as_ref() @@ -153,23 +153,23 @@ impl ThreadView { let thread_iter = threads.thread_iter(self.coordinates.2); self.entries.clear(); - for (line, (ind, idx)) in thread_iter.enumerate() { - let entry = if let Some(msg_hash) = threads.thread_nodes()[idx].message() { + for (line, (ind, thread_hash)) in thread_iter.enumerate() { + let entry = if let Some(msg_hash) = threads.thread_nodes()[&thread_hash].message() { let seen: bool = mailbox.collection[&msg_hash].is_seen(); - self.make_entry((ind, idx, line), msg_hash, seen) + self.make_entry((ind, thread_hash, line), msg_hash, seen) } else { continue; }; self.entries.push(entry); - match expanded_idx { - Some(expanded_idx) if expanded_idx == idx => { + match expanded_hash { + Some(expanded_hash) if expanded_hash == thread_hash => { self.new_expanded_pos = self.entries.len().saturating_sub(1); self.expanded_pos = self.new_expanded_pos + 1; } _ => {} } } - if expanded_idx.is_none() { + if expanded_hash.is_none() { self.new_expanded_pos = self.entries.len().saturating_sub(1); self.expanded_pos = self.new_expanded_pos + 1; } @@ -181,7 +181,7 @@ impl ThreadView { Vec::with_capacity(self.entries.len()); for e in &mut self.entries { let envelope: &Envelope = &mailbox.collection[&e.msg_hash]; - let thread_node = &threads.thread_nodes()[e.index.1]; + let thread_node = &threads.thread_nodes()[&e.index.1]; let string = if thread_node.show_subject() { let subject = envelope.subject(); highlight_reply_subjects.push(Some(subject.grapheme_width())); @@ -324,7 +324,7 @@ impl ThreadView { fn make_entry( &mut self, - i: (usize, usize, usize), + i: (usize, ThreadHash, usize), msg_hash: EnvelopeHash, seen: bool, ) -> ThreadEntry { @@ -535,11 +535,11 @@ impl ThreadView { .as_ref() .unwrap(); let threads = &mailbox.collection.threads; - let thread_node = &threads.thread_nodes()[threads.root_set(self.coordinates.2)]; + let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; let i = if let Some(i) = thread_node.message() { i } else { - threads.thread_nodes()[thread_node.children()[0]] + threads.thread_nodes()[&thread_node.children()[0]] .message() .unwrap() }; @@ -615,15 +615,15 @@ impl ThreadView { .as_ref() .unwrap(); let threads = &mailbox.collection.threads; - let thread_node = &threads.thread_nodes()[threads.root_set(self.coordinates.2)]; + let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; let i = if let Some(i) = thread_node.message() { i } else { let mut iter_ptr = thread_node.children()[0]; - while threads.thread_nodes()[iter_ptr].message().is_none() { - iter_ptr = threads.thread_nodes()[iter_ptr].children()[0]; + while threads.thread_nodes()[&iter_ptr].message().is_none() { + iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; } - threads.thread_nodes()[iter_ptr].message().unwrap() + threads.thread_nodes()[&iter_ptr].message().unwrap() }; let envelope: &Envelope = &mailbox.collection[&i]; @@ -835,11 +835,12 @@ impl Component for ThreadView { .as_ref() .unwrap(); let threads = &mailbox.collection.threads; - let thread_node = &threads.thread_nodes()[threads.root_set(self.coordinates.2)]; + let thread_node = + &threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; let i = if let Some(i) = thread_node.message() { i } else { - threads.thread_nodes()[thread_node.children()[0]] + threads.thread_nodes()[&thread_node.children()[0]] .message() .unwrap() }; @@ -890,8 +891,8 @@ impl Component for ThreadView { } UIEvent::Input(Key::Ctrl('r')) => { self.reversed = !self.reversed; - let expanded_pos = self.expanded_pos; - self.initiate(Some(expanded_pos), context); + let expanded_hash = self.entries[self.expanded_pos].index.1; + self.initiate(Some(expanded_hash), context); self.initiated = false; self.dirty = true; return true; diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index 1a755f6f..a927193d 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -92,6 +92,17 @@ impl Drop for Account { let writer = io::BufWriter::new(f); serde_json::to_writer(writer, &self.address_book).unwrap(); }; + if let Ok(data) = data_dir.place_data_file("mailbox") { + /* place result in cache directory */ + let f = match fs::File::create(data) { + Ok(f) => f, + Err(e) => { + panic!("{}", e); + } + }; + let writer = io::BufWriter::new(f); + bincode::serialize_into(writer, &self.folders).unwrap(); + }; } } @@ -125,7 +136,7 @@ impl<'a> Iterator for MailboxIterator<'a> { } } -#[derive(Debug, Default)] +#[derive(Serialize, Debug, Default)] struct FolderNode { hash: FolderHash, kids: Vec, diff --git a/ui/src/conf/mailer.rs b/ui/src/conf/mailer.rs index e555746f..56fc6641 100644 --- a/ui/src/conf/mailer.rs +++ b/ui/src/conf/mailer.rs @@ -20,7 +20,7 @@ */ /// Settings for the mailer function. -#[derive(Debug, Deserialize, Clone, Default)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct MailerSettings { /// A command to pipe new emails to /// Required diff --git a/ui/src/conf/pager.rs b/ui/src/conf/pager.rs index 59eaa2fc..1aa822f5 100644 --- a/ui/src/conf/pager.rs +++ b/ui/src/conf/pager.rs @@ -21,7 +21,7 @@ use super::default_vals::*; /// Settings for the pager function. -#[derive(Debug, Deserialize, Clone, Default)] +#[derive(Debug, Deserialize, Clone, Default, Serialize)] pub struct PagerSettings { /// Number of context lines when going to next page. /// Default: 0 diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index a84232b8..5402c846 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -25,6 +25,7 @@ use components::Component; pub use melib::mailbox::{SortField, SortOrder}; +use melib::thread::ThreadHash; extern crate uuid; use uuid::Uuid; @@ -40,9 +41,9 @@ pub enum ListingAction { pub enum TabAction { TabOpen(Option>), NewDraft(usize), - Reply((usize, usize, usize), usize), // thread coordinates (account, mailbox, root_set idx) and message idx + Reply((usize, usize, usize), ThreadHash), // thread coordinates (account, mailbox, root_set idx) and thread hash Close, - Edit((usize, usize, usize), usize), // thread coordinates (account, mailbox, root_set idx) and message idx + Edit((usize, usize, usize), ThreadHash), // thread coordinates (account, mailbox, root_set idx) and thread hash Kill(Uuid), } -- cgit v1.2.3