parent
4582bcd5ae
commit
22d868f499
|
@ -19,7 +19,7 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Serialize, Default, Clone)]
|
||||
pub struct AccountSettings {
|
||||
pub name: String,
|
||||
pub root_folder: String,
|
||||
|
|
|
@ -35,7 +35,7 @@ use nom;
|
|||
|
||||
pub type Result<T> = result::Result<T, MeliError>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct MeliError {
|
||||
details: String,
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<EnvelopeHash, Envelope>,
|
||||
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
||||
|
|
|
@ -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<References>,
|
||||
|
||||
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<chrono::FixedOffset>) {
|
||||
|
|
|
@ -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<EnvelopeHash, Envelope>;
|
||||
|
||||
#[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<ThreadNode>, 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<ThreadHash, ThreadNode>,
|
||||
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<ThreadNode>, idx: usize, new_root: usize) {
|
||||
b[idx].thread_group = new_root;
|
||||
fn rec_change_children(
|
||||
b: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||
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<ThreadTree>,
|
||||
}
|
||||
|
||||
|
@ -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<ThreadTree>>,
|
||||
}
|
||||
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<ThreadTree>>,
|
||||
}
|
||||
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<EnvelopeHash>,
|
||||
parent: Option<usize>,
|
||||
children: Vec<usize>,
|
||||
parent: Option<ThreadHash>,
|
||||
children: Vec<ThreadHash>,
|
||||
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<usize> {
|
||||
pub fn parent(&self) -> Option<ThreadHash> {
|
||||
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<ThreadNode>,
|
||||
root_set: RefCell<Vec<usize>>,
|
||||
thread_nodes: FnvHashMap<ThreadHash, ThreadNode>,
|
||||
root_set: RefCell<Vec<ThreadHash>>,
|
||||
tree: RefCell<Vec<ThreadTree>>,
|
||||
|
||||
message_ids: FnvHashMap<Vec<u8>, usize>,
|
||||
message_ids: FnvHashMap<Vec<u8>, ThreadHash>,
|
||||
pub hash_set: FnvHashSet<EnvelopeHash>,
|
||||
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<usize> {
|
||||
type Item = ThreadHash;
|
||||
fn next(&mut self) -> Option<ThreadHash> {
|
||||
{
|
||||
if self.pos == self.root_tree.len() {
|
||||
return None;
|
||||
|
@ -436,15 +468,16 @@ impl<'a> Iterator for RootIterator<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
fn find(buf: &mut Vec<ThreadNode>, i: usize) -> usize {
|
||||
if buf[i].thread_group == i {
|
||||
return i;
|
||||
fn find(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, 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<ThreadNode>, x: usize, y: usize) -> usize {
|
||||
fn union(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, 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<ThreadNode>, 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<usize>) {
|
||||
fn prune_empty_nodes(&mut self, root_set: &mut Vec<ThreadHash>) {
|
||||
fn prune(
|
||||
thread_nodes: &mut Vec<ThreadNode>,
|
||||
idx: usize,
|
||||
root_set: &mut Vec<usize>,
|
||||
thread_nodes: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||
idx: ThreadHash,
|
||||
root_set: &mut Vec<ThreadHash>,
|
||||
) -> 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<ThreadNode> =
|
||||
Vec::with_capacity((collection.len() as f64 * 1.2) as usize);
|
||||
let thread_nodes: FnvHashMap<ThreadHash, ThreadNode> = 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<Vec<u8>, usize> =
|
||||
let message_ids: FnvHashMap<Vec<u8>, ThreadHash> =
|
||||
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
|
||||
let hash_set: FnvHashSet<EnvelopeHash> =
|
||||
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<usize> = Vec::with_capacity(collection.len());
|
||||
let mut root_set: Vec<ThreadHash> = 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<Vec<u8>, (bool, usize)> =
|
||||
let mut subject_table: FnvHashMap<Vec<u8>, (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<usize> = self.tree.borrow().iter().map(|t| t.id).collect();
|
||||
let mut root_set: Vec<ThreadHash> = 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<usize> = self.tree.borrow().iter().map(|t| t.id).collect();
|
||||
let mut root_set: Vec<ThreadHash> = 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<ThreadNode> {
|
||||
pub fn thread_nodes(&self) -> &FnvHashMap<ThreadHash, ThreadNode> {
|
||||
&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 {
|
||||
let new_id = ThreadHash::new();
|
||||
self.thread_nodes.insert(
|
||||
new_id,
|
||||
ThreadNode {
|
||||
thread_group: new_id,
|
||||
..Default::default()
|
||||
});
|
||||
let new_id = self.thread_nodes.len() - 1;
|
||||
self.thread_nodes[new_id].thread_group = 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. */
|
||||
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<usize> 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<usize> for Threads {
|
|||
|
||||
fn node_build(
|
||||
tree: &mut ThreadTree,
|
||||
idx: usize,
|
||||
thread_nodes: &mut Vec<ThreadNode>,
|
||||
idx: ThreadHash,
|
||||
thread_nodes: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||
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<ThreadTree> = 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<usize>;
|
||||
let children = &thread_nodes[&idx].children as *const Vec<ThreadHash>;
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
use super::*;
|
||||
use melib::backends::Folder;
|
||||
use melib::backends::FolderHash;
|
||||
use melib::thread::ThreadHash;
|
||||
|
||||
pub mod listing;
|
||||
pub use listing::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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<ThreadNode> = &threads.thread_nodes();
|
||||
let thread_nodes: &FnvHashMap<ThreadHash, ThreadNode> = &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<BackendOp>,
|
||||
) -> 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();
|
||||
|
||||
|
|
|
@ -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<usize>,
|
||||
expanded_hash: Option<ThreadHash>,
|
||||
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<usize>, context: &Context) {
|
||||
fn initiate(&mut self, expanded_hash: Option<ThreadHash>, 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;
|
||||
|
|
|
@ -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<FolderNode>,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Box<Component>>),
|
||||
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),
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue