parent
c619d46861
commit
f6533d51c7
|
@ -50,7 +50,6 @@ use std::fs;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -98,12 +97,11 @@ macro_rules! get_path_hash {
|
|||
}
|
||||
|
||||
fn get_file_hash(file: &Path) -> EnvelopeHash {
|
||||
let mut buf = Vec::new();
|
||||
let mut buf = Vec::with_capacity(2048);
|
||||
let mut f = fs::File::open(&file).unwrap_or_else(|_| panic!("Can't open {}", file.display()));
|
||||
f.read_to_end(&mut buf)
|
||||
.unwrap_or_else(|_| panic!("Can't read {}", file.display()));
|
||||
let mut hasher = FnvHasher::default();
|
||||
hasher.write(file.as_os_str().as_bytes());
|
||||
hasher.write(&buf);
|
||||
hasher.finish()
|
||||
}
|
||||
|
@ -412,17 +410,20 @@ impl MaildirType {
|
|||
let map = map.clone();
|
||||
let len = c.len();
|
||||
for file in c {
|
||||
//thread::yield_now();
|
||||
|
||||
/* Check if we have a cache file with this email's
|
||||
* filename */
|
||||
let file_name = PathBuf::from(file)
|
||||
.strip_prefix(&root_path)
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
if let Some(cached) =
|
||||
let hash = if let Some(cached) =
|
||||
cache_dir.find_cache_file(&file_name)
|
||||
{
|
||||
/* Cached struct exists, try to load it */
|
||||
let reader = io::BufReader::new(
|
||||
fs::File::open(cached).unwrap(),
|
||||
fs::File::open(&cached).unwrap(),
|
||||
);
|
||||
let result: result::Result<Envelope, _> = bincode::deserialize_from(reader);
|
||||
if let Ok(env) = result {
|
||||
|
@ -431,13 +432,26 @@ impl MaildirType {
|
|||
if (*map).contains_key(&hash) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(*map).insert(hash, (0, file.clone()));
|
||||
local_r.push(env);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut reader = io::BufReader::new(
|
||||
fs::File::open(cached).unwrap(),
|
||||
);
|
||||
let mut buf = Vec::with_capacity(2048);
|
||||
reader.read_to_end(&mut buf).unwrap_or_else(|_| {
|
||||
panic!("Can't read {}", file.display())
|
||||
});
|
||||
let mut hasher = FnvHasher::default();
|
||||
hasher.write(&buf);
|
||||
hasher.finish()
|
||||
} else {
|
||||
get_file_hash(file)
|
||||
};
|
||||
{
|
||||
let hash = get_file_hash(file);
|
||||
{
|
||||
let mut map = map.lock().unwrap();
|
||||
if (*map).contains_key(&hash) {
|
||||
|
|
|
@ -205,15 +205,19 @@ impl BackendFolder for MaildirFolder {
|
|||
fn hash(&self) -> FolderHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn change_name(&mut self, s: &str) {
|
||||
self.name = s.to_string();
|
||||
}
|
||||
|
||||
fn children(&self) -> &Vec<usize> {
|
||||
&self.children
|
||||
}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
Box::new(MaildirFolder {
|
||||
hash: self.hash,
|
||||
|
|
|
@ -242,17 +242,22 @@ impl BackendFolder for DummyFolder {
|
|||
fn hash(&self) -> FolderHash {
|
||||
0
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
fn change_name(&mut self, _s: &str) {}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
folder_default()
|
||||
}
|
||||
|
||||
fn children(&self) -> &Vec<usize> {
|
||||
&self.v
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folder_default() -> Folder {
|
||||
Box::new(DummyFolder {
|
||||
v: Vec::with_capacity(0),
|
||||
|
@ -260,4 +265,4 @@ pub fn folder_default() -> Folder {
|
|||
}
|
||||
|
||||
pub type FolderHash = u64;
|
||||
pub type Folder = Box<BackendFolder>;
|
||||
pub type Folder = Box<BackendFolder + Send>;
|
||||
|
|
|
@ -21,7 +21,7 @@ pub struct Collection {
|
|||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn new(vec: Vec<Envelope>, name: &str) -> Collection {
|
||||
pub fn new(vec: Vec<Envelope>, folder: &Folder) -> Collection {
|
||||
let mut envelopes: FnvHashMap<EnvelopeHash, Envelope> =
|
||||
FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default());
|
||||
for e in vec {
|
||||
|
@ -30,7 +30,9 @@ impl Collection {
|
|||
let date_index = BTreeMap::new();
|
||||
let subject_index = None;
|
||||
|
||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", name).unwrap();
|
||||
let cache_dir =
|
||||
xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", folder.hash()))
|
||||
.unwrap();
|
||||
let threads = if let Some(cached) = cache_dir.find_cache_file("threads") {
|
||||
let reader = io::BufReader::new(fs::File::open(cached).unwrap());
|
||||
let result: result::Result<Threads, _> = bincode::deserialize_from(reader);
|
||||
|
@ -38,10 +40,34 @@ impl Collection {
|
|||
cached_t.update(&mut envelopes);
|
||||
cached_t
|
||||
} else {
|
||||
Threads::new(&mut envelopes) // sent_folder);
|
||||
let ret = Threads::new(&mut envelopes); // sent_folder);
|
||||
if let Ok(cached) = cache_dir.place_cache_file("threads") {
|
||||
/* place result in cache directory */
|
||||
let f = match fs::File::create(cached) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
panic!("{}", e);
|
||||
}
|
||||
};
|
||||
let writer = io::BufWriter::new(f);
|
||||
bincode::serialize_into(writer, &ret).unwrap();
|
||||
}
|
||||
ret
|
||||
}
|
||||
} else {
|
||||
Threads::new(&mut envelopes) // sent_folder);
|
||||
let ret = Threads::new(&mut envelopes); // sent_folder);
|
||||
if let Ok(cached) = cache_dir.place_cache_file("threads") {
|
||||
/* place result in cache directory */
|
||||
let f = match fs::File::create(cached) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
panic!("{}", e);
|
||||
}
|
||||
};
|
||||
let writer = io::BufWriter::new(f);
|
||||
bincode::serialize_into(writer, &ret).unwrap();
|
||||
}
|
||||
ret
|
||||
};
|
||||
Collection {
|
||||
envelopes,
|
||||
|
@ -59,8 +85,9 @@ impl Collection {
|
|||
self.envelopes.is_empty()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, hash: EnvelopeHash, mut envelope: Envelope) {
|
||||
self.threads.insert(&mut envelope);
|
||||
pub fn insert(&mut self, mut envelope: Envelope) {
|
||||
self.threads.insert(&mut envelope, &self.envelopes);
|
||||
let hash = envelope.hash();
|
||||
self.envelopes.insert(hash, envelope);
|
||||
}
|
||||
pub(crate) fn insert_reply(&mut self, envelope: Envelope) {
|
||||
|
|
|
@ -67,12 +67,12 @@ impl Default for Mailbox {
|
|||
}
|
||||
|
||||
impl Mailbox {
|
||||
pub fn new(folder: &Folder, envelopes: Result<Vec<Envelope>>) -> Result<Mailbox> {
|
||||
pub fn new(folder: Folder, envelopes: Result<Vec<Envelope>>) -> Result<Mailbox> {
|
||||
let mut envelopes: Vec<Envelope> = envelopes?;
|
||||
envelopes.sort_by(|a, b| a.date().cmp(&b.date()));
|
||||
let collection = Collection::new(envelopes, folder.name());
|
||||
let collection = Collection::new(envelopes, &folder);
|
||||
Ok(Mailbox {
|
||||
folder: (*folder).clone(),
|
||||
folder,
|
||||
collection,
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -118,12 +118,12 @@ impl Mailbox {
|
|||
|
||||
pub fn update(&mut self, old_hash: EnvelopeHash, envelope: Envelope) {
|
||||
self.collection.remove(&old_hash);
|
||||
self.collection.insert(envelope.hash(), envelope);
|
||||
self.collection.insert(envelope);
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, envelope: Envelope) -> &Envelope {
|
||||
let hash = envelope.hash();
|
||||
self.collection.insert(hash, envelope);
|
||||
self.collection.insert(envelope);
|
||||
&self.collection[&hash]
|
||||
}
|
||||
|
||||
|
|
|
@ -32,11 +32,13 @@
|
|||
* user having mutable ownership.
|
||||
*/
|
||||
|
||||
use mailbox::email::parser::BytesExt;
|
||||
use mailbox::email::*;
|
||||
|
||||
extern crate fnv;
|
||||
use self::fnv::{FnvHashMap, FnvHashSet};
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::cmp;
|
||||
use std::cmp::Ordering;
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
|
@ -89,21 +91,21 @@ macro_rules! make {
|
|||
}
|
||||
|
||||
/* Strip common prefixes from subjects */
|
||||
trait SubjectPrefix {
|
||||
fn strip_prefixes(&mut self) -> bool;
|
||||
trait SubjectPrefix<'a> {
|
||||
fn strip_prefixes(&'a mut self) -> bool;
|
||||
}
|
||||
|
||||
impl SubjectPrefix for String {
|
||||
fn strip_prefixes(&mut self) -> bool {
|
||||
impl<'a> SubjectPrefix<'a> for &'a [u8] {
|
||||
fn strip_prefixes(&'a mut self) -> bool {
|
||||
let mut ret: bool = false;
|
||||
let result = {
|
||||
let mut slice = self.trim();
|
||||
let mut end_prefix_idx = 0;
|
||||
loop {
|
||||
if slice.starts_with("RE: ")
|
||||
|| slice.starts_with("Re: ")
|
||||
|| slice.starts_with("FW: ")
|
||||
|| slice.starts_with("Fw: ")
|
||||
if slice.starts_with(b"RE: ")
|
||||
|| slice.starts_with(b"Re: ")
|
||||
|| slice.starts_with(b"FW: ")
|
||||
|| slice.starts_with(b"Fw: ")
|
||||
{
|
||||
if end_prefix_idx == 0 {
|
||||
ret = true;
|
||||
|
@ -112,23 +114,23 @@ impl SubjectPrefix for String {
|
|||
end_prefix_idx += 3;
|
||||
continue;
|
||||
}
|
||||
if slice.starts_with("FWD: ")
|
||||
|| slice.starts_with("Fwd: ")
|
||||
|| slice.starts_with("fwd: ")
|
||||
if slice.starts_with(b"FWD: ")
|
||||
|| slice.starts_with(b"Fwd: ")
|
||||
|| slice.starts_with(b"fwd: ")
|
||||
{
|
||||
slice = &slice[4..];
|
||||
end_prefix_idx += 4;
|
||||
continue;
|
||||
}
|
||||
if slice.starts_with(' ') || slice.starts_with('\t') || slice.starts_with('\r') {
|
||||
if slice.starts_with(b" ") || slice.starts_with(b"\t") || slice.starts_with(b"\r") {
|
||||
slice = &slice[1..];
|
||||
end_prefix_idx += 1;
|
||||
continue;
|
||||
}
|
||||
if slice.starts_with('[')
|
||||
&& !(slice.starts_with("[PATCH") || slice.starts_with("[RFC"))
|
||||
if slice.starts_with(b"[")
|
||||
&& !(slice.starts_with(b"[PATCH") || slice.starts_with(b"[RFC"))
|
||||
{
|
||||
if let Some(pos) = slice.find(']') {
|
||||
if let Some(pos) = slice.find(b"]") {
|
||||
end_prefix_idx += pos;
|
||||
slice = &slice[pos..];
|
||||
continue;
|
||||
|
@ -139,22 +141,22 @@ impl SubjectPrefix for String {
|
|||
}
|
||||
break;
|
||||
}
|
||||
slice.to_string()
|
||||
slice
|
||||
};
|
||||
mem::replace(self, result);
|
||||
*self = result;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorting states. */
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize)]
|
||||
pub enum SortOrder {
|
||||
Asc,
|
||||
Desc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize)]
|
||||
pub enum SortField {
|
||||
Subject,
|
||||
Date,
|
||||
|
@ -197,7 +199,7 @@ impl FromStr for SortOrder {
|
|||
/*
|
||||
* The thread tree holds the sorted state of the thread nodes */
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct ThreadTree {
|
||||
id: usize,
|
||||
children: Vec<ThreadTree>,
|
||||
|
@ -262,7 +264,7 @@ impl<'a> Iterator for ThreadIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ThreadNode {
|
||||
message: Option<EnvelopeHash>,
|
||||
parent: Option<usize>,
|
||||
|
@ -339,13 +341,13 @@ impl ThreadNode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Threads {
|
||||
thread_nodes: Vec<ThreadNode>,
|
||||
root_set: RefCell<Vec<usize>>,
|
||||
tree: RefCell<Vec<ThreadTree>>,
|
||||
|
||||
message_ids: FnvHashMap<String, usize>,
|
||||
message_ids: FnvHashMap<Vec<u8>, usize>,
|
||||
hash_set: FnvHashSet<EnvelopeHash>,
|
||||
sort: RefCell<(SortField, SortOrder)>,
|
||||
subsort: RefCell<(SortField, SortOrder)>,
|
||||
|
@ -503,7 +505,7 @@ impl Threads {
|
|||
let thread_nodes: Vec<ThreadNode> =
|
||||
Vec::with_capacity((collection.len() as f64 * 1.2) as usize);
|
||||
/* A hash table of Message IDs */
|
||||
let message_ids: FnvHashMap<String, usize> =
|
||||
let message_ids: FnvHashMap<Vec<u8>, usize> =
|
||||
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
|
||||
let hash_set: FnvHashSet<EnvelopeHash> =
|
||||
FnvHashSet::with_capacity_and_hasher(collection.len(), Default::default());
|
||||
|
@ -541,20 +543,17 @@ impl Threads {
|
|||
* messages which don't have References headers at all still get threaded (to the extent
|
||||
* possible, at least.)"
|
||||
*/
|
||||
let mut subject_table: FnvHashMap<String, (bool, usize)> =
|
||||
let mut subject_table: FnvHashMap<Vec<u8>, (bool, usize)> =
|
||||
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
|
||||
|
||||
for r in &root_set {
|
||||
/* "Find the subject of that sub-tree": */
|
||||
let (mut subject, mut is_re): (String, bool) = if t.thread_nodes[*r].message.is_some() {
|
||||
let (mut subject, mut is_re): (_, bool) = if t.thread_nodes[*r].message.is_some() {
|
||||
/* "If there is a message in the Container, the subject is the subject of that
|
||||
* message. " */
|
||||
let msg_idx = t.thread_nodes[*r].message.unwrap();
|
||||
let envelope = &collection[&msg_idx];
|
||||
(
|
||||
envelope.subject().to_string(),
|
||||
!envelope.references().is_empty(),
|
||||
)
|
||||
(envelope.subject(), !envelope.references().is_empty())
|
||||
} else {
|
||||
/* "If there is no message in the Container, then the Container will have at least
|
||||
* one child Container, and that Container will have a message. Use the subject of
|
||||
|
@ -563,24 +562,22 @@ impl Threads {
|
|||
.message
|
||||
.unwrap();
|
||||
let envelope = &collection[&msg_idx];
|
||||
(
|
||||
envelope.subject().to_string(),
|
||||
!envelope.references().is_empty(),
|
||||
)
|
||||
(envelope.subject(), !envelope.references().is_empty())
|
||||
};
|
||||
|
||||
/* "Strip ``Re:'', ``RE:'', ``RE[5]:'', ``Re: Re[4]: Re:'' and so on." */
|
||||
/* References of this envelope can be empty but if the subject contains a ``Re:``
|
||||
* prefix, it's a reply */
|
||||
is_re |= subject.strip_prefixes();
|
||||
let mut stripped_subj = subject.to_mut().as_bytes();
|
||||
is_re |= stripped_subj.strip_prefixes();
|
||||
|
||||
if subject.is_empty() {
|
||||
if stripped_subj.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* "Add this Container to the subject_table if:" */
|
||||
if subject_table.contains_key(&subject) {
|
||||
let (other_is_re, id) = subject_table[&subject];
|
||||
if subject_table.contains_key(stripped_subj) {
|
||||
let (other_is_re, id) = subject_table[stripped_subj];
|
||||
/* "This one is an empty container and the old one is not: the empty one is more
|
||||
* interesting as a root, so put it in the table instead."
|
||||
* or
|
||||
|
@ -590,11 +587,14 @@ impl Threads {
|
|||
if (!t.thread_nodes[id].has_message() && t.thread_nodes[*r].has_message())
|
||||
|| (other_is_re && !is_re)
|
||||
{
|
||||
mem::replace(subject_table.entry(subject).or_default(), (is_re, *r));
|
||||
mem::replace(
|
||||
subject_table.entry(stripped_subj.to_vec()).or_default(),
|
||||
(is_re, *r),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
/* "There is no container in the table with this subject" */
|
||||
subject_table.insert(subject, (is_re, *r));
|
||||
subject_table.insert(stripped_subj.to_vec(), (is_re, *r));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,30 +603,25 @@ impl Threads {
|
|||
for i in 0..root_set.len() {
|
||||
let r = root_set[i];
|
||||
/* "Find the subject of this Container (as above.)" */
|
||||
let (mut subject, mut is_re): (String, bool) = if t.thread_nodes[r].message.is_some() {
|
||||
let (mut subject, mut is_re): (_, bool) = if t.thread_nodes[r].message.is_some() {
|
||||
let msg_idx = t.thread_nodes[r].message.unwrap();
|
||||
let envelope = &collection[&msg_idx];
|
||||
(
|
||||
envelope.subject().to_string(),
|
||||
!envelope.references().is_empty(),
|
||||
)
|
||||
(envelope.subject(), !envelope.references().is_empty())
|
||||
} else {
|
||||
let msg_idx = t.thread_nodes[t.thread_nodes[r].children[0]]
|
||||
.message
|
||||
.unwrap();
|
||||
let envelope = &collection[&msg_idx];
|
||||
(
|
||||
envelope.subject().to_string(),
|
||||
!envelope.references().is_empty(),
|
||||
)
|
||||
(envelope.subject(), !envelope.references().is_empty())
|
||||
};
|
||||
|
||||
let mut subject = subject.to_mut().as_bytes();
|
||||
is_re |= subject.strip_prefixes();
|
||||
if subject.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 !t.thread_nodes[other_idx].has_message() || other_idx == r {
|
||||
continue;
|
||||
|
@ -723,12 +718,25 @@ impl Threads {
|
|||
let difference: Vec<EnvelopeHash> =
|
||||
new_hash_set.difference(&self.hash_set).cloned().collect();
|
||||
for h in difference {
|
||||
self.insert(collection.entry(h).or_default());
|
||||
let env = collection.entry(h).or_default() as *mut Envelope;
|
||||
unsafe {
|
||||
// `collection` is borrowed immutably and `insert` only changes the envelope's
|
||||
// `thread` field.
|
||||
self.insert(&mut (*env), collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, envelope: &mut Envelope) {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
envelope: &mut Envelope,
|
||||
collection: &FnvHashMap<EnvelopeHash, Envelope>,
|
||||
) {
|
||||
self.link_envelope(envelope);
|
||||
{
|
||||
let id = self.message_ids[envelope.message_id().raw()];
|
||||
self.rebuild_thread(id, collection);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_reply(
|
||||
|
@ -736,10 +744,10 @@ impl Threads {
|
|||
envelope: Envelope,
|
||||
collection: &mut FnvHashMap<EnvelopeHash, Envelope>,
|
||||
) -> bool {
|
||||
return false;
|
||||
//return false;
|
||||
{
|
||||
let in_reply_to = envelope.in_reply_to_raw();
|
||||
if !self.message_ids.contains_key(in_reply_to.as_ref()) {
|
||||
let in_reply_to = envelope.in_reply_to_bytes();
|
||||
if !self.message_ids.contains_key(in_reply_to) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -754,8 +762,8 @@ impl Threads {
|
|||
}
|
||||
let envelope: &Envelope = &collection[&hash];
|
||||
{
|
||||
let in_reply_to = envelope.in_reply_to_raw();
|
||||
let parent_id = self.message_ids[in_reply_to.as_ref()];
|
||||
let in_reply_to = envelope.in_reply_to_bytes();
|
||||
let parent_id = self.message_ids[in_reply_to];
|
||||
self.rebuild_thread(parent_id, collection);
|
||||
}
|
||||
true
|
||||
|
@ -781,11 +789,13 @@ impl Threads {
|
|||
* let tree = &mut tree[s].children;
|
||||
*/
|
||||
let temp_tree = tree;
|
||||
let pos = temp_tree.iter().position(|v| v.id == s).unwrap();
|
||||
match temp_tree[pos].children {
|
||||
ref mut v => {
|
||||
tree = v;
|
||||
}
|
||||
if let Some(pos) = temp_tree.iter().position(|v| v.id == s) {
|
||||
tree = &mut temp_tree[pos].children;
|
||||
} else {
|
||||
let tree_node = ThreadTree::new(s);
|
||||
temp_tree.push(tree_node);
|
||||
let new_id = temp_tree.len() - 1;
|
||||
tree = &mut temp_tree[new_id].children;
|
||||
}
|
||||
}
|
||||
/* Add new child */
|
||||
|
@ -951,38 +961,40 @@ impl Threads {
|
|||
}
|
||||
|
||||
fn link_envelope(&mut self, envelope: &mut Envelope) {
|
||||
let m_id = envelope.message_id_raw().to_string();
|
||||
let t_idx: usize = {
|
||||
let m_id = envelope.message_id().raw();
|
||||
|
||||
/* t_idx: The index of this message's ThreadNode in thread_nodes
|
||||
*
|
||||
* If id_table contains an empty Container for this ID:
|
||||
* Store this message in the Container's message slot.
|
||||
* Else:
|
||||
* Create a new Container object holding this message;
|
||||
* Index the Container by Message-ID in id_table.
|
||||
*/
|
||||
let t_idx: usize = if self.message_ids.get(&m_id).is_some() {
|
||||
let node_idx = self.message_ids[&m_id];
|
||||
/* the already existing ThreadNote should be empty, since we're
|
||||
* seeing this message for the first time. otherwise it's a
|
||||
* duplicate. */
|
||||
if self.thread_nodes[node_idx].message.is_some() {
|
||||
return;
|
||||
/* t_idx: The index of this message's ThreadNode in thread_nodes
|
||||
*
|
||||
* If id_table contains an empty Container for this ID:
|
||||
* Store this message in the Container's message slot.
|
||||
* Else:
|
||||
* Create a new Container object holding this message;
|
||||
* Index the Container by Message-ID in id_table.
|
||||
*/
|
||||
if self.message_ids.get(m_id).is_some() {
|
||||
let node_idx = self.message_ids[m_id];
|
||||
/* the already existing ThreadNote should be empty, since we're
|
||||
* seeing this message for the first time. otherwise it's a
|
||||
* duplicate. */
|
||||
if self.thread_nodes[node_idx].message.is_some() {
|
||||
return;
|
||||
}
|
||||
node_idx
|
||||
} else {
|
||||
/* Create a new ThreadNode object holding this message */
|
||||
self.thread_nodes.push(ThreadNode {
|
||||
message: Some(envelope.hash()),
|
||||
date: envelope.date(),
|
||||
..Default::default()
|
||||
});
|
||||
/* The new thread node's set is just itself */
|
||||
let new_id = self.thread_nodes.len() - 1;
|
||||
self.thread_nodes[new_id].thread_group = new_id;
|
||||
|
||||
self.message_ids.insert(m_id.to_vec(), new_id);
|
||||
new_id
|
||||
}
|
||||
node_idx
|
||||
} else {
|
||||
/* Create a new ThreadNode object holding this message */
|
||||
self.thread_nodes.push(ThreadNode {
|
||||
message: Some(envelope.hash()),
|
||||
date: envelope.date(),
|
||||
..Default::default()
|
||||
});
|
||||
/* The new thread node's set is just itself */
|
||||
let new_id = self.thread_nodes.len() - 1;
|
||||
self.thread_nodes[new_id].thread_group = new_id;
|
||||
|
||||
self.message_ids.insert(m_id, new_id);
|
||||
new_id
|
||||
};
|
||||
self.thread_nodes[t_idx].date = envelope.date();
|
||||
self.thread_nodes[t_idx].message = Some(envelope.hash());
|
||||
|
@ -1007,9 +1019,9 @@ impl Threads {
|
|||
}
|
||||
|
||||
for &refn in envelope.references().iter().rev() {
|
||||
let r_id = String::from_utf8_lossy(refn.raw()).into();
|
||||
let parent_id = if self.message_ids.contains_key(&r_id) {
|
||||
self.message_ids[&r_id]
|
||||
let r_id = refn.raw();
|
||||
let parent_id = if self.message_ids.contains_key(r_id) {
|
||||
self.message_ids[r_id]
|
||||
} else {
|
||||
/* Create a new ThreadNode object holding this reference */
|
||||
self.thread_nodes.push(ThreadNode {
|
||||
|
@ -1017,7 +1029,7 @@ impl Threads {
|
|||
});
|
||||
let new_id = self.thread_nodes.len() - 1;
|
||||
self.thread_nodes[new_id].thread_group = new_id;
|
||||
self.message_ids.insert(r_id, new_id);
|
||||
self.message_ids.insert(r_id.to_vec(), new_id);
|
||||
new_id
|
||||
};
|
||||
/* If they are already linked, don't change the existing links. */
|
||||
|
@ -1035,21 +1047,6 @@ impl Threads {
|
|||
self.union(ref_ptr, parent_id);
|
||||
make!((parent_id) parent of (ref_ptr), &mut self.thread_nodes);
|
||||
}
|
||||
|
||||
/* Update thread's date */
|
||||
let mut parent_iter = parent_id;
|
||||
'date: loop {
|
||||
let p: &mut ThreadNode = &mut self.thread_nodes[parent_iter];
|
||||
if p.date < envelope.date() {
|
||||
p.date = envelope.date();
|
||||
}
|
||||
if let Some(p) = p.parent {
|
||||
parent_iter = p;
|
||||
} else {
|
||||
break 'date;
|
||||
}
|
||||
}
|
||||
ref_ptr = parent_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1069,6 +1066,26 @@ impl Index<usize> for Threads {
|
|||
.expect("thread index out of bounds")
|
||||
}
|
||||
}
|
||||
/*
|
||||
*
|
||||
*
|
||||
/* Update thread's date */
|
||||
let mut parent_iter = parent_id;
|
||||
'date: loop {
|
||||
let p: &mut ThreadNode = &mut self.thread_nodes[parent_iter];
|
||||
if p.date < envelope.date() {
|
||||
p.date = envelope.date();
|
||||
}
|
||||
if let Some(p) = p.parent {
|
||||
parent_iter = p;
|
||||
} else {
|
||||
break 'date;
|
||||
}
|
||||
}
|
||||
ref_ptr = parent_id;
|
||||
*
|
||||
*
|
||||
* */
|
||||
|
||||
fn node_build(
|
||||
tree: &mut ThreadTree,
|
||||
|
@ -1080,10 +1097,12 @@ fn node_build(
|
|||
if let Some(hash) = thread_nodes[idx].message {
|
||||
if let Some(parent_id) = thread_nodes[idx].parent {
|
||||
if let Some(parent_hash) = thread_nodes[parent_id].message {
|
||||
let mut subject = collection[&hash].subject().to_string();
|
||||
subject.strip_prefixes();
|
||||
let mut parent_subject = collection[&parent_hash].subject().to_string();
|
||||
parent_subject.strip_prefixes();
|
||||
let mut subject = collection[&hash].subject();
|
||||
let mut subject = subject.to_mut().as_bytes();
|
||||
let subject = subject.strip_prefixes();
|
||||
let mut parent_subject = collection[&parent_hash].subject();
|
||||
let mut parent_subject = parent_subject.to_mut().as_bytes();
|
||||
let parent_subject = parent_subject.strip_prefixes();
|
||||
if subject == parent_subject {
|
||||
thread_nodes[idx].show_subject = false;
|
||||
}
|
||||
|
@ -1103,15 +1122,16 @@ fn node_build(
|
|||
let mut has_unseen = thread_nodes[idx].has_unseen;
|
||||
let mut child_vec: Vec<ThreadTree> = Vec::new();
|
||||
|
||||
let mut length = thread_nodes[idx].children.len();
|
||||
thread_nodes[idx].len = thread_nodes[idx].children.len();
|
||||
for c in thread_nodes[idx].children.clone() {
|
||||
let mut new_tree = ThreadTree::new(c);
|
||||
node_build(&mut new_tree, c, thread_nodes, indentation, collection);
|
||||
length += thread_nodes[c].len;
|
||||
thread_nodes[idx].len += thread_nodes[c].len;
|
||||
thread_nodes[idx].date = cmp::max(thread_nodes[idx].date, thread_nodes[c].date);
|
||||
|
||||
has_unseen |= thread_nodes[c].has_unseen;
|
||||
child_vec.push(new_tree);
|
||||
}
|
||||
tree.children = child_vec;
|
||||
thread_nodes[idx].len = length;
|
||||
thread_nodes[idx].has_unseen = has_unseen;
|
||||
}
|
||||
|
|
|
@ -33,9 +33,10 @@ use melib::error::Result;
|
|||
use std::ops::{Index, IndexMut};
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use types::UIEventType::{self, Notification};
|
||||
|
||||
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
|
||||
pub type Worker = Option<Async<Result<Mailbox>>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Account {
|
||||
|
@ -64,8 +65,12 @@ impl Account {
|
|||
let notify_fn = Arc::new(notify_fn);
|
||||
for f in ref_folders {
|
||||
folders.push(None);
|
||||
let handle = backend.get(&f, notify_fn.clone());
|
||||
workers.push(Some(handle));
|
||||
workers.push(Account::new_worker(
|
||||
&name,
|
||||
f,
|
||||
&mut backend,
|
||||
notify_fn.clone(),
|
||||
));
|
||||
}
|
||||
Account {
|
||||
name,
|
||||
|
@ -78,6 +83,30 @@ impl Account {
|
|||
notify_fn,
|
||||
}
|
||||
}
|
||||
fn new_worker(
|
||||
name: &str,
|
||||
folder: Folder,
|
||||
backend: &mut Box<MailBackend>,
|
||||
notify_fn: Arc<NotifyFn>,
|
||||
) -> Worker {
|
||||
let mailbox_handle = backend.get(&folder, notify_fn.clone());
|
||||
let mut builder = AsyncBuilder::new();
|
||||
let tx = builder.tx();
|
||||
Some(
|
||||
builder.build(
|
||||
thread::Builder::new()
|
||||
.name(format!("Loading {}", name))
|
||||
.spawn(move || {
|
||||
let envelopes = mailbox_handle.join();
|
||||
let ret = Mailbox::new(folder, envelopes);
|
||||
tx.send(AsyncStatus::Finished);
|
||||
notify_fn.notify();
|
||||
ret
|
||||
})
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
}
|
||||
pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option<UIEventType> {
|
||||
let kind = event.kind();
|
||||
let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
|
||||
|
@ -103,8 +132,13 @@ impl Account {
|
|||
}
|
||||
RefreshEventKind::Rescan => {
|
||||
let ref_folders: Vec<Folder> = self.backend.folders();
|
||||
let handle = self.backend.get(&ref_folders[idx], self.notify_fn.clone());
|
||||
self.workers[idx] = Some(handle);
|
||||
let handle = Account::new_worker(
|
||||
&self.name,
|
||||
ref_folders[idx].clone(),
|
||||
&mut self.backend,
|
||||
self.notify_fn.clone(),
|
||||
);
|
||||
self.workers[idx] = handle;
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -140,17 +174,15 @@ impl Account {
|
|||
&mut self.workers
|
||||
}
|
||||
|
||||
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) {
|
||||
let folders = self.backend.folders();
|
||||
let folder = &folders[index];
|
||||
fn load_mailbox(&mut self, index: usize, mailbox: Result<Mailbox>) {
|
||||
if self.sent_folder.is_some() && self.sent_folder.unwrap() == index {
|
||||
self.folders[index] = Some(Mailbox::new(folder, envelopes));
|
||||
self.folders[index] = Some(mailbox);
|
||||
/* Add our replies to other folders */
|
||||
for id in (0..self.folders.len()).filter(|i| *i != index) {
|
||||
self.add_replies_to_folder(id);
|
||||
}
|
||||
} else {
|
||||
self.folders[index] = Some(Mailbox::new(folder, envelopes));
|
||||
self.folders[index] = Some(mailbox);
|
||||
self.add_replies_to_folder(index);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue