parent
4582bcd5ae
commit
22d868f499
|
@ -19,7 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Serialize, Default, Clone)]
|
||||||
pub struct AccountSettings {
|
pub struct AccountSettings {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub root_folder: String,
|
pub root_folder: String,
|
||||||
|
|
|
@ -35,7 +35,7 @@ use nom;
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, MeliError>;
|
pub type Result<T> = result::Result<T, MeliError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct MeliError {
|
pub struct MeliError {
|
||||||
details: String,
|
details: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub use self::email::*;
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
use self::backends::Folder;
|
use self::backends::Folder;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
use crate::mailbox::thread::ThreadHash;
|
||||||
|
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub use self::thread::{SortField, SortOrder, ThreadNode, Threads};
|
pub use self::thread::{SortField, SortOrder, ThreadNode, Threads};
|
||||||
|
|
||||||
|
@ -40,8 +42,9 @@ pub use self::collection::*;
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
|
|
||||||
/// `Mailbox` represents a folder of mail.
|
/// `Mailbox` represents a folder of mail.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||||
pub struct Mailbox {
|
pub struct Mailbox {
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub folder: Folder,
|
pub folder: Folder,
|
||||||
name: String,
|
name: String,
|
||||||
pub collection: Collection,
|
pub collection: Collection,
|
||||||
|
@ -72,28 +75,28 @@ impl Mailbox {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.collection.len()
|
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
|
self.collection
|
||||||
.envelopes
|
.envelopes
|
||||||
.entry(self.collection.threads.thread_to_mail(i))
|
.entry(self.collection.threads.thread_to_mail(h))
|
||||||
.or_default()
|
.or_default()
|
||||||
}
|
}
|
||||||
pub fn thread_to_mail(&self, i: usize) -> &Envelope {
|
pub fn thread_to_mail(&self, h: ThreadHash) -> &Envelope {
|
||||||
&self.collection.envelopes[&self.collection.threads.thread_to_mail(i)]
|
&self.collection.envelopes[&self.collection.threads.thread_to_mail(h)]
|
||||||
}
|
}
|
||||||
pub fn threaded_mail(&self, i: usize) -> EnvelopeHash {
|
pub fn threaded_mail(&self, h: ThreadHash) -> EnvelopeHash {
|
||||||
self.collection.threads.thread_to_mail(i)
|
self.collection.threads.thread_to_mail(h)
|
||||||
}
|
}
|
||||||
pub fn mail_and_thread(&mut self, i: EnvelopeHash) -> (&mut Envelope, &ThreadNode) {
|
pub fn mail_and_thread(&mut self, i: EnvelopeHash) -> (&mut Envelope, &ThreadNode) {
|
||||||
let thread;
|
let thread;
|
||||||
{
|
{
|
||||||
let x = &mut self.collection.envelopes.entry(i).or_default();
|
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)
|
(self.collection.envelopes.entry(i).or_default(), thread)
|
||||||
}
|
}
|
||||||
pub fn thread(&self, i: usize) -> &ThreadNode {
|
pub fn thread(&self, h: ThreadHash) -> &ThreadNode {
|
||||||
&self.collection.threads.thread_nodes()[i]
|
&self.collection.threads.thread_nodes()[&h]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_sent_folder(&mut self, _sent: &Mailbox) {
|
pub fn insert_sent_folder(&mut self, _sent: &Mailbox) {
|
||||||
|
|
|
@ -7,8 +7,9 @@ use std::ops::{Deref, DerefMut};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
|
||||||
/// `Mailbox` represents a folder of mail.
|
/// `Mailbox` represents a folder of mail.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Deserialize, Default, Serialize)]
|
||||||
pub struct Collection {
|
pub struct Collection {
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
folder: Folder,
|
folder: Folder,
|
||||||
pub envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
pub envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
||||||
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
||||||
|
|
|
@ -34,6 +34,7 @@ use parser::BytesExt;
|
||||||
|
|
||||||
use super::backends::BackendOp;
|
use super::backends::BackendOp;
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
|
use crate::mailbox::thread::ThreadHash;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -320,7 +321,7 @@ pub struct Envelope {
|
||||||
references: Option<References>,
|
references: Option<References>,
|
||||||
|
|
||||||
timestamp: UnixTimestamp,
|
timestamp: UnixTimestamp,
|
||||||
thread: usize,
|
thread: ThreadHash,
|
||||||
|
|
||||||
hash: EnvelopeHash,
|
hash: EnvelopeHash,
|
||||||
|
|
||||||
|
@ -355,7 +356,7 @@ impl Envelope {
|
||||||
|
|
||||||
timestamp: 0,
|
timestamp: 0,
|
||||||
|
|
||||||
thread: 0,
|
thread: ThreadHash::null(),
|
||||||
|
|
||||||
hash,
|
hash,
|
||||||
flags: Flag::default(),
|
flags: Flag::default(),
|
||||||
|
@ -731,10 +732,10 @@ impl Envelope {
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn thread(&self) -> usize {
|
pub fn thread(&self) -> ThreadHash {
|
||||||
self.thread
|
self.thread
|
||||||
}
|
}
|
||||||
pub fn set_thread(&mut self, new_val: usize) {
|
pub fn set_thread(&mut self, new_val: ThreadHash) {
|
||||||
self.thread = new_val;
|
self.thread = new_val;
|
||||||
}
|
}
|
||||||
pub fn set_datetime(&mut self, new_val: chrono::DateTime<chrono::FixedOffset>) {
|
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::parser::BytesExt;
|
||||||
use crate::mailbox::email::*;
|
use crate::mailbox::email::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use fnv::{FnvHashMap, FnvHashSet};
|
use fnv::{FnvHashMap, FnvHashSet};
|
||||||
use std::cell::{Ref, RefCell};
|
use std::cell::{Ref, RefCell};
|
||||||
|
@ -48,46 +49,71 @@ use std::str::FromStr;
|
||||||
|
|
||||||
type Envelopes = FnvHashMap<EnvelopeHash, Envelope>;
|
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 */
|
/* Helper macros to avoid repeating ourselves */
|
||||||
|
|
||||||
fn rec_change_root_parent(b: &mut Vec<ThreadNode>, idx: usize, new_root: usize) {
|
fn rec_change_root_parent(
|
||||||
b[idx].thread_group = new_root;
|
b: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
if let Some(p) = b[idx].parent {
|
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_children(b, p, new_root);
|
||||||
rec_change_root_parent(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) {
|
fn rec_change_children(
|
||||||
b[idx].thread_group = new_root;
|
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);
|
rec_change_children(b, c, new_root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! remove_from_parent {
|
macro_rules! remove_from_parent {
|
||||||
($buf:expr, $idx:expr) => {
|
($buf:expr, $idx:expr) => {{
|
||||||
if let Some(p) = $buf[$idx].parent {
|
let entry = $buf.entry($idx).or_default();
|
||||||
if let Some(pos) = $buf[p].children.iter().position(|c| *c == $idx) {
|
if let Some(p) = entry.parent {
|
||||||
$buf[p].children.remove(pos);
|
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);
|
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);
|
rec_change_children($buf, $idx, $idx);
|
||||||
$buf[$idx].thread_group = $idx;
|
$buf.entry($idx).and_modify(|e| e.thread_group = $idx);
|
||||||
};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make {
|
macro_rules! make {
|
||||||
(($p:expr)parent of($c:expr), $buf:expr) => {
|
(($p:expr)parent of($c:expr), $buf:expr) => {
|
||||||
remove_from_parent!($buf, $c);
|
remove_from_parent!($buf, $c);
|
||||||
if !($buf[$p]).children.contains(&$c) {
|
if !($buf[&$p]).children.contains(&$c) {
|
||||||
$buf[$p].children.push($c);
|
$buf.entry($p).and_modify(|e| e.children.push($c));
|
||||||
} else {
|
} else {
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
$buf[$c].parent = Some($p);
|
$buf.entry($c).and_modify(|e| e.parent = Some($p));
|
||||||
union($buf, $c, $p);
|
union($buf, $c, $p);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -201,7 +227,7 @@ impl FromStr for SortOrder {
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
struct ThreadTree {
|
struct ThreadTree {
|
||||||
id: usize,
|
id: ThreadHash,
|
||||||
children: Vec<ThreadTree>,
|
children: Vec<ThreadTree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +238,7 @@ impl fmt::Debug for ThreadTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadTree {
|
impl ThreadTree {
|
||||||
fn new(id: usize) -> Self {
|
fn new(id: ThreadHash) -> Self {
|
||||||
ThreadTree {
|
ThreadTree {
|
||||||
id,
|
id,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
@ -241,8 +267,8 @@ pub struct ThreadsIterator<'a> {
|
||||||
tree: Ref<'a, Vec<ThreadTree>>,
|
tree: Ref<'a, Vec<ThreadTree>>,
|
||||||
}
|
}
|
||||||
impl<'a> Iterator for ThreadsIterator<'a> {
|
impl<'a> Iterator for ThreadsIterator<'a> {
|
||||||
type Item = (usize, usize, bool);
|
type Item = (usize, ThreadHash, bool);
|
||||||
fn next(&mut self) -> Option<(usize, usize, bool)> {
|
fn next(&mut self) -> Option<(usize, ThreadHash, bool)> {
|
||||||
{
|
{
|
||||||
let mut tree = &(*self.tree);
|
let mut tree = &(*self.tree);
|
||||||
for i in &self.stack {
|
for i in &self.stack {
|
||||||
|
@ -293,8 +319,8 @@ pub struct ThreadIterator<'a> {
|
||||||
tree: Ref<'a, Vec<ThreadTree>>,
|
tree: Ref<'a, Vec<ThreadTree>>,
|
||||||
}
|
}
|
||||||
impl<'a> Iterator for ThreadIterator<'a> {
|
impl<'a> Iterator for ThreadIterator<'a> {
|
||||||
type Item = (usize, usize);
|
type Item = (usize, ThreadHash);
|
||||||
fn next(&mut self) -> Option<(usize, usize)> {
|
fn next(&mut self) -> Option<(usize, ThreadHash)> {
|
||||||
{
|
{
|
||||||
let mut tree = &(*self.tree);
|
let mut tree = &(*self.tree);
|
||||||
for i in &self.stack {
|
for i in &self.stack {
|
||||||
|
@ -324,8 +350,8 @@ impl<'a> Iterator for ThreadIterator<'a> {
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct ThreadNode {
|
pub struct ThreadNode {
|
||||||
message: Option<EnvelopeHash>,
|
message: Option<EnvelopeHash>,
|
||||||
parent: Option<usize>,
|
parent: Option<ThreadHash>,
|
||||||
children: Vec<usize>,
|
children: Vec<ThreadHash>,
|
||||||
date: UnixTimestamp,
|
date: UnixTimestamp,
|
||||||
indentation: usize,
|
indentation: usize,
|
||||||
show_subject: bool,
|
show_subject: bool,
|
||||||
|
@ -334,7 +360,7 @@ pub struct ThreadNode {
|
||||||
has_unseen: bool,
|
has_unseen: bool,
|
||||||
|
|
||||||
/* Union/Find set fields */
|
/* Union/Find set fields */
|
||||||
thread_group: usize,
|
thread_group: ThreadHash,
|
||||||
rank: i32,
|
rank: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,13 +376,19 @@ impl Default for ThreadNode {
|
||||||
|
|
||||||
len: 0,
|
len: 0,
|
||||||
has_unseen: false,
|
has_unseen: false,
|
||||||
thread_group: 0,
|
thread_group: ThreadHash::default(),
|
||||||
rank: 0,
|
rank: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadNode {
|
impl ThreadNode {
|
||||||
|
fn new(thread_group: ThreadHash) -> Self {
|
||||||
|
ThreadNode {
|
||||||
|
thread_group,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn show_subject(&self) -> bool {
|
pub fn show_subject(&self) -> bool {
|
||||||
self.show_subject
|
self.show_subject
|
||||||
}
|
}
|
||||||
|
@ -381,7 +413,7 @@ impl ThreadNode {
|
||||||
self.message.is_some()
|
self.message.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent(&self) -> Option<usize> {
|
pub fn parent(&self) -> Option<ThreadHash> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +421,7 @@ impl ThreadNode {
|
||||||
self.parent.is_some()
|
self.parent.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> &[usize] {
|
pub fn children(&self) -> &[ThreadHash] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,11 +432,11 @@ impl ThreadNode {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct Threads {
|
pub struct Threads {
|
||||||
thread_nodes: Vec<ThreadNode>,
|
thread_nodes: FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
root_set: RefCell<Vec<usize>>,
|
root_set: RefCell<Vec<ThreadHash>>,
|
||||||
tree: RefCell<Vec<ThreadTree>>,
|
tree: RefCell<Vec<ThreadTree>>,
|
||||||
|
|
||||||
message_ids: FnvHashMap<Vec<u8>, usize>,
|
message_ids: FnvHashMap<Vec<u8>, ThreadHash>,
|
||||||
pub hash_set: FnvHashSet<EnvelopeHash>,
|
pub hash_set: FnvHashSet<EnvelopeHash>,
|
||||||
sort: RefCell<(SortField, SortOrder)>,
|
sort: RefCell<(SortField, SortOrder)>,
|
||||||
subsort: RefCell<(SortField, SortOrder)>,
|
subsort: RefCell<(SortField, SortOrder)>,
|
||||||
|
@ -425,8 +457,8 @@ pub struct RootIterator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for RootIterator<'a> {
|
impl<'a> Iterator for RootIterator<'a> {
|
||||||
type Item = usize;
|
type Item = ThreadHash;
|
||||||
fn next(&mut self) -> Option<usize> {
|
fn next(&mut self) -> Option<ThreadHash> {
|
||||||
{
|
{
|
||||||
if self.pos == self.root_tree.len() {
|
if self.pos == self.root_tree.len() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -436,15 +468,16 @@ impl<'a> Iterator for RootIterator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn find(buf: &mut Vec<ThreadNode>, i: usize) -> usize {
|
fn find(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, h: ThreadHash) -> ThreadHash {
|
||||||
if buf[i].thread_group == i {
|
if buf[&h].thread_group == h {
|
||||||
return i;
|
return h;
|
||||||
}
|
}
|
||||||
let p = buf[i].thread_group;
|
let p = buf[&h].thread_group;
|
||||||
buf[i].thread_group = find(buf, p);
|
let new_group = find(buf, p);
|
||||||
buf[i].thread_group
|
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 x_root = find(buf, x);
|
||||||
let mut y_root = find(buf, y);
|
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;
|
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);
|
mem::swap(&mut x_root, &mut y_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// x and y are not in same set, so we merge them
|
// x and y are not in same set, so we merge them
|
||||||
//
|
//
|
||||||
buf[y_root].thread_group = x_root;
|
buf.entry(y_root).and_modify(|e| e.thread_group = x_root);
|
||||||
if buf[x_root].rank == buf[y_root].rank {
|
if buf[&x_root].rank == buf[&y_root].rank {
|
||||||
buf[x_root].rank += 1;
|
buf.entry(x_root).and_modify(|e| {
|
||||||
|
e.rank += 1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
x_root
|
x_root
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Threads {
|
impl Threads {
|
||||||
fn find(&mut self, i: usize) -> usize {
|
fn find(&mut self, i: ThreadHash) -> ThreadHash {
|
||||||
find(&mut self.thread_nodes, i)
|
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 x_root = self.find(x);
|
||||||
let mut y_root = self.find(y);
|
let mut y_root = self.find(y);
|
||||||
|
|
||||||
|
@ -479,47 +514,51 @@ impl Threads {
|
||||||
return x_root;
|
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);
|
mem::swap(&mut x_root, &mut y_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
// x and y are not in same set, so we merge them
|
// x and y are not in same set, so we merge them
|
||||||
//
|
//
|
||||||
self.thread_nodes[y_root].thread_group = x_root;
|
self.thread_nodes
|
||||||
if self.thread_nodes[x_root].rank == self.thread_nodes[y_root].rank {
|
.entry(y_root)
|
||||||
self.thread_nodes[x_root].rank += 1;
|
.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
|
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(
|
fn prune(
|
||||||
thread_nodes: &mut Vec<ThreadNode>,
|
thread_nodes: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
idx: usize,
|
idx: ThreadHash,
|
||||||
root_set: &mut Vec<usize>,
|
root_set: &mut Vec<ThreadHash>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
/* "If it is an empty container with no children, nuke it." */
|
/* "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);
|
remove_from_parent!(thread_nodes, idx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !thread_nodes[idx].has_message() && !thread_nodes[idx].has_parent() {
|
if !thread_nodes[&idx].has_message() && !thread_nodes[&idx].has_parent() {
|
||||||
if thread_nodes[idx].children.len() == 1 {
|
if thread_nodes[&idx].children.len() == 1 {
|
||||||
/* "Do not promote the children if doing so would promote them to the root set
|
/* "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." */
|
* -- 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);
|
root_set.push(child);
|
||||||
remove_from_parent!(thread_nodes, child);
|
remove_from_parent!(thread_nodes, child);
|
||||||
return true; // Pruned
|
return true; // Pruned
|
||||||
}
|
}
|
||||||
} else if let Some(p) = thread_nodes[idx].parent {
|
} else if let Some(p) = thread_nodes[&idx].parent {
|
||||||
if !thread_nodes[idx].has_message() {
|
if !thread_nodes[&idx].has_message() {
|
||||||
let orphans = thread_nodes[idx].children.clone();
|
let orphans = thread_nodes[&idx].children.clone();
|
||||||
for c in orphans {
|
for c in orphans {
|
||||||
make!((p) parent of (c), thread_nodes);
|
make!((p) parent of (c), thread_nodes);
|
||||||
}
|
}
|
||||||
remove_from_parent!(thread_nodes, idx);
|
remove_from_parent!(thread_nodes, idx);
|
||||||
thread_nodes[idx].children.clear();
|
thread_nodes.entry(idx).and_modify(|e| {
|
||||||
|
e.children.clear();
|
||||||
|
});
|
||||||
return true; // Pruned
|
return true; // Pruned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -528,15 +567,15 @@ impl Threads {
|
||||||
*/
|
*/
|
||||||
let mut c_idx = 0;
|
let mut c_idx = 0;
|
||||||
loop {
|
loop {
|
||||||
if c_idx == thread_nodes[idx].children.len() {
|
if c_idx == thread_nodes[&idx].children.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let c = thread_nodes[idx].children[c_idx];
|
let c = thread_nodes[&idx].children[c_idx];
|
||||||
if !prune(thread_nodes, c, root_set) {
|
if !prune(thread_nodes, c, root_set) {
|
||||||
c_idx += 1;
|
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;
|
let mut idx = 0;
|
||||||
|
@ -556,10 +595,12 @@ impl Threads {
|
||||||
/* To reconstruct thread information from the mails we need: */
|
/* To reconstruct thread information from the mails we need: */
|
||||||
|
|
||||||
/* a vector to hold thread members */
|
/* a vector to hold thread members */
|
||||||
let thread_nodes: Vec<ThreadNode> =
|
let thread_nodes: FnvHashMap<ThreadHash, ThreadNode> = FnvHashMap::with_capacity_and_hasher(
|
||||||
Vec::with_capacity((collection.len() as f64 * 1.2) as usize);
|
(collection.len() as f64 * 1.2) as usize,
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
/* A hash table of Message IDs */
|
/* 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());
|
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
|
||||||
let hash_set: FnvHashSet<EnvelopeHash> =
|
let hash_set: FnvHashSet<EnvelopeHash> =
|
||||||
FnvHashSet::with_capacity_and_hasher(collection.len(), Default::default());
|
FnvHashSet::with_capacity_and_hasher(collection.len(), Default::default());
|
||||||
|
@ -616,11 +657,11 @@ impl Threads {
|
||||||
fn create_root_set(&mut self, collection: &Envelopes) {
|
fn create_root_set(&mut self, collection: &Envelopes) {
|
||||||
/* Walk over the elements of message_ids, and gather a list of the ThreadNode objects that
|
/* 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 */
|
* 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 */
|
/* Find the root set */
|
||||||
for v in self.message_ids.values() {
|
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);
|
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
|
* messages which don't have References headers at all still get threaded (to the extent
|
||||||
* possible, at least.)"
|
* 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());
|
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
|
||||||
|
|
||||||
for &r in root_set.iter() {
|
for &r in root_set.iter() {
|
||||||
/* "Find the subject of that sub-tree": */
|
/* "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
|
/* "If there is a message in the Container, the subject is the subject of that
|
||||||
* message. " */
|
* message. " */
|
||||||
let msg_idx = self.thread_nodes[r].message.unwrap();
|
let msg_idx = self.thread_nodes[&r].message.unwrap();
|
||||||
let envelope = &collection[&msg_idx];
|
let envelope = &collection[&msg_idx];
|
||||||
(envelope.subject(), !envelope.references().is_empty())
|
(envelope.subject(), !envelope.references().is_empty())
|
||||||
} else {
|
} else {
|
||||||
/* "If there is no message in the Container, then the Container will have at least
|
/* "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
|
* one child Container, and that Container will have a message. Use the subject of
|
||||||
* that message instead." */
|
* 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
|
.message
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope = &collection[&msg_idx];
|
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
|
* "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
|
* container has a non-``Re:'' version of this subject. The non-re version is the
|
||||||
* more interesting of the two." */
|
* 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)
|
|| (other_is_re && !is_re)
|
||||||
{
|
{
|
||||||
mem::replace(
|
mem::replace(
|
||||||
|
@ -697,12 +738,12 @@ impl Threads {
|
||||||
let r = root_set[i];
|
let r = root_set[i];
|
||||||
|
|
||||||
/* "Find the subject of this Container (as above.)" */
|
/* "Find the subject of this Container (as above.)" */
|
||||||
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() {
|
||||||
let msg_idx = self.thread_nodes[r].message.unwrap();
|
let msg_idx = self.thread_nodes[&r].message.unwrap();
|
||||||
let envelope = &collection[&msg_idx];
|
let envelope = &collection[&msg_idx];
|
||||||
(envelope.subject(), !envelope.references().is_empty())
|
(envelope.subject(), !envelope.references().is_empty())
|
||||||
} else {
|
} 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
|
.message
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope = &collection[&msg_idx];
|
let envelope = &collection[&msg_idx];
|
||||||
|
@ -718,7 +759,7 @@ impl Threads {
|
||||||
|
|
||||||
let (other_is_re, other_idx) = subject_table[subject];
|
let (other_is_re, other_idx) = subject_table[subject];
|
||||||
/* "If it is null, or if it is this container, continue." */
|
/* "If 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,8 +770,9 @@ impl Threads {
|
||||||
* "If both are dummies, append one's children to the other, and remove the now-empty
|
* "If both are dummies, append one's children to the other, and remove the now-empty
|
||||||
* container."
|
* container."
|
||||||
*/
|
*/
|
||||||
if !self.thread_nodes[r].has_message() && !self.thread_nodes[other_idx].has_message() {
|
if !self.thread_nodes[&r].has_message() && !self.thread_nodes[&other_idx].has_message()
|
||||||
let children = self.thread_nodes[r].children.clone();
|
{
|
||||||
|
let children = self.thread_nodes[&r].children.clone();
|
||||||
for c in children {
|
for c in children {
|
||||||
make!((other_idx) parent of (c), &mut self.thread_nodes);
|
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
|
* of the empty, and a sibling of the other ``real'' messages with the same subject
|
||||||
* (the empty's children.)"
|
* (the empty's children.)"
|
||||||
*/
|
*/
|
||||||
} else if self.thread_nodes[r].has_message()
|
} else if self.thread_nodes[&r].has_message()
|
||||||
&& !self.thread_nodes[other_idx].has_message()
|
&& !self.thread_nodes[&other_idx].has_message()
|
||||||
{
|
{
|
||||||
make!((other_idx) parent of (r), &mut self.thread_nodes);
|
make!((other_idx) parent of (r), &mut self.thread_nodes);
|
||||||
if !root_set.contains(&other_idx) {
|
if !root_set.contains(&other_idx) {
|
||||||
root_set.push(other_idx);
|
root_set.push(other_idx);
|
||||||
}
|
}
|
||||||
roots_to_remove.push(i);
|
roots_to_remove.push(i);
|
||||||
} else if !self.thread_nodes[r].has_message()
|
} else if !self.thread_nodes[&r].has_message()
|
||||||
&& self.thread_nodes[other_idx].has_message()
|
&& self.thread_nodes[&other_idx].has_message()
|
||||||
{
|
{
|
||||||
make!((r) parent of (other_idx), &mut self.thread_nodes);
|
make!((r) parent of (other_idx), &mut self.thread_nodes);
|
||||||
if let Some(pos) = root_set.iter().position(|&i| i == other_idx) {
|
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
|
* "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."
|
* 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);
|
make!((other_idx) parent of (r), &mut self.thread_nodes);
|
||||||
roots_to_remove.push(i);
|
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
|
* without will be in the hash table, regardless of the order in which they were
|
||||||
* seen.)"
|
* 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);
|
make!((r) parent of (other_idx), &mut self.thread_nodes);
|
||||||
if let Some(pos) = root_set.iter().position(|r| *r == other_idx) {
|
if let Some(pos) = root_set.iter().position(|r| *r == other_idx) {
|
||||||
roots_to_remove.push(pos);
|
roots_to_remove.push(pos);
|
||||||
|
@ -780,9 +822,8 @@ impl Threads {
|
||||||
* hierarchical relationship which might not be true."
|
* hierarchical relationship which might not be true."
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
self.thread_nodes.push(Default::default());
|
let new_id = ThreadHash::new();
|
||||||
let new_id = self.thread_nodes.len() - 1;
|
self.thread_nodes.insert(new_id, ThreadNode::new(new_id));
|
||||||
self.thread_nodes[new_id].thread_group = new_id;
|
|
||||||
make!((new_id) parent of (r), &mut self.thread_nodes);
|
make!((new_id) parent of (r), &mut self.thread_nodes);
|
||||||
make!((new_id) parent of (other_idx), &mut self.thread_nodes);
|
make!((new_id) parent of (other_idx), &mut self.thread_nodes);
|
||||||
root_set[i] = new_id;
|
root_set[i] = new_id;
|
||||||
|
@ -827,20 +868,19 @@ impl Threads {
|
||||||
* - hash_set
|
* - hash_set
|
||||||
* - message fields in thread_nodes
|
* - message fields in thread_nodes
|
||||||
*/
|
*/
|
||||||
let idx = if let Some((idx, node)) = self
|
let thread_hash = if let Some((key, node)) = self
|
||||||
.thread_nodes
|
.thread_nodes
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
|
||||||
.find(|(_, n)| n.message.map(|n| n == old_hash).unwrap_or(false))
|
.find(|(_, n)| n.message.map(|n| n == old_hash).unwrap_or(false))
|
||||||
{
|
{
|
||||||
node.message = Some(new_hash);
|
node.message = Some(new_hash);
|
||||||
idx
|
*key
|
||||||
} else {
|
} else {
|
||||||
return Err(());
|
return Err(());
|
||||||
};
|
};
|
||||||
self.hash_set.remove(&old_hash);
|
self.hash_set.remove(&old_hash);
|
||||||
self.hash_set.insert(new_hash);
|
self.hash_set.insert(new_hash);
|
||||||
self.rebuild_thread(idx, collection);
|
self.rebuild_thread(thread_hash, collection);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,15 +896,15 @@ impl Threads {
|
||||||
// debug!("DEBUG: {} in threads is idx= {}", envelope_hash, pos);
|
// 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
|
.thread_nodes
|
||||||
.iter()
|
.iter_mut()
|
||||||
.position(|n| n.message.map(|n| n == envelope_hash).unwrap_or(false))
|
.find(|(_, n)| n.message.map(|n| n == envelope_hash).unwrap_or(false))
|
||||||
{
|
{
|
||||||
t_id = pos;
|
t_id = *pos;
|
||||||
self.thread_nodes[pos].message = None;
|
n.message = None;
|
||||||
} else {
|
} else {
|
||||||
/* else it was deleted during a thread_rebuild or others */
|
/* else it was deleted during a thread_rebuild or others */
|
||||||
return;
|
return;
|
||||||
|
@ -874,7 +914,7 @@ impl Threads {
|
||||||
let mut node_idx = t_id;
|
let mut node_idx = t_id;
|
||||||
|
|
||||||
/* Trace path back to root ThreadNode */
|
/* 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;
|
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.prune_empty_nodes(&mut root_set);
|
||||||
self.tree.borrow_mut().retain(|t| root_set.contains(&t.id));
|
self.tree.borrow_mut().retain(|t| root_set.contains(&t.id));
|
||||||
}
|
}
|
||||||
|
@ -922,7 +962,7 @@ impl Threads {
|
||||||
}
|
}
|
||||||
self.create_root_set(collection);
|
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);
|
self.prune_empty_nodes(&mut root_set);
|
||||||
let tree = self.tree.get_mut();
|
let tree = self.tree.get_mut();
|
||||||
tree.retain(|t| root_set.contains(&t.id));
|
tree.retain(|t| root_set.contains(&t.id));
|
||||||
|
@ -966,11 +1006,11 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update thread tree information on envelope insertion */
|
/* 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 node_idx = id;
|
||||||
let mut stack = Vec::with_capacity(32);
|
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()
|
node.parent.is_none()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -987,7 +1027,7 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Trace path back to root ThreadNode */
|
/* 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;
|
node_idx = *p;
|
||||||
stack.push(node_idx);
|
stack.push(node_idx);
|
||||||
}
|
}
|
||||||
|
@ -1053,18 +1093,18 @@ impl Threads {
|
||||||
for t in tree.iter_mut() {
|
for t in tree.iter_mut() {
|
||||||
t.children.sort_by(|a, b| match subsort {
|
t.children.sort_by(|a, b| match subsort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[a.id];
|
let a = &self.thread_nodes[&a.id];
|
||||||
let b = &self.thread_nodes[b.id];
|
let b = &self.thread_nodes[&b.id];
|
||||||
b.date.cmp(&a.date)
|
b.date.cmp(&a.date)
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[a.id];
|
let a = &self.thread_nodes[&a.id];
|
||||||
let b = &self.thread_nodes[b.id];
|
let b = &self.thread_nodes[&b.id];
|
||||||
a.date.cmp(&b.date)
|
a.date.cmp(&b.date)
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
(SortField::Subject, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[a.id].message();
|
let a = &self.thread_nodes[&a.id].message();
|
||||||
let b = &self.thread_nodes[b.id].message();
|
let b = &self.thread_nodes[&b.id].message();
|
||||||
|
|
||||||
if a.is_none() || b.is_none() {
|
if a.is_none() || b.is_none() {
|
||||||
return Ordering::Equal;
|
return Ordering::Equal;
|
||||||
|
@ -1074,8 +1114,8 @@ impl Threads {
|
||||||
ma.subject().cmp(&mb.subject())
|
ma.subject().cmp(&mb.subject())
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Asc) => {
|
(SortField::Subject, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[a.id].message();
|
let a = &self.thread_nodes[&a.id].message();
|
||||||
let b = &self.thread_nodes[b.id].message();
|
let b = &self.thread_nodes[&b.id].message();
|
||||||
|
|
||||||
if a.is_none() || b.is_none() {
|
if a.is_none() || b.is_none() {
|
||||||
return Ordering::Equal;
|
return Ordering::Equal;
|
||||||
|
@ -1092,18 +1132,18 @@ impl Threads {
|
||||||
let tree = &mut self.tree.borrow_mut();
|
let tree = &mut self.tree.borrow_mut();
|
||||||
tree.sort_by(|a, b| match sort {
|
tree.sort_by(|a, b| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[a.id];
|
let a = &self.thread_nodes[&a.id];
|
||||||
let b = &self.thread_nodes[b.id];
|
let b = &self.thread_nodes[&b.id];
|
||||||
b.date.cmp(&a.date)
|
b.date.cmp(&a.date)
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[a.id];
|
let a = &self.thread_nodes[&a.id];
|
||||||
let b = &self.thread_nodes[b.id];
|
let b = &self.thread_nodes[&b.id];
|
||||||
a.date.cmp(&b.date)
|
a.date.cmp(&b.date)
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
(SortField::Subject, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[a.id].message();
|
let a = &self.thread_nodes[&a.id].message();
|
||||||
let b = &self.thread_nodes[b.id].message();
|
let b = &self.thread_nodes[&b.id].message();
|
||||||
|
|
||||||
if a.is_none() || b.is_none() {
|
if a.is_none() || b.is_none() {
|
||||||
return Ordering::Equal;
|
return Ordering::Equal;
|
||||||
|
@ -1113,8 +1153,8 @@ impl Threads {
|
||||||
ma.subject().cmp(&mb.subject())
|
ma.subject().cmp(&mb.subject())
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Asc) => {
|
(SortField::Subject, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[a.id].message();
|
let a = &self.thread_nodes[&a.id].message();
|
||||||
let b = &self.thread_nodes[b.id].message();
|
let b = &self.thread_nodes[&b.id].message();
|
||||||
|
|
||||||
if a.is_none() || b.is_none() {
|
if a.is_none() || b.is_none() {
|
||||||
return Ordering::Equal;
|
return Ordering::Equal;
|
||||||
|
@ -1142,12 +1182,12 @@ impl Threads {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_to_mail(&self, i: usize) -> EnvelopeHash {
|
pub fn thread_to_mail(&self, i: ThreadHash) -> EnvelopeHash {
|
||||||
let thread = &self.thread_nodes[i];
|
let thread = &self.thread_nodes[&i];
|
||||||
thread.message().unwrap()
|
thread.message().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_nodes(&self) -> &Vec<ThreadNode> {
|
pub fn thread_nodes(&self) -> &FnvHashMap<ThreadHash, ThreadNode> {
|
||||||
&self.thread_nodes
|
&self.thread_nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1159,7 +1199,7 @@ impl Threads {
|
||||||
self.tree.borrow().len()
|
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
|
self.tree.borrow()[idx].id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,15 +1210,15 @@ impl Threads {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_sibling(&self, i: usize) -> bool {
|
pub fn has_sibling(&self, h: ThreadHash) -> bool {
|
||||||
if let Some(parent) = self[i].parent {
|
if let Some(parent) = self[&h].parent {
|
||||||
let children = &self[parent].children;
|
let children = &self[&parent].children;
|
||||||
if children.is_empty() {
|
if children.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let pos = children
|
let pos = children
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&x| x == i)
|
.position(|&x| x == h)
|
||||||
.expect("Did not find node in parent!");
|
.expect("Did not find node in parent!");
|
||||||
pos != children.len() - 1
|
pos != children.len() - 1
|
||||||
} else {
|
} else {
|
||||||
|
@ -1187,7 +1227,7 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_envelope(&mut self, envelope: &mut Envelope) {
|
fn link_envelope(&mut self, envelope: &mut Envelope) {
|
||||||
let t_idx: usize = {
|
let t_idx: ThreadHash = {
|
||||||
let m_id = envelope.message_id().raw();
|
let m_id = envelope.message_id().raw();
|
||||||
|
|
||||||
/* t_idx: The index of this message's ThreadNode in thread_nodes
|
/* 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
|
/* the already existing ThreadNote should be empty, since we're
|
||||||
* seeing this message for the first time. otherwise it's a
|
* seeing this message for the first time. otherwise it's a
|
||||||
* duplicate. */
|
* duplicate. */
|
||||||
if self.thread_nodes[node_idx].message.is_some() {
|
if self.thread_nodes[&node_idx].message.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node_idx
|
node_idx
|
||||||
} else {
|
} else {
|
||||||
/* Create a new ThreadNode object holding this message */
|
/* 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()),
|
message: Some(envelope.hash()),
|
||||||
date: envelope.date(),
|
date: envelope.date(),
|
||||||
|
thread_group: new_id,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
};
|
||||||
/* The new thread node's set is just itself */
|
self.thread_nodes.insert(new_id, node);
|
||||||
let new_id = self.thread_nodes.len() - 1;
|
|
||||||
self.thread_nodes[new_id].thread_group = new_id;
|
|
||||||
|
|
||||||
self.message_ids.insert(m_id.to_vec(), new_id);
|
self.message_ids.insert(m_id.to_vec(), new_id);
|
||||||
new_id
|
new_id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.thread_nodes[t_idx].date = envelope.date();
|
self.thread_nodes.entry(t_idx).and_modify(|e| {
|
||||||
self.thread_nodes[t_idx].message = Some(envelope.hash());
|
e.date = envelope.date();
|
||||||
self.thread_nodes[t_idx].has_unseen |= !envelope.is_seen();
|
e.message = Some(envelope.hash());
|
||||||
|
e.has_unseen |= !envelope.is_seen();
|
||||||
|
});
|
||||||
envelope.set_thread(t_idx);
|
envelope.set_thread(t_idx);
|
||||||
self.hash_set.insert(envelope.hash());
|
self.hash_set.insert(envelope.hash());
|
||||||
|
|
||||||
|
@ -1247,17 +1290,20 @@ impl Threads {
|
||||||
self.message_ids[r_id]
|
self.message_ids[r_id]
|
||||||
} else {
|
} else {
|
||||||
/* Create a new ThreadNode object holding this reference */
|
/* Create a new ThreadNode object holding this reference */
|
||||||
self.thread_nodes.push(ThreadNode {
|
let new_id = ThreadHash::new();
|
||||||
..Default::default()
|
self.thread_nodes.insert(
|
||||||
});
|
new_id,
|
||||||
let new_id = self.thread_nodes.len() - 1;
|
ThreadNode {
|
||||||
self.thread_nodes[new_id].thread_group = new_id;
|
thread_group: new_id,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
self.message_ids.insert(r_id.to_vec(), new_id);
|
self.message_ids.insert(r_id.to_vec(), new_id);
|
||||||
new_id
|
new_id
|
||||||
};
|
};
|
||||||
/* If they are already linked, don't change the existing links. */
|
/* If they are already linked, don't change the existing links. */
|
||||||
if self.thread_nodes[ref_ptr].has_parent()
|
if self.thread_nodes[&ref_ptr].has_parent()
|
||||||
&& self.thread_nodes[ref_ptr].parent.unwrap() != parent_id
|
&& self.thread_nodes[&ref_ptr].parent.unwrap() != parent_id
|
||||||
{
|
{
|
||||||
ref_ptr = parent_id;
|
ref_ptr = parent_id;
|
||||||
continue;
|
continue;
|
||||||
|
@ -1283,10 +1329,10 @@ impl Threads {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for Threads {
|
impl Index<&ThreadHash> for Threads {
|
||||||
type Output = ThreadNode;
|
type Output = ThreadNode;
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &ThreadNode {
|
fn index(&self, index: &ThreadHash) -> &ThreadNode {
|
||||||
self.thread_nodes
|
self.thread_nodes
|
||||||
.get(index)
|
.get(index)
|
||||||
.expect("thread index out of bounds")
|
.expect("thread index out of bounds")
|
||||||
|
@ -1295,20 +1341,20 @@ impl Index<usize> for Threads {
|
||||||
|
|
||||||
fn node_build(
|
fn node_build(
|
||||||
tree: &mut ThreadTree,
|
tree: &mut ThreadTree,
|
||||||
idx: usize,
|
idx: ThreadHash,
|
||||||
thread_nodes: &mut Vec<ThreadNode>,
|
thread_nodes: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
indentation: usize,
|
indentation: usize,
|
||||||
collection: &Envelopes,
|
collection: &Envelopes,
|
||||||
) {
|
) {
|
||||||
if let Some(hash) = thread_nodes[idx].message {
|
if let Some(hash) = thread_nodes[&idx].message {
|
||||||
if !collection.contains_key(&hash) {
|
if !collection.contains_key(&hash) {
|
||||||
/* invalidate node */
|
/* invalidate node */
|
||||||
// thread_nodes[idx].message = None;
|
// thread_nodes[&idx].message = None;
|
||||||
} else if let Some(parent_id) = thread_nodes[idx].parent {
|
} else if let Some(parent_id) = thread_nodes[&idx].parent {
|
||||||
if let Some(parent_hash) = thread_nodes[parent_id].message {
|
if let Some(parent_hash) = thread_nodes[&parent_id].message {
|
||||||
if !collection.contains_key(&parent_hash) {
|
if !collection.contains_key(&parent_hash) {
|
||||||
/* invalidate node */
|
/* invalidate node */
|
||||||
// thread_nodes[parent_id].message = None;
|
// thread_nodes[&parent_id].message = None;
|
||||||
} else {
|
} else {
|
||||||
/* decide if the subject should be shown in the UI.
|
/* decide if the subject should be shown in the UI.
|
||||||
* If parent subject is Foobar and reply is `Re: Foobar`
|
* 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();
|
let mut parent_subject = parent_subject.to_mut().as_bytes();
|
||||||
parent_subject.strip_prefixes();
|
parent_subject.strip_prefixes();
|
||||||
if subject == parent_subject {
|
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() {
|
let indentation = if thread_nodes[&idx].has_message() {
|
||||||
thread_nodes[idx].indentation = indentation;
|
thread_nodes
|
||||||
|
.entry(idx)
|
||||||
|
.and_modify(|e| e.indentation = indentation);
|
||||||
indentation + 1
|
indentation + 1
|
||||||
} else if indentation > 0 {
|
} else if indentation > 0 {
|
||||||
indentation
|
indentation
|
||||||
|
@ -1337,28 +1387,35 @@ fn node_build(
|
||||||
indentation + 1
|
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()
|
!collection[&msg].is_seen()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
let mut child_vec: Vec<ThreadTree> = Vec::new();
|
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
|
/* 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.
|
* 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) } {
|
for &c in unsafe { &(*children) } {
|
||||||
let mut new_tree = ThreadTree::new(c);
|
let mut new_tree = ThreadTree::new(c);
|
||||||
node_build(&mut new_tree, c, thread_nodes, indentation, collection);
|
node_build(&mut new_tree, c, thread_nodes, indentation, collection);
|
||||||
thread_nodes[idx].len += thread_nodes[c].len;
|
let _c = (thread_nodes[&c].len, thread_nodes[&c].date);
|
||||||
thread_nodes[idx].date = cmp::max(thread_nodes[idx].date, 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);
|
child_vec.push(new_tree);
|
||||||
}
|
}
|
||||||
tree.children = child_vec;
|
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 super::*;
|
||||||
use melib::backends::Folder;
|
use melib::backends::Folder;
|
||||||
use melib::backends::FolderHash;
|
use melib::backends::FolderHash;
|
||||||
|
use melib::thread::ThreadHash;
|
||||||
|
|
||||||
pub mod listing;
|
pub mod listing;
|
||||||
pub use listing::*;
|
pub use listing::*;
|
||||||
|
|
|
@ -127,14 +127,14 @@ impl Composer {
|
||||||
* msg: index of message we reply to in thread_nodes
|
* msg: index of message we reply to in thread_nodes
|
||||||
* context: current context
|
* 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]
|
let mailbox = &context.accounts[coordinates.0][coordinates.1]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let threads = &mailbox.collection.threads;
|
let threads = &mailbox.collection.threads;
|
||||||
let thread_nodes = &threads.thread_nodes();
|
let thread_nodes = &threads.thread_nodes();
|
||||||
let mut ret = Composer::default();
|
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]
|
let op = context.accounts[coordinates.0]
|
||||||
.backend
|
.backend
|
||||||
.operation(message.hash(), mailbox.folder.hash());
|
.operation(message.hash(), mailbox.folder.hash());
|
||||||
|
@ -144,14 +144,18 @@ impl Composer {
|
||||||
ret.account_cursor = coordinates.0;
|
ret.account_cursor = coordinates.0;
|
||||||
ret
|
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]
|
let mailbox = &context.accounts[coordinates.0][coordinates.1]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let threads = &mailbox.collection.threads;
|
let threads = &mailbox.collection.threads;
|
||||||
let thread_nodes = &threads.thread_nodes();
|
let thread_nodes = &threads.thread_nodes();
|
||||||
let mut ret = Composer::default();
|
let mut ret = Composer::default();
|
||||||
let p = &thread_nodes[msg];
|
let p = &thread_nodes[&msg];
|
||||||
let parent_message = &mailbox.collection[&p.message().unwrap()];
|
let parent_message = &mailbox.collection[&p.message().unwrap()];
|
||||||
let mut op = context.accounts[coordinates.0]
|
let mut op = context.accounts[coordinates.0]
|
||||||
.backend
|
.backend
|
||||||
|
|
|
@ -207,15 +207,15 @@ impl MailboxView {
|
||||||
|
|
||||||
threads.sort_by(self.sort, self.subsort, &mailbox.collection);
|
threads.sort_by(self.sort, self.subsort, &mailbox.collection);
|
||||||
for (idx, root_idx) in threads.root_iter().enumerate() {
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
let mut iter_ptr = thread_node.children()[0];
|
let mut iter_ptr = thread_node.children()[0];
|
||||||
while threads.thread_nodes()[iter_ptr].message().is_none() {
|
while threads.thread_nodes()[&iter_ptr].message().is_none() {
|
||||||
iter_ptr = threads.thread_nodes()[iter_ptr].children()[0];
|
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) {
|
if !mailbox.collection.contains_key(&i) {
|
||||||
debug!("key = {}", i);
|
debug!("key = {}", i);
|
||||||
|
@ -251,15 +251,15 @@ impl MailboxView {
|
||||||
};
|
};
|
||||||
|
|
||||||
for ((idx, root_idx), strings) in threads.root_iter().enumerate().zip(rows) {
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
let mut iter_ptr = thread_node.children()[0];
|
let mut iter_ptr = thread_node.children()[0];
|
||||||
while threads.thread_nodes()[iter_ptr].message().is_none() {
|
while threads.thread_nodes()[&iter_ptr].message().is_none() {
|
||||||
iter_ptr = threads.thread_nodes()[iter_ptr].children()[0];
|
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) {
|
if !mailbox.collection.contains_key(&i) {
|
||||||
debug!("key = {}", i);
|
debug!("key = {}", i);
|
||||||
|
@ -354,15 +354,15 @@ impl MailboxView {
|
||||||
}
|
}
|
||||||
let threads = &mailbox.collection.threads;
|
let threads = &mailbox.collection.threads;
|
||||||
let thread_node = threads.root_set(idx);
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
let mut iter_ptr = thread_node.children()[0];
|
let mut iter_ptr = thread_node.children()[0];
|
||||||
while threads.thread_nodes()[iter_ptr].message().is_none() {
|
while threads.thread_nodes()[&iter_ptr].message().is_none() {
|
||||||
iter_ptr = threads.thread_nodes()[iter_ptr].children()[0];
|
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];
|
let root_envelope: &Envelope = &mailbox.collection[&i];
|
||||||
|
|
|
@ -151,12 +151,12 @@ impl ThreadListing {
|
||||||
/* Draw threaded view. */
|
/* Draw threaded view. */
|
||||||
let threads = &mailbox.collection.threads;
|
let threads = &mailbox.collection.threads;
|
||||||
threads.sort_by(self.sort, self.subsort, &mailbox.collection);
|
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();
|
let mut iter = threads.threads_iter().peekable();
|
||||||
/* This is just a desugared for loop so that we can use .peek() */
|
/* This is just a desugared for loop so that we can use .peek() */
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
while let Some((indentation, i, has_sibling)) = iter.next() {
|
while let Some((indentation, thread_hash, has_sibling)) = iter.next() {
|
||||||
let thread_node = &thread_nodes[i];
|
let thread_node = &thread_nodes[&thread_hash];
|
||||||
|
|
||||||
if indentation == 0 {
|
if indentation == 0 {
|
||||||
thread_idx += 1;
|
thread_idx += 1;
|
||||||
|
@ -181,7 +181,7 @@ impl ThreadListing {
|
||||||
envelope,
|
envelope,
|
||||||
idx,
|
idx,
|
||||||
indentation,
|
indentation,
|
||||||
i,
|
thread_hash,
|
||||||
threads,
|
threads,
|
||||||
&indentations,
|
&indentations,
|
||||||
has_sibling,
|
has_sibling,
|
||||||
|
@ -362,13 +362,13 @@ impl ThreadListing {
|
||||||
envelope: &Envelope,
|
envelope: &Envelope,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
node_idx: usize,
|
node_idx: ThreadHash,
|
||||||
threads: &Threads,
|
threads: &Threads,
|
||||||
indentations: &[bool],
|
indentations: &[bool],
|
||||||
has_sibling: bool,
|
has_sibling: bool,
|
||||||
//op: Box<BackendOp>,
|
//op: Box<BackendOp>,
|
||||||
) -> String {
|
) -> String {
|
||||||
let thread_node = &threads[node_idx];
|
let thread_node = &threads[&node_idx];
|
||||||
let has_parent = thread_node.has_parent();
|
let has_parent = thread_node.has_parent();
|
||||||
let show_subject = thread_node.show_subject();
|
let show_subject = thread_node.show_subject();
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use std::cmp;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct ThreadEntry {
|
struct ThreadEntry {
|
||||||
index: (usize, usize, usize),
|
index: (usize, ThreadHash, usize),
|
||||||
/// (indentation, thread_node index, line number in listing)
|
/// (indentation, thread_node index, line number in listing)
|
||||||
indentation: usize,
|
indentation: usize,
|
||||||
msg_hash: EnvelopeHash,
|
msg_hash: EnvelopeHash,
|
||||||
|
@ -57,13 +57,13 @@ impl ThreadView {
|
||||||
const DESCRIPTION: &'static str = "thread view";
|
const DESCRIPTION: &'static str = "thread view";
|
||||||
/*
|
/*
|
||||||
* coordinates: (account index, mailbox index, root set thread_node index)
|
* 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.
|
* expanded message is the last one.
|
||||||
* context: current context
|
* context: current context
|
||||||
*/
|
*/
|
||||||
pub fn new(
|
pub fn new(
|
||||||
coordinates: (usize, usize, usize),
|
coordinates: (usize, usize, usize),
|
||||||
expanded_idx: Option<usize>,
|
expanded_hash: Option<ThreadHash>,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut view = ThreadView {
|
let mut view = ThreadView {
|
||||||
|
@ -79,7 +79,7 @@ impl ThreadView {
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
view.initiate(expanded_idx, context);
|
view.initiate(expanded_hash, context);
|
||||||
view.new_cursor_pos = view.new_expanded_pos;
|
view.new_cursor_pos = view.new_expanded_pos;
|
||||||
view
|
view
|
||||||
}
|
}
|
||||||
|
@ -102,8 +102,8 @@ impl ThreadView {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let expanded_pos = self.expanded_pos;
|
let expanded_hash = old_expanded_entry.as_ref().map(|e| e.index.1);
|
||||||
self.initiate(Some(expanded_pos), context);
|
self.initiate(expanded_hash, context);
|
||||||
|
|
||||||
let mut old_cursor = 0;
|
let mut old_cursor = 0;
|
||||||
let mut new_cursor = 0;
|
let mut new_cursor = 0;
|
||||||
|
@ -144,7 +144,7 @@ impl ThreadView {
|
||||||
}
|
}
|
||||||
self.set_dirty();
|
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 */
|
/* 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]
|
let mailbox = &context.accounts[self.coordinates.0][self.coordinates.1]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -153,23 +153,23 @@ impl ThreadView {
|
||||||
|
|
||||||
let thread_iter = threads.thread_iter(self.coordinates.2);
|
let thread_iter = threads.thread_iter(self.coordinates.2);
|
||||||
self.entries.clear();
|
self.entries.clear();
|
||||||
for (line, (ind, idx)) in thread_iter.enumerate() {
|
for (line, (ind, thread_hash)) in thread_iter.enumerate() {
|
||||||
let entry = if let Some(msg_hash) = threads.thread_nodes()[idx].message() {
|
let entry = if let Some(msg_hash) = threads.thread_nodes()[&thread_hash].message() {
|
||||||
let seen: bool = mailbox.collection[&msg_hash].is_seen();
|
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 {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
match expanded_idx {
|
match expanded_hash {
|
||||||
Some(expanded_idx) if expanded_idx == idx => {
|
Some(expanded_hash) if expanded_hash == thread_hash => {
|
||||||
self.new_expanded_pos = self.entries.len().saturating_sub(1);
|
self.new_expanded_pos = self.entries.len().saturating_sub(1);
|
||||||
self.expanded_pos = self.new_expanded_pos + 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.new_expanded_pos = self.entries.len().saturating_sub(1);
|
||||||
self.expanded_pos = self.new_expanded_pos + 1;
|
self.expanded_pos = self.new_expanded_pos + 1;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ impl ThreadView {
|
||||||
Vec::with_capacity(self.entries.len());
|
Vec::with_capacity(self.entries.len());
|
||||||
for e in &mut self.entries {
|
for e in &mut self.entries {
|
||||||
let envelope: &Envelope = &mailbox.collection[&e.msg_hash];
|
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 string = if thread_node.show_subject() {
|
||||||
let subject = envelope.subject();
|
let subject = envelope.subject();
|
||||||
highlight_reply_subjects.push(Some(subject.grapheme_width()));
|
highlight_reply_subjects.push(Some(subject.grapheme_width()));
|
||||||
|
@ -324,7 +324,7 @@ impl ThreadView {
|
||||||
|
|
||||||
fn make_entry(
|
fn make_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
i: (usize, usize, usize),
|
i: (usize, ThreadHash, usize),
|
||||||
msg_hash: EnvelopeHash,
|
msg_hash: EnvelopeHash,
|
||||||
seen: bool,
|
seen: bool,
|
||||||
) -> ThreadEntry {
|
) -> ThreadEntry {
|
||||||
|
@ -535,11 +535,11 @@ impl ThreadView {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let threads = &mailbox.collection.threads;
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
threads.thread_nodes()[thread_node.children()[0]]
|
threads.thread_nodes()[&thread_node.children()[0]]
|
||||||
.message()
|
.message()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
@ -615,15 +615,15 @@ impl ThreadView {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let threads = &mailbox.collection.threads;
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
let mut iter_ptr = thread_node.children()[0];
|
let mut iter_ptr = thread_node.children()[0];
|
||||||
while threads.thread_nodes()[iter_ptr].message().is_none() {
|
while threads.thread_nodes()[&iter_ptr].message().is_none() {
|
||||||
iter_ptr = threads.thread_nodes()[iter_ptr].children()[0];
|
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];
|
let envelope: &Envelope = &mailbox.collection[&i];
|
||||||
|
|
||||||
|
@ -835,11 +835,12 @@ impl Component for ThreadView {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let threads = &mailbox.collection.threads;
|
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() {
|
let i = if let Some(i) = thread_node.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
threads.thread_nodes()[thread_node.children()[0]]
|
threads.thread_nodes()[&thread_node.children()[0]]
|
||||||
.message()
|
.message()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
@ -890,8 +891,8 @@ impl Component for ThreadView {
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Ctrl('r')) => {
|
UIEvent::Input(Key::Ctrl('r')) => {
|
||||||
self.reversed = !self.reversed;
|
self.reversed = !self.reversed;
|
||||||
let expanded_pos = self.expanded_pos;
|
let expanded_hash = self.entries[self.expanded_pos].index.1;
|
||||||
self.initiate(Some(expanded_pos), context);
|
self.initiate(Some(expanded_hash), context);
|
||||||
self.initiated = false;
|
self.initiated = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -92,6 +92,17 @@ impl Drop for Account {
|
||||||
let writer = io::BufWriter::new(f);
|
let writer = io::BufWriter::new(f);
|
||||||
serde_json::to_writer(writer, &self.address_book).unwrap();
|
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 {
|
struct FolderNode {
|
||||||
hash: FolderHash,
|
hash: FolderHash,
|
||||||
kids: Vec<FolderNode>,
|
kids: Vec<FolderNode>,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// Settings for the mailer function.
|
/// Settings for the mailer function.
|
||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
pub struct MailerSettings {
|
pub struct MailerSettings {
|
||||||
/// A command to pipe new emails to
|
/// A command to pipe new emails to
|
||||||
/// Required
|
/// Required
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
use super::default_vals::*;
|
use super::default_vals::*;
|
||||||
/// Settings for the pager function.
|
/// Settings for the pager function.
|
||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Clone, Default, Serialize)]
|
||||||
pub struct PagerSettings {
|
pub struct PagerSettings {
|
||||||
/// Number of context lines when going to next page.
|
/// Number of context lines when going to next page.
|
||||||
/// Default: 0
|
/// Default: 0
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
use components::Component;
|
use components::Component;
|
||||||
pub use melib::mailbox::{SortField, SortOrder};
|
pub use melib::mailbox::{SortField, SortOrder};
|
||||||
|
use melib::thread::ThreadHash;
|
||||||
|
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -40,9 +41,9 @@ pub enum ListingAction {
|
||||||
pub enum TabAction {
|
pub enum TabAction {
|
||||||
TabOpen(Option<Box<Component>>),
|
TabOpen(Option<Box<Component>>),
|
||||||
NewDraft(usize),
|
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,
|
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),
|
Kill(Uuid),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue