mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-19 03:25:38 +00:00
melib/threads: use all References in thread building
WIP
This commit is contained in:
parent
56e3ea1548
commit
1eb49efb22
@ -418,22 +418,22 @@ impl Envelope {
|
|||||||
_ => Cow::from(String::new()),
|
_ => Cow::from(String::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_reply_to(&self) -> Option<&MessageID> {
|
pub fn in_reply_to(&self) -> Option<&MessageID> {
|
||||||
self.in_reply_to.as_ref()
|
self.in_reply_to
|
||||||
|
.as_ref()
|
||||||
|
.or(self.references.as_ref().and_then(|r| r.refs.last()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_reply_to_display(&self) -> Option<Cow<str>> {
|
pub fn in_reply_to_display(&self) -> Option<Cow<str>> {
|
||||||
if let Some(ref m) = self.in_reply_to {
|
self.in_reply_to
|
||||||
Some(String::from_utf8_lossy(m.val()))
|
.as_ref()
|
||||||
} else {
|
.map(|m| String::from_utf8_lossy(m.val()))
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn in_reply_to_raw(&self) -> Option<Cow<str>> {
|
pub fn in_reply_to_raw(&self) -> Option<Cow<str>> {
|
||||||
if let Some(ref m) = self.in_reply_to {
|
self.in_reply_to
|
||||||
Some(String::from_utf8_lossy(m.raw()))
|
.as_ref()
|
||||||
} else {
|
.map(|m| String::from_utf8_lossy(m.raw()))
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn message_id(&self) -> &MessageID {
|
pub fn message_id(&self) -> &MessageID {
|
||||||
&self.message_id
|
&self.message_id
|
||||||
|
@ -135,26 +135,26 @@ macro_rules! remove_from_parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make {
|
macro_rules! make {
|
||||||
(($p:expr)parent of($c:expr), $buf:expr) => {{
|
(($p:expr)parent of($c:expr), $threads:expr) => {{
|
||||||
let prev_parent = remove_from_parent!($buf, $c);
|
if $threads.find($c) != $threads.find($p) {
|
||||||
if !($buf[&$p]).children.contains(&$c) {
|
let prev_parent = remove_from_parent!(&mut $threads.thread_nodes, $c);
|
||||||
/* Pruned nodes keep their children in case they show up in a later merge, so do not panic
|
if !($threads.thread_nodes[&$p]).children.contains(&$c) {
|
||||||
* if children exists */
|
/* Pruned nodes keep their children in case they show up in a later merge, so do not panic
|
||||||
$buf.entry($p).and_modify(|e| e.children.push($c));
|
* if children exists */
|
||||||
}
|
$threads.thread_nodes.entry($p).and_modify(|e| e.children.push($c));
|
||||||
let child_date = $buf[&$c].date;
|
}
|
||||||
let child_len = $buf[&$c].len;
|
let child_len = $threads.thread_nodes[&$c].len;
|
||||||
let has_unseen = $buf[&$c].has_unseen;
|
let has_unseen = $threads.thread_nodes[&$c].has_unseen;
|
||||||
$buf.entry($c).and_modify(|e| {
|
$threads.thread_nodes.entry($c).and_modify(|e| {
|
||||||
e.parent = Some($p);
|
e.parent = Some($p);
|
||||||
});
|
});
|
||||||
$buf.entry($p).and_modify(|e| {
|
$threads.thread_nodes.entry($p).and_modify(|e| {
|
||||||
e.len += child_len + 1;
|
e.len += child_len + 1;
|
||||||
e.date = std::cmp::max(e.date, child_date);
|
e.has_unseen |= has_unseen;
|
||||||
e.has_unseen |= has_unseen;
|
});
|
||||||
});
|
$threads.union($c, $p);
|
||||||
union($buf, $c, $p);
|
|
||||||
prev_parent
|
prev_parent
|
||||||
|
} else { None }
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,10 +308,16 @@ impl<'a> Iterator for ThreadsIterator<'a> {
|
|||||||
if !self.thread_nodes[&tree[self.pos]].children.is_empty() {
|
if !self.thread_nodes[&tree[self.pos]].children.is_empty() {
|
||||||
self.stack.push(self.pos);
|
self.stack.push(self.pos);
|
||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
return Some(ret);
|
if self.thread_nodes[&ret.1].message.is_some() {
|
||||||
|
return Some(ret);
|
||||||
|
} else {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
return Some(ret);
|
if self.thread_nodes[&ret.1].message.is_some() {
|
||||||
|
return Some(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.next()
|
self.next()
|
||||||
@ -356,10 +362,16 @@ impl<'a> Iterator for ThreadIterator<'a> {
|
|||||||
if !self.thread_nodes[&tree[self.pos]].children.is_empty() {
|
if !self.thread_nodes[&tree[self.pos]].children.is_empty() {
|
||||||
self.stack.push(self.pos);
|
self.stack.push(self.pos);
|
||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
return Some(ret);
|
if self.thread_nodes[&ret.1].message.is_some() {
|
||||||
|
return Some(ret);
|
||||||
|
} else {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
return Some(ret);
|
if self.thread_nodes[&ret.1].message.is_some() {
|
||||||
|
return Some(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.next()
|
self.next()
|
||||||
@ -375,6 +387,7 @@ pub struct ThreadNode {
|
|||||||
indentation: usize,
|
indentation: usize,
|
||||||
show_subject: bool,
|
show_subject: bool,
|
||||||
pruned: bool,
|
pruned: bool,
|
||||||
|
is_root: bool,
|
||||||
|
|
||||||
len: usize,
|
len: usize,
|
||||||
has_unseen: bool,
|
has_unseen: bool,
|
||||||
@ -396,6 +409,7 @@ impl Default for ThreadNode {
|
|||||||
indentation: 0,
|
indentation: 0,
|
||||||
show_subject: true,
|
show_subject: true,
|
||||||
pruned: false,
|
pruned: false,
|
||||||
|
is_root: false,
|
||||||
|
|
||||||
len: 0,
|
len: 0,
|
||||||
has_unseen: false,
|
has_unseen: false,
|
||||||
@ -478,81 +492,64 @@ impl ThreadNode {
|
|||||||
vec: &[ThreadHash],
|
vec: &[ThreadHash],
|
||||||
child: ThreadHash,
|
child: ThreadHash,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
buf: &FnvHashMap<ThreadHash, ThreadNode>,
|
buf: &mut FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
|
dates: &FnvHashMap<ThreadHash, UnixTimestamp>,
|
||||||
envelopes: &Envelopes,
|
envelopes: &Envelopes,
|
||||||
) -> usize {
|
) -> std::result::Result<usize, usize> {
|
||||||
let envelopes = envelopes.read().unwrap();
|
let envelopes = envelopes.read().unwrap();
|
||||||
match sort {
|
match sort {
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
match vec.binary_search_by(|probe| buf[&probe].date.cmp(&buf[&child].date)) {
|
vec.binary_search_by(|probe| dates[probe].cmp(&dates[&child]))
|
||||||
Ok(p) => p,
|
|
||||||
Err(p) => p,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
match vec
|
vec.binary_search_by(|probe| dates[probe].cmp(&dates[&child]).reverse())
|
||||||
.binary_search_by(|probe| buf[&probe].date.cmp(&buf[&child].date).reverse())
|
|
||||||
{
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(p) => p,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Asc) => {
|
(SortField::Subject, SortOrder::Asc) => vec.binary_search_by(|probe| {
|
||||||
match vec.binary_search_by(|probe| {
|
match (
|
||||||
match (
|
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
) {
|
||||||
) {
|
(Some(p), Some(c)) => {
|
||||||
(Some(p), Some(c)) => {
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
#[cfg(feature = "unicode_algorithms")]
|
{
|
||||||
{
|
envelopes[p]
|
||||||
envelopes[p]
|
.subject()
|
||||||
.subject()
|
.split_graphemes()
|
||||||
.split_graphemes()
|
.cmp(&envelopes[c].subject().split_graphemes())
|
||||||
.cmp(&envelopes[c].subject().split_graphemes())
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "unicode_algorithms"))]
|
|
||||||
{
|
|
||||||
envelopes[p].subject().cmp(&envelopes[c].subject())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(Some(_), None) => Ordering::Greater,
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
(None, Some(_)) => Ordering::Less,
|
{
|
||||||
(None, None) => Ordering::Equal,
|
envelopes[p].subject().cmp(&envelopes[c].subject())
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(p) => p,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
|
||||||
match vec.binary_search_by(|probe| {
|
|
||||||
match (
|
|
||||||
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
|
||||||
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
|
||||||
) {
|
|
||||||
(Some(p), Some(c)) => {
|
|
||||||
#[cfg(feature = "unicode_algorithms")]
|
|
||||||
{
|
|
||||||
envelopes[c]
|
|
||||||
.subject()
|
|
||||||
.split_graphemes()
|
|
||||||
.cmp(&envelopes[p].subject().split_graphemes())
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "unicode_algorithms"))]
|
|
||||||
{
|
|
||||||
envelopes[c].subject().cmp(&envelopes[p].subject())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(Some(_), None) => Ordering::Less,
|
|
||||||
(None, Some(_)) => Ordering::Greater,
|
|
||||||
(None, None) => Ordering::Equal,
|
|
||||||
}
|
}
|
||||||
}) {
|
(Some(_), None) => Ordering::Greater,
|
||||||
Ok(p) => p,
|
(None, Some(_)) => Ordering::Less,
|
||||||
Err(p) => p,
|
(None, None) => Ordering::Equal,
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
|
(SortField::Subject, SortOrder::Desc) => vec.binary_search_by(|probe| {
|
||||||
|
match (
|
||||||
|
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
|
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
|
) {
|
||||||
|
(Some(p), Some(c)) => {
|
||||||
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
|
{
|
||||||
|
envelopes[c]
|
||||||
|
.subject()
|
||||||
|
.split_graphemes()
|
||||||
|
.cmp(&envelopes[p].subject().split_graphemes())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
|
{
|
||||||
|
envelopes[c].subject().cmp(&envelopes[p].subject())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(_), None) => Ordering::Less,
|
||||||
|
(None, Some(_)) => Ordering::Greater,
|
||||||
|
(None, None) => Ordering::Equal,
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,6 +557,7 @@ impl ThreadNode {
|
|||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct Threads {
|
pub struct Threads {
|
||||||
pub thread_nodes: FnvHashMap<ThreadHash, ThreadNode>,
|
pub thread_nodes: FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
|
pub thread_dates: FnvHashMap<ThreadHash, UnixTimestamp>,
|
||||||
root_set: RefCell<Vec<ThreadHash>>,
|
root_set: RefCell<Vec<ThreadHash>>,
|
||||||
tree_index: RefCell<Vec<ThreadHash>>,
|
tree_index: RefCell<Vec<ThreadHash>>,
|
||||||
|
|
||||||
@ -634,29 +632,6 @@ fn find(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, h: ThreadHash) -> ThreadHa
|
|||||||
buf.entry(h).and_modify(|e| e.thread_group = new_group);
|
buf.entry(h).and_modify(|e| e.thread_group = new_group);
|
||||||
new_group
|
new_group
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
// x and y are already in the same set
|
|
||||||
if x_root == y_root {
|
|
||||||
return x_root;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.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 {
|
impl Threads {
|
||||||
pub fn is_snoozed(&self, h: ThreadHash) -> bool {
|
pub fn is_snoozed(&self, h: ThreadHash) -> bool {
|
||||||
@ -672,10 +647,22 @@ impl Threads {
|
|||||||
|
|
||||||
// x and y are already in the same set
|
// x and y are already in the same set
|
||||||
if x_root == y_root {
|
if x_root == y_root {
|
||||||
|
let max = std::cmp::max(
|
||||||
|
*self
|
||||||
|
.thread_dates
|
||||||
|
.entry(x_root)
|
||||||
|
.or_insert(self.thread_nodes[&x_root].date),
|
||||||
|
*self
|
||||||
|
.thread_dates
|
||||||
|
.entry(y_root)
|
||||||
|
.or_insert(self.thread_nodes[&y_root].date),
|
||||||
|
);
|
||||||
|
*self.thread_dates.entry(x_root).or_default() = max;
|
||||||
|
*self.thread_dates.entry(y_root).or_default() = max;
|
||||||
return x_root;
|
return x_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.thread_nodes[&x_root].rank < self.thread_nodes[&y_root].rank {
|
if self.thread_nodes[&y_root].date < self.thread_nodes[&x_root].date {
|
||||||
mem::swap(&mut x_root, &mut y_root);
|
mem::swap(&mut x_root, &mut y_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,9 +671,21 @@ impl Threads {
|
|||||||
self.thread_nodes
|
self.thread_nodes
|
||||||
.entry(y_root)
|
.entry(y_root)
|
||||||
.and_modify(|e| e.thread_group = x_root);
|
.and_modify(|e| e.thread_group = 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 {
|
||||||
self.thread_nodes.entry(x_root).and_modify(|e| e.rank += 1);
|
// self.thread_nodes.entry(x_root).and_modify(|e| e.rank += 1);
|
||||||
}
|
//}
|
||||||
|
let max = std::cmp::max(
|
||||||
|
*self
|
||||||
|
.thread_dates
|
||||||
|
.entry(x_root)
|
||||||
|
.or_insert(self.thread_nodes[&x_root].date),
|
||||||
|
*self
|
||||||
|
.thread_dates
|
||||||
|
.entry(y_root)
|
||||||
|
.or_insert(self.thread_nodes[&y_root].date),
|
||||||
|
);
|
||||||
|
*self.thread_dates.entry(x_root).or_default() = max;
|
||||||
|
*self.thread_dates.entry(y_root).or_default() = max;
|
||||||
x_root
|
x_root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,6 +697,8 @@ impl Threads {
|
|||||||
(length as f64 * 1.2) as usize,
|
(length as f64 * 1.2) as usize,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
|
let thread_dates: FnvHashMap<ThreadHash, UnixTimestamp> =
|
||||||
|
FnvHashMap::with_capacity_and_hasher(thread_nodes.len(), Default::default());
|
||||||
/* A hash table of Message IDs */
|
/* A hash table of Message IDs */
|
||||||
let message_ids: FnvHashMap<Vec<u8>, ThreadHash> =
|
let message_ids: FnvHashMap<Vec<u8>, ThreadHash> =
|
||||||
FnvHashMap::with_capacity_and_hasher(length, Default::default());
|
FnvHashMap::with_capacity_and_hasher(length, Default::default());
|
||||||
@ -712,10 +713,12 @@ impl Threads {
|
|||||||
|
|
||||||
Threads {
|
Threads {
|
||||||
thread_nodes,
|
thread_nodes,
|
||||||
|
thread_dates,
|
||||||
message_ids,
|
message_ids,
|
||||||
message_ids_set,
|
message_ids_set,
|
||||||
missing_message_ids,
|
missing_message_ids,
|
||||||
hash_set,
|
hash_set,
|
||||||
|
sort: RefCell::new((SortField::Date, SortOrder::Desc)),
|
||||||
subsort: RefCell::new((SortField::Subject, SortOrder::Desc)),
|
subsort: RefCell::new((SortField::Subject, SortOrder::Desc)),
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -872,32 +875,47 @@ impl Threads {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) {
|
pub fn insert(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) {
|
||||||
let envelopes_lck = envelopes.read().unwrap();
|
self.insert_internal(envelopes, env_hash, false);
|
||||||
if self
|
}
|
||||||
.message_ids
|
|
||||||
.contains_key(envelopes_lck[&env_hash].message_id().raw())
|
|
||||||
&& !self
|
|
||||||
.missing_message_ids
|
|
||||||
.contains(envelopes_lck[&env_hash].message_id().raw())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn insert_internal(
|
||||||
|
&mut self,
|
||||||
|
envelopes: &mut Envelopes,
|
||||||
|
env_hash: EnvelopeHash,
|
||||||
|
other_folder: bool,
|
||||||
|
) -> bool {
|
||||||
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
.map(crate::email::StrBuild::raw)
|
.map(crate::email::StrBuild::raw)
|
||||||
.and_then(|r| self.message_ids.get(r).cloned());
|
.and_then(|r| self.message_ids.get(r).cloned());
|
||||||
let new_id = ThreadHash::new();
|
if other_folder
|
||||||
self.thread_nodes.insert(
|
&& reply_to_id.is_none()
|
||||||
new_id,
|
&& !envelopes_lck[&env_hash]
|
||||||
ThreadNode {
|
.references()
|
||||||
message: Some(env_hash),
|
.iter()
|
||||||
parent: reply_to_id,
|
.any(|r| self.message_ids.contains_key(r.raw()))
|
||||||
date: envelopes_lck[&env_hash].date(),
|
{
|
||||||
has_unseen: !envelopes_lck[&env_hash].is_seen(),
|
return false;
|
||||||
..ThreadNode::new(new_id)
|
}
|
||||||
},
|
let new_id = self
|
||||||
);
|
.message_ids
|
||||||
|
.get(envelopes_lck[&env_hash].message_id().raw())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| ThreadHash::new());
|
||||||
|
{
|
||||||
|
let mut node = self.thread_nodes.entry(new_id).or_default();
|
||||||
|
node.message = Some(env_hash);
|
||||||
|
if node.thread_group == ThreadHash::default() {
|
||||||
|
node.thread_group = new_id;
|
||||||
|
}
|
||||||
|
if node.parent.is_none() {
|
||||||
|
node.parent = reply_to_id;
|
||||||
|
}
|
||||||
|
node.date = envelopes_lck[&env_hash].date();
|
||||||
|
*self.thread_dates.entry(new_id).or_default() = node.date;
|
||||||
|
node.has_unseen = !envelopes_lck[&env_hash].is_seen();
|
||||||
|
}
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelopes_lck[&env_hash].message_id().raw().to_vec(), new_id);
|
.insert(envelopes_lck[&env_hash].message_id().raw().to_vec(), new_id);
|
||||||
self.message_ids_set.insert(
|
self.message_ids_set.insert(
|
||||||
@ -910,9 +928,10 @@ impl Threads {
|
|||||||
self.missing_message_ids
|
self.missing_message_ids
|
||||||
.remove(envelopes_lck[&env_hash].message_id().raw());
|
.remove(envelopes_lck[&env_hash].message_id().raw());
|
||||||
self.hash_set.insert(env_hash);
|
self.hash_set.insert(env_hash);
|
||||||
|
let mut new_root = None;
|
||||||
if let Some(reply_to_id) = reply_to_id {
|
if let Some(reply_to_id) = reply_to_id {
|
||||||
self.union(reply_to_id, new_id);
|
//self.union(reply_to_id, new_id);
|
||||||
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
make!((reply_to_id) parent of (new_id), self);
|
||||||
} else {
|
} else {
|
||||||
if let Some(r) = envelopes_lck[&env_hash]
|
if let Some(r) = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
@ -927,15 +946,122 @@ impl Threads {
|
|||||||
..ThreadNode::new(reply_to_id)
|
..ThreadNode::new(reply_to_id)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.union(reply_to_id, new_id);
|
make!((reply_to_id) parent of (new_id), self);
|
||||||
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
|
||||||
self.missing_message_ids.insert(r.to_vec());
|
self.missing_message_ids.insert(r.to_vec());
|
||||||
self.message_ids.insert(r.to_vec(), reply_to_id);
|
self.message_ids.insert(r.to_vec(), reply_to_id);
|
||||||
self.message_ids_set.insert(r.to_vec().to_vec());
|
self.message_ids_set.insert(r.to_vec().to_vec());
|
||||||
|
new_root = Some(reply_to_id);
|
||||||
|
} else {
|
||||||
|
new_root = Some(new_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if envelopes_lck[&env_hash].references.is_some() {
|
||||||
|
let sort = *self.sort.borrow();
|
||||||
|
let mut current_descendant = envelopes_lck[&env_hash].message_id().to_string();
|
||||||
|
//debug!("{} references are {}", ¤t_descendant, unsafe { std::str::from_utf8_unchecked( &envelopes_lck[&env_hash].references.as_ref().unwrap().raw,) });
|
||||||
|
//debug!( "{} in_reply_to is {:?}", ¤t_descendant, &envelopes_lck[&env_hash].in_reply_to_display());
|
||||||
|
let mut current_descendant_id = new_id;
|
||||||
|
let mut references = envelopes_lck[&env_hash].references();
|
||||||
|
if let Some(irt) = envelopes_lck[&env_hash].in_reply_to().as_ref() {
|
||||||
|
if references.first() == envelopes_lck[&env_hash].in_reply_to().as_ref() {
|
||||||
|
references.reverse();
|
||||||
|
/*
|
||||||
|
references.remove(0);
|
||||||
|
if !references.contains(irt) {
|
||||||
|
references.push(irt);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug!( "{} references iter is {:?}", ¤t_descendant, references .iter() .map(|r| unsafe { std::str::from_utf8_unchecked(r.raw()) }) .collect::<Vec<&str>>());
|
||||||
|
|
||||||
|
for reference in references.into_iter().rev() {
|
||||||
|
//debug!("parent of {} is {}", current_descendant, reference);
|
||||||
|
if let Some(&id) = self.message_ids.get(reference.raw()) {
|
||||||
|
//self.union(id, current_descendant_id);
|
||||||
|
if self.thread_nodes[&id].date > self.thread_nodes[¤t_descendant_id].date
|
||||||
|
|| self.thread_nodes[¤t_descendant_id].parent.is_some()
|
||||||
|
{
|
||||||
|
current_descendant_id = id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
make!((id) parent of (current_descendant_id), self);
|
||||||
|
if self.thread_nodes[&id].message.is_some()
|
||||||
|
&& (self.thread_nodes[&new_id].parent.is_none()
|
||||||
|
|| self.thread_nodes
|
||||||
|
[self.thread_nodes[&new_id].parent.as_ref().unwrap()]
|
||||||
|
.message
|
||||||
|
.is_none())
|
||||||
|
{
|
||||||
|
if self.thread_nodes[¤t_descendant_id].is_root {
|
||||||
|
let pos = ThreadNode::insert_child_pos(
|
||||||
|
&self.tree_index.borrow(),
|
||||||
|
current_descendant_id,
|
||||||
|
sort,
|
||||||
|
&mut self.thread_nodes,
|
||||||
|
&self.thread_dates,
|
||||||
|
&envelopes,
|
||||||
|
);
|
||||||
|
if let Ok(pos) = pos {
|
||||||
|
self.tree_index.borrow_mut().remove(pos);
|
||||||
|
}
|
||||||
|
self.thread_nodes
|
||||||
|
.entry(current_descendant_id)
|
||||||
|
.and_modify(|n| {
|
||||||
|
n.is_root = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if self.thread_nodes[&id].is_root {
|
||||||
|
new_root = None;
|
||||||
|
} else {
|
||||||
|
new_root = Some(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_descendant_id = id;
|
||||||
|
} else {
|
||||||
|
let id = ThreadHash::new();
|
||||||
|
self.thread_nodes.insert(
|
||||||
|
id,
|
||||||
|
ThreadNode {
|
||||||
|
date: envelopes_lck[&env_hash].date(),
|
||||||
|
thread_group: id,
|
||||||
|
..ThreadNode::new(id)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
//self.union(id, current_descendant_id);
|
||||||
|
make!((id) parent of (current_descendant_id), self);
|
||||||
|
self.missing_message_ids.insert(reference.raw().to_vec());
|
||||||
|
self.message_ids.insert(reference.raw().to_vec(), id);
|
||||||
|
self.message_ids_set
|
||||||
|
.insert(reference.raw().to_vec().to_vec());
|
||||||
|
current_descendant_id = id;
|
||||||
|
}
|
||||||
|
current_descendant = reference.to_string();
|
||||||
}
|
}
|
||||||
self.tree_insert_root(new_id, envelopes);
|
|
||||||
}
|
}
|
||||||
drop(envelopes_lck);
|
drop(envelopes_lck);
|
||||||
|
/*{
|
||||||
|
let mut new_tree = vec![];
|
||||||
|
for (k, t) in self.thread_nodes.iter() {
|
||||||
|
if t.message.is_some() && t.parent.is_none() {
|
||||||
|
new_tree.push(*k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vec_inner_sort_by(&mut new_tree, *self.sort.borrow(), envelopes);
|
||||||
|
let mut tree = self.tree_index.borrow_mut();
|
||||||
|
tree.clear();
|
||||||
|
*tree = new_tree;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if let Some(id) = new_root {
|
||||||
|
self.tree_insert_root(id, envelopes);
|
||||||
|
} else {
|
||||||
|
//self.inner_sort_by(*self.sort.borrow(), envelopes);
|
||||||
|
}
|
||||||
|
|
||||||
self.update_show_subject(new_id, env_hash, envelopes);
|
self.update_show_subject(new_id, env_hash, envelopes);
|
||||||
envelopes
|
envelopes
|
||||||
.write()
|
.write()
|
||||||
@ -943,10 +1069,26 @@ impl Threads {
|
|||||||
.get_mut(&env_hash)
|
.get_mut(&env_hash)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_thread(new_id);
|
.set_thread(new_id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
save_graph(
|
||||||
|
&self.tree_index.borrow(),
|
||||||
|
&self.thread_nodes,
|
||||||
|
&self
|
||||||
|
.message_ids
|
||||||
|
.iter()
|
||||||
|
.map(|(a, &b)| (b, a.to_vec()))
|
||||||
|
.collect::<FnvHashMap<ThreadHash, Vec<u8>>>(),
|
||||||
|
&envelopes,
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert or update */
|
/* Insert or update */
|
||||||
pub fn insert_reply(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) -> bool {
|
pub fn insert_reply(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) -> bool {
|
||||||
|
return self.insert_internal(envelopes, env_hash, true);
|
||||||
|
|
||||||
let mut envelopes_lck = envelopes.write().unwrap();
|
let mut envelopes_lck = envelopes.write().unwrap();
|
||||||
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
@ -972,7 +1114,7 @@ impl Threads {
|
|||||||
});
|
});
|
||||||
if let Some(reply_to_id) = reply_to_id {
|
if let Some(reply_to_id) = reply_to_id {
|
||||||
if !self.thread_nodes[&reply_to_id].children.contains(&id) {
|
if !self.thread_nodes[&reply_to_id].children.contains(&id) {
|
||||||
make!((reply_to_id) parent of (id), &mut self.thread_nodes);
|
make!((reply_to_id) parent of (id), self);
|
||||||
self.union(id, reply_to_id);
|
self.union(id, reply_to_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1030,7 +1172,7 @@ impl Threads {
|
|||||||
envelopes_lck.get_mut(&env_hash).unwrap().set_thread(new_id);
|
envelopes_lck.get_mut(&env_hash).unwrap().set_thread(new_id);
|
||||||
self.hash_set.insert(env_hash);
|
self.hash_set.insert(env_hash);
|
||||||
self.union(reply_to_id, new_id);
|
self.union(reply_to_id, new_id);
|
||||||
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
make!((reply_to_id) parent of (new_id), self);
|
||||||
drop(envelopes_lck);
|
drop(envelopes_lck);
|
||||||
self.update_show_subject(new_id, env_hash, envelopes);
|
self.update_show_subject(new_id, env_hash, envelopes);
|
||||||
true
|
true
|
||||||
@ -1113,16 +1255,16 @@ impl Threads {
|
|||||||
envelopes: &Envelopes,
|
envelopes: &Envelopes,
|
||||||
) {
|
) {
|
||||||
let envelopes = envelopes.read().unwrap();
|
let envelopes = envelopes.read().unwrap();
|
||||||
vec.sort_by(|b, a| match sort {
|
vec.sort_by(|a, b| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = self.thread_dates[a];
|
||||||
let b = &self.thread_nodes[&b];
|
let b = self.thread_dates[b];
|
||||||
b.date.cmp(&a.date)
|
b.cmp(&a)
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = self.thread_dates[a];
|
||||||
let b = &self.thread_nodes[&b];
|
let b = self.thread_dates[b];
|
||||||
a.date.cmp(&b.date)
|
a.cmp(&b)
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
(SortField::Subject, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a].message();
|
let a = &self.thread_nodes[&a].message();
|
||||||
@ -1189,16 +1331,16 @@ impl Threads {
|
|||||||
fn inner_sort_by(&self, sort: (SortField, SortOrder), envelopes: &Envelopes) {
|
fn inner_sort_by(&self, sort: (SortField, SortOrder), envelopes: &Envelopes) {
|
||||||
let tree = &mut self.tree_index.borrow_mut();
|
let tree = &mut self.tree_index.borrow_mut();
|
||||||
let envelopes = envelopes.read().unwrap();
|
let envelopes = envelopes.read().unwrap();
|
||||||
tree.sort_by(|b, a| match sort {
|
tree.sort_by(|a, b| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = self.thread_dates[a];
|
||||||
let b = &self.thread_nodes[&b];
|
let b = self.thread_dates[b];
|
||||||
b.date.cmp(&a.date)
|
b.cmp(&a)
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = self.thread_dates[a];
|
||||||
let b = &self.thread_nodes[&b];
|
let b = self.thread_dates[b];
|
||||||
a.date.cmp(&b.date)
|
a.cmp(&b)
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
(SortField::Subject, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a].message();
|
let a = &self.thread_nodes[&a].message();
|
||||||
@ -1420,12 +1562,14 @@ impl Threads {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
fn tree_insert_root(&mut self, new_id: ThreadHash, envelopes: &Envelopes) {
|
fn tree_insert_root(&mut self, new_id: ThreadHash, envelopes: &Envelopes) {
|
||||||
debug_assert!(
|
if !(self.thread_nodes[&new_id].parent.is_none()
|
||||||
self.thread_nodes[&new_id].parent.is_none()
|
|| self.thread_nodes[self.thread_nodes[&new_id].parent.as_ref().unwrap()]
|
||||||
|| self.thread_nodes[self.thread_nodes[&new_id].parent.as_ref().unwrap()]
|
.message
|
||||||
.message
|
.is_none())
|
||||||
.is_none()
|
{
|
||||||
);
|
return;
|
||||||
|
};
|
||||||
|
debug!("tree_insert_root {}", new_id);
|
||||||
/* Index of reply_to_id in self.trees */
|
/* Index of reply_to_id in self.trees */
|
||||||
let Threads {
|
let Threads {
|
||||||
ref mut tree_index,
|
ref mut tree_index,
|
||||||
@ -1438,14 +1582,21 @@ impl Threads {
|
|||||||
tree_index.remove(i);
|
tree_index.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let pos = ThreadNode::insert_child_pos(
|
self.thread_nodes.entry(new_id).and_modify(|n| {
|
||||||
|
n.is_root = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
match ThreadNode::insert_child_pos(
|
||||||
&tree_index,
|
&tree_index,
|
||||||
new_id,
|
new_id,
|
||||||
*self.sort.borrow(),
|
*self.sort.borrow(),
|
||||||
&self.thread_nodes,
|
&mut self.thread_nodes,
|
||||||
|
&self.thread_dates,
|
||||||
envelopes,
|
envelopes,
|
||||||
);
|
) {
|
||||||
tree_index.insert(pos, new_id);
|
Err(pos) => tree_index.insert(pos, new_id),
|
||||||
|
Ok(pos) => tree_index[pos] = new_id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1490,3 +1641,143 @@ fn print_threadnodes(
|
|||||||
help(0, node_hash, nodes, envelopes);
|
help(0, node_hash, nodes, envelopes);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Node {
|
||||||
|
id: String,
|
||||||
|
subject: String,
|
||||||
|
from: String,
|
||||||
|
to: String,
|
||||||
|
date: UnixTimestamp,
|
||||||
|
references: String,
|
||||||
|
in_reply_to: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Link {
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Graph {
|
||||||
|
nodes: Vec<Node>,
|
||||||
|
links: Vec<Link>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn save_graph(
|
||||||
|
node_arr: &[ThreadHash],
|
||||||
|
nodes: &FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
|
ids: &FnvHashMap<ThreadHash, Vec<u8>>,
|
||||||
|
envelopes: &Envelopes,
|
||||||
|
) {
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
|
let mut graph = Graph {
|
||||||
|
nodes: vec![],
|
||||||
|
links: vec![],
|
||||||
|
};
|
||||||
|
let mut stack: SmallVec<[(ThreadHash, String); 16]> = SmallVec::new();
|
||||||
|
for n in node_arr {
|
||||||
|
stack.extend(
|
||||||
|
nodes[n].children.iter().cloned().zip(
|
||||||
|
std::iter::repeat(unsafe { std::str::from_utf8_unchecked(&ids[n]) }.to_string())
|
||||||
|
.take(nodes[n].children.len()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
graph.nodes.push(Node {
|
||||||
|
id: unsafe { std::str::from_utf8_unchecked(&ids[n]).to_string() },
|
||||||
|
subject: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].subject().to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
from: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_from_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
to: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_to_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
date: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].date())
|
||||||
|
.unwrap_or(0),
|
||||||
|
references: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_references_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
in_reply_to: nodes[n]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|h| envelopes[h].in_reply_to())
|
||||||
|
.map(|h| h.to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
});
|
||||||
|
while let Some((target, parent)) = stack.pop() {
|
||||||
|
graph.nodes.push(Node {
|
||||||
|
id: unsafe { std::str::from_utf8_unchecked(&ids[&target]).to_string() },
|
||||||
|
subject: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].subject().to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
from: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_from_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
to: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_to_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
date: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].date())
|
||||||
|
.unwrap_or(0),
|
||||||
|
references: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| envelopes[h].field_references_to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
in_reply_to: nodes[&target]
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|h| envelopes[h].in_reply_to())
|
||||||
|
.map(|h| h.to_string())
|
||||||
|
.unwrap_or("missing".into()),
|
||||||
|
});
|
||||||
|
graph.links.push(Link {
|
||||||
|
source: parent,
|
||||||
|
target: unsafe { std::str::from_utf8_unchecked(&ids[&target]).to_string() },
|
||||||
|
});
|
||||||
|
|
||||||
|
stack.extend(
|
||||||
|
nodes[&target].children.iter().cloned().zip(
|
||||||
|
std::iter::repeat(
|
||||||
|
unsafe { std::str::from_utf8_unchecked(&ids[&target]) }.to_string(),
|
||||||
|
)
|
||||||
|
.take(nodes[&target].children.len()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let s = serde_json::to_string(&graph).unwrap();
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut file = File::create(format!(
|
||||||
|
"/tmp/meli/threads/threads_{}.json",
|
||||||
|
crate::datetime::now()
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
file.write_all(s.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user