summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2017-09-16 14:14:08 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-10 19:14:20 +0300
commitcddea885f2eccbb590957f812c78c1b41d6e6bee (patch)
treea9208b8e868d23f9a42f64bef35c47fd1b5366d3
parent655b5a6ea78ae86c8f8c7f2da1e958799ec4ae4a (diff)
fix lints, rename types, add thread module
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
-rw-r--r--benches/maildir.rs1
-rw-r--r--benches/parse.rs4
-rw-r--r--src/bin.rs6
-rw-r--r--src/mailbox/accounts.rs2
-rw-r--r--src/mailbox/backends/maildir.rs10
-rw-r--r--src/mailbox/backends/mod.rs12
-rw-r--r--src/mailbox/email/mod.rs31
-rw-r--r--src/mailbox/email/parser.rs9
-rw-r--r--src/mailbox/mod.rs373
-rw-r--r--src/mailbox/thread.rs395
-rw-r--r--src/ui/index.rs6
-rw-r--r--src/ui/pager.rs8
12 files changed, 444 insertions, 413 deletions
diff --git a/benches/maildir.rs b/benches/maildir.rs
index a5377323..8a0c2c5a 100644
--- a/benches/maildir.rs
+++ b/benches/maildir.rs
@@ -1,6 +1,5 @@
#![feature(test)]
extern crate melib;
-use melib::mailbox::backends::MailBackend;
use melib::mailbox::backends::maildir::*;
extern crate test;
diff --git a/benches/parse.rs b/benches/parse.rs
index 96448114..c1fced80 100644
--- a/benches/parse.rs
+++ b/benches/parse.rs
@@ -1,7 +1,7 @@
#![feature(test)]
extern crate melib;
-use melib::mailbox::email::Mail;
+use melib::mailbox::email::Envelope;
use melib::mailbox::backends::BackendOpGenerator;
use melib::mailbox::backends::maildir::MaildirOp;
@@ -10,6 +10,6 @@ use self::test::Bencher;
#[bench]
fn mail_parse(b: &mut Bencher) {
- b.iter(|| Mail::from(Box::new(BackendOpGenerator::new(Box::new(move || {
+ b.iter(|| Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || {
Box::new(MaildirOp::new("test/attachment_test".to_string()))})))) );
}
diff --git a/src/bin.rs b/src/bin.rs
index c8d527b7..bc21d1f1 100644
--- a/src/bin.rs
+++ b/src/bin.rs
@@ -41,11 +41,11 @@ fn main() {
ncurses::touchwin(ncurses::stdscr());
ncurses::mv(0,0);
let mailbox = &mut account[j];
- let mut index: Box<Window> = match mailbox.as_ref().unwrap() {
- &Ok(ref v) => {
+ let mut index: Box<Window> = match *mailbox.as_ref().unwrap() {
+ Ok(ref v) => {
Box::new(Index::new(v))
},
- &Err(ref v) => {
+ Err(ref v) => {
Box::new(ErrorWindow::new((*v).clone()))
}
};
diff --git a/src/mailbox/accounts.rs b/src/mailbox/accounts.rs
index 2d047109..a040ad23 100644
--- a/src/mailbox/accounts.rs
+++ b/src/mailbox/accounts.rs
@@ -37,7 +37,7 @@ pub struct Account {
impl Account {
pub fn new(name: String, settings: AccountSettings) -> Self {
eprintln!("new acc" );
- let sent_folder = settings.folders.iter().position(|ref x| **x == settings.sent_folder);
+ let sent_folder = settings.folders.iter().position(|x| *x == settings.sent_folder);
let mut folders = Vec::with_capacity(settings.folders.len());
for _ in 0..settings.folders.len() {
folders.push(None);
diff --git a/src/mailbox/backends/maildir.rs b/src/mailbox/backends/maildir.rs
index e855e7e3..1fc9a01b 100644
--- a/src/mailbox/backends/maildir.rs
+++ b/src/mailbox/backends/maildir.rs
@@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
-use mailbox::email::Mail;
+use mailbox::email::Envelope;
use error::{MeliError, Result};
use mailbox::backends::{MailBackend, BackendOp, BackendOpGenerator};
use mailbox::email::parser;
@@ -81,7 +81,7 @@ impl BackendOp for MaildirOp {
impl MailBackend for MaildirType {
- fn get(&self) -> Result<Vec<Mail>> {
+ fn get(&self) -> Result<Vec<Envelope>> {
self.get_multicore(4)
/*
@@ -124,7 +124,7 @@ impl MaildirType {
}
Ok(())
}
- pub fn get_multicore(&self, cores: usize) -> Result<Vec<Mail>> {
+ pub fn get_multicore(&self, cores: usize) -> Result<Vec<Envelope>> {
MaildirType::is_valid(&self.path)?;
let mut path = PathBuf::from(&self.path);
path.push("cur");
@@ -167,10 +167,10 @@ panic!("didn't parse"); },
};
for chunk in files.chunks(chunk_size) {
let s = scope.spawn(move || {
- let mut local_r:Vec<Mail> = Vec::with_capacity(chunk.len());
+ let mut local_r:Vec<Envelope> = Vec::with_capacity(chunk.len());
for e in chunk {
let e_copy = e.to_string();
- if let Some(e) = Mail::from(Box::new(BackendOpGenerator::new(Box::new(move || {
+ if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || {
Box::new(MaildirOp::new(e_copy.clone()))
} )))) {
local_r.push(e);
diff --git a/src/mailbox/backends/mod.rs b/src/mailbox/backends/mod.rs
index 1dbc11f5..33696ee5 100644
--- a/src/mailbox/backends/mod.rs
+++ b/src/mailbox/backends/mod.rs
@@ -20,18 +20,18 @@
*/
pub mod maildir;
-use mailbox::email::Mail;
+use mailbox::email::Envelope;
use error::Result;
use std::fmt;
pub trait MailBackend {
- fn get(&self) -> Result<Vec<Mail>>;
+ fn get(&self) -> Result<Vec<Envelope>>;
}
-/// A BackendOp manages common operations for the various mail backends. They only live for the
-/// duration of the operation. They are generated by BackendOpGenerator on demand.
+/// A `BackendOp` manages common operations for the various mail backends. They only live for the
+/// duration of the operation. They are generated by `BackendOpGenerator` on demand.
///
/// # Motivation
///
@@ -75,8 +75,8 @@ pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
fn fetch_body(&mut self) -> Result<&[u8]>;
}
-/// BackendOpGenerator is a wrapper for a closure that returns a BackendOp object
-/// See BackendOp for details.
+/// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object
+/// See `BackendOp` for details.
/*
* I know this sucks, but that's the best way I found that rustc deems safe.
* */
diff --git a/src/mailbox/email/mod.rs b/src/mailbox/email/mod.rs
index 0764238b..d4c420d9 100644
--- a/src/mailbox/email/mod.rs
+++ b/src/mailbox/email/mod.rs
@@ -96,7 +96,7 @@ struct References {
use std::sync::Arc;
/* A very primitive mail object */
#[derive(Debug, Clone)]
-pub struct Mail
+pub struct Envelope
{
date: String,
from: Option<String>,
@@ -115,7 +115,7 @@ pub struct Mail
}
-impl Mail
+impl Envelope
{
pub fn get_date(&self) -> i64 {
self.timestamp
@@ -146,7 +146,7 @@ impl Mail
Err(_) => {
let operation = self.operation_token.generate();
eprintln!("error in parsing mail\n{}", operation.description());
- panic!();
+ panic!()
},
};
let mut builder = AttachmentBuilder::new(body);
@@ -233,7 +233,7 @@ impl Mail
Some(ref mut s) => {
if s.refs.contains(&new_ref) {
if s.refs[s.refs.len() - 1] != new_ref {
- if let Some(index) = s.refs.iter().position(|ref x| **x == new_ref) {
+ if let Some(index) = s.refs.iter().position(|x| *x == new_ref) {
s.refs.remove(index);
} else {
panic!();
@@ -282,13 +282,10 @@ impl Mail
self.datetime = new_val;
if let Some(v) = self.datetime {
self.timestamp = v.timestamp();
- if self.timestamp == 1485962960 {
- eprintln!("it's {:?}", self);
- }
}
}
pub fn new(token: Box<BackendOpGenerator>) -> Self {
- Mail {
+ Envelope {
date: "".to_string(),
from: None,
to: None,
@@ -307,7 +304,7 @@ impl Mail
}
}
- pub fn from(operation_token: Box<BackendOpGenerator>) -> Option<Mail> {
+ pub fn from(operation_token: Box<BackendOpGenerator>) -> Option<Envelope> {
let mut operation = operation_token.generate();
let headers = match parser::headers(operation.fetch_headers().unwrap()).to_full_result() {
Ok(v) => {
@@ -320,7 +317,7 @@ impl Mail
},
};
- let mut mail = Mail::new(operation_token);
+ let mut mail = Envelope::new(operation_token);
let mut in_reply_to = None;
let mut datetime = None;
@@ -383,20 +380,20 @@ impl Mail
}
}
-impl Eq for Mail {}
-impl Ord for Mail {
- fn cmp(&self, other: &Mail) -> Ordering {
+impl Eq for Envelope {}
+impl Ord for Envelope {
+ fn cmp(&self, other: &Envelope) -> Ordering {
self.get_datetime().cmp(&other.get_datetime())
}
}
-impl PartialOrd for Mail {
- fn partial_cmp(&self, other: &Mail) -> Option<Ordering> {
+impl PartialOrd for Envelope {
+ fn partial_cmp(&self, other: &Envelope) -> Option<Ordering> {
Some(self.cmp(other))
}
}
-impl PartialEq for Mail {
- fn eq(&self, other: &Mail) -> bool {
+impl PartialEq for Envelope {
+ fn eq(&self, other: &Envelope) -> bool {
self.get_message_id_raw() == other.get_message_id_raw()
}
}
diff --git a/src/mailbox/email/parser.rs b/src/mailbox/email/parser.rs
index 5ded035d..81720b9d 100644
--- a/src/mailbox/email/parser.rs
+++ b/src/mailbox/email/parser.rs
@@ -30,9 +30,7 @@ use encoding::{Encoding, DecoderTrap};
fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> {
if input.is_empty() || input.len() < 3 {
IResult::Incomplete(Needed::Size(1))
- } else if input[0] != b'=' {
- IResult::Error(error_code!(ErrorKind::Custom(43)))
- } else if is_hex_digit(input[1]) && is_hex_digit(input[2]) {
+ } else if input[0] == b'=' && is_hex_digit(input[1]) && is_hex_digit(input[2]) {
let a = if input[1] < b':' {
input[1] - 48
} else if input[1] < b'[' {
@@ -47,7 +45,6 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> {
} else {
input[2] - 87
};
-
IResult::Done(&input[3..], a*16+b)
} else {
IResult::Error(error_code!(ErrorKind::Custom(43)))
@@ -350,8 +347,8 @@ fn message_id_peek(input: &[u8]) -> IResult<&[u8],&str> {
} else if input_length == 2 || input[0] != b'<' {
IResult::Error(error_code!(ErrorKind::Custom(43)))
} else {
- for i in 1..input_length {
- if input[i] == b'>' {
+ for (i, &x) in input.iter().take(input_length).enumerate().skip(1) {
+ if x == b'>' {
return IResult::Done(&input[i+1..], from_utf8(&input[0..i+1]).unwrap());
}
}
diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs
index 6c2ba1ed..6ceec1e2 100644
--- a/src/mailbox/mod.rs
+++ b/src/mailbox/mod.rs
@@ -28,386 +28,29 @@ use mailbox::backends::maildir;
use error::Result;
pub mod accounts;
pub use mailbox::accounts::Account;
+mod thread;
+use mailbox::thread::{Container, build_threads};
-extern crate fnv;
-
-use self::fnv::FnvHashMap;
use std::option::Option;
-use std;
-type UnixTimestamp = i64;
/*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/
#[derive(Debug,Clone)]
pub struct Mailbox{
pub path: String,
- pub collection: Vec<Mail>,
+ pub collection: Vec<Envelope>,
pub threaded_collection: Vec<usize>,
- threads: Vec<Thread>,
+ threads: Vec<Container>,
length: usize,
}
-/* a Thread struct is needed to describe the Thread tree forest during creation
- * of threads. Because of Rust's memory model, we store indexes of other node
- * instead of references and every reference is passed through the Thread owner
- * (a Vec<Thread>).
- *
- * message refers to a Mail entry in a Vec. If it's empty, the Thread is
- * nonexistent in our Mailbox but we know it exists (for example we have a copy
- * of a reply to a mail but we don't have its copy.
- */
-#[derive(Clone, Copy, Debug)]
-pub struct Thread {
- id: usize,
- message: Option<usize>,
- parent: Option<usize>,
- first_child: Option<usize>,
- next_sibling: Option<usize>,
- date: UnixTimestamp,
- indentation: usize,
- show_subject: bool,
-}
-
-impl Thread {
- pub fn get_message(&self) -> Option<usize> {
- self.message
- }
- pub fn get_parent(&self) -> Option<usize> {
- self.parent
- }
- pub fn has_parent(&self) -> bool {
- self.parent.is_some()
- }
- pub fn get_first_child(&self) -> Option<usize> {
- self.first_child
- }
- pub fn get_next_sibling(&self) -> Option<usize> {
- self.next_sibling
- }
- pub fn has_children(&self) -> bool {
- self.first_child.is_some()
- }
- pub fn has_sibling(&self) -> bool {
- self.next_sibling.is_some()
- }
- pub fn has_message(&self) -> bool {
- self.message.is_some()
- }
- fn set_indentation(&mut self, i: usize) {
- self.indentation = i;
- }
- pub fn get_indentation(&self) -> usize {
- self.indentation
- }
- fn is_descendant(&self, threads: &[Thread], other: &Thread) -> bool {
- if self == other {
- return true;
- }
-
- if let Some(v) = self.first_child {
- if threads[v].is_descendant(threads, other) {
- return true;
- }
- };
- if let Some(v) = self.next_sibling {
- if threads[v].is_descendant(threads, other) {
- return true;
- }
- };
- false
- }
- fn set_show_subject(&mut self, set: bool) -> () {
- self.show_subject = set;
- }
- pub fn get_show_subject(&self) -> bool {
- self.show_subject
- }
-}
-
-impl PartialEq for Thread {
- fn eq(&self, other: &Thread) -> bool {
- match (self.message, other.message) {
- (Some(s), Some(o)) => {
- s == o
- },
- _ => {
- self.id == other.id
- }
- }
- }
-}
-
-fn build_collection(threads: &mut Vec<Thread>, id_table: &mut FnvHashMap<std::string::String, usize>, collection: &mut [Mail]) -> ()
-{
- for (i, x) in collection.iter_mut().enumerate() {
- let x_index; /* x's index in threads */
- let m_id = x.get_message_id_raw().to_string();
- if id_table.contains_key(&m_id) {
- let t = id_table[&m_id];
- /* the already existing Thread should be empty, since we're
- * seeing this message for the first time */
- if threads[t].message.is_some() {
- /* skip duplicate message-id, but this should be handled instead */
- continue;
- }
- x_index = t;
- /* Store this message in the Thread's message slot. */
- threads[t].date = x.get_date();
- x.set_thread(t);
- threads[t].message = Some(i);
- } else {
- /* Create a new Thread object holding this message */
- x_index = threads.len();
- threads.push(
- Thread {
- message: Some(i),
- id: x_index,
- parent: None,
- first_child: None,
- next_sibling: None,
- date: x.get_date(),
- indentation: 0,
- show_subject: true,
- });
- x.set_thread(x_index);
- id_table.insert(m_id, x_index);
- }
- /* For each element in the message's References field:
- *
- * Find a Thread object for the given Message-ID:
- * If there's one in id_table use that;
- * Otherwise, make (and index) one with a null Message
- *
- * Link the References field's Threads together in the order implied by the References header.
- * If they are already linked, don't change the existing links.
- * Do not add a link if adding that link would introduce a loop: that is, before asserting A->B, search down the children of B to see if A is reachable, and also search down the children of A to see if B is reachable. If either is already reachable as a child of the other, don't add the link.
- */
- let mut curr_ref = x_index;
- let mut iasf = 0;
- for &r in x.get_references().iter().rev() {
- if iasf == 1 {
- continue;
- }
- iasf += 1;
- let parent_id =
- if id_table.contains_key(r.get_raw()) {
- let p = id_table[r.get_raw()];
- if !(threads[p].is_descendant(threads, &threads[curr_ref]) ||
- threads[curr_ref].is_descendant(threads, &threads[p])) {
- threads[curr_ref].parent = Some(p);
- if threads[p].first_child.is_none() {
- threads[p].first_child = Some(curr_ref);
- } else {
- let mut child_iter = threads[p].first_child.unwrap();
- while threads[child_iter].next_sibling.is_some() {
- threads[child_iter].parent = Some(p);
- child_iter = threads[child_iter].next_sibling.unwrap();
- }
- threads[child_iter].next_sibling = Some(curr_ref);
- threads[child_iter].parent = Some(p);
- }
- }
- p
- } else {
- let idx = threads.len();
- threads.push(
- Thread {
- message: None,
- id: idx,
- parent: None,
- first_child: Some(curr_ref),
- next_sibling: None,
- date: x.get_date(),
- indentation: 0,
- show_subject: true,
- });
- if threads[curr_ref].parent.is_none() {
- threads[curr_ref].parent = Some(idx);
- }
- id_table.insert(r.get_raw().to_string(), idx);
- idx
- };
- /* update thread date */
- let mut parent_iter = parent_id;
- 'date: loop {
- let p = &mut threads[parent_iter];
- if p.date < x.get_date() {
- p.date = x.get_date();
- }
- match p.parent {
- Some(p) => { parent_iter = p; },
- None => { break 'date; },
- }
- }
- curr_ref = parent_id;
- }
- }
-}
impl Mailbox
{
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>) -> Result<Mailbox> {
- let mut collection: Vec<Mail> = maildir::MaildirType::new(path).get()?;
- /* To reconstruct thread information from the mails we need: */
-
- /* a vector to hold thread members */
- let mut threads: Vec<Thread> = Vec::with_capacity((collection.len() as f64 * 1.2) as usize);
- /* A hash table of Message IDs */
- let mut id_table: FnvHashMap<std::string::String, usize> = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
-
+ let mut collection: Vec<Envelope> = maildir::MaildirType::new(path).get()?;
collection.sort_by(|a, b| a.get_date().cmp(&b.get_date()));
- /* Add each message to id_table and threads, and link them together according to the
- * References / In-Reply-To headers */
- build_collection(&mut threads, &mut id_table, &mut collection);
- let mut idx = collection.len();
- let mut tidx = threads.len();
- /* Link messages from Sent folder if they are relevant to this folder.
- * This means that
- * - if a message from Sent is a reply to a message in this folder, we will
- * add it to the threading (but not the collection; non-threading users shouldn't care
- * about this)
- * - if a message in this folder is a reply to a message we sent, we will add it to the
- * threading
- */
-
- if let &Some(ref sent_box) = sent_folder {
- if sent_box.is_ok() {
- let sent_mailbox = sent_box.as_ref();
- let sent_mailbox = sent_mailbox.unwrap();;
-
- for ref x in &sent_mailbox.collection {
- if id_table.contains_key(x.get_message_id_raw()) ||
- (!x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw())) {
- let mut x: Mail = (*x).clone();
- if id_table.contains_key(x.get_message_id_raw()) {
- let c = id_table[x.get_message_id_raw()];
- if threads[c].message.is_some() {
- /* skip duplicate message-id, but this should be handled instead */
- continue;
- }
- threads[c].message = Some(idx);
- assert!(threads[c].has_children());
- threads[c].date = x.get_date();
- x.set_thread(c);
- } else if !x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw()) {
- let p = id_table[x.get_in_reply_to_raw()];
- let c = if id_table.contains_key(x.get_message_id_raw()) {
- id_table[x.get_message_id_raw()]
- } else {
- threads.push(
- Thread {
- message: Some(idx),
- id: tidx,
- parent: Some(p),
- first_child: None,
- next_sibling: None,
- date: x.get_date(),
- indentation: 0,
- show_subject: true,
- });
- id_table.insert(x.get_message_id_raw().to_string(), tidx);
- x.set_thread(tidx);
- tidx += 1;
- tidx - 1
- };
- threads[c].parent = Some(p);
- if threads[p].is_descendant(&threads, &threads[c]) ||
- threads[c].is_descendant(&threads, &threads[p]) {
- continue;
- }
- if threads[p].first_child.is_none() {
- threads[p].first_child = Some(c);
- } else {
- let mut fc = threads[p].first_child.unwrap();
- while threads[fc].next_sibling.is_some() {
- threads[fc].parent = Some(p);
- fc = threads[fc].next_sibling.unwrap();
- }
- threads[fc].next_sibling = Some(c);
- threads[fc].parent = Some(p);
- }
- /* update thread date */
- let mut parent_iter = p;
- 'date: loop {
- let p = &mut threads[parent_iter];
- if p.date < x.get_date() {
- p.date = x.get_date();
- }
- match p.parent {
- Some(p) => { parent_iter = p; },
- None => { break 'date; },
- }
- }
- }
- collection.push(x);
- idx += 1;
- }
- }
- }
- }
- /* Walk over the elements of id_table, and gather a list of the Thread objects that have
- * no parents. These are the root messages of each thread */
- let mut root_set = Vec::with_capacity(collection.len());
- 'root_set: for v in id_table.values() {
- if threads[*v].parent.is_none() {
- if !threads[*v].has_message() && threads[*v].has_children() && !threads[threads[*v].first_child.unwrap()].has_sibling() {
- /* 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. */
- root_set.push(threads[*v].first_child.unwrap());
- continue 'root_set;
- }
- root_set.push(*v);
- }
- }
- root_set.sort_by(|a, b| threads[*b].date.cmp(&threads[*a].date));
-
- /* Group messages together by thread in a collection so we can print them together */
- let mut threaded_collection: Vec<usize> = Vec::with_capacity(collection.len());
- fn build_threaded(threads: &mut Vec<Thread>, indentation: usize, threaded: &mut Vec<usize>, i: usize, root_subject_idx: usize, collection: &[Mail])
- {
- let thread = threads[i];
- if threads[root_subject_idx].has_message() {
- let root_subject = collection[threads[root_subject_idx].get_message().unwrap()].get_subject();
- /* If the Container has no Message, but does have children, remove this container but
- * promote its children to this level (that is, splice them in to the current child
- * list.) */
- if indentation > 0 && thread.has_message() {
- let subject = collection[thread.get_message().unwrap()].get_subject();
- if subject == root_subject || subject.starts_with("Re: ") && subject.ends_with(root_subject) {
- threads[i].set_show_subject(false);
- }
- }
- }
- if thread.has_parent() && !threads[thread.get_parent().unwrap()].has_message() {
- threads[i].parent = None;
- }
- let indentation =
- if thread.has_message() {
- threads[i].set_indentation(indentation);
- if !threaded.contains(&i) {
- threaded.push(i);
- }
- indentation + 1
- } else if indentation > 0 {
- indentation
- } else {
- indentation + 1
- };
- if thread.has_children() {
- let mut fc = thread.get_first_child().unwrap();
- loop {
- build_threaded(threads, indentation, threaded, fc, i, collection);
- let thread_ = threads[fc];
- if !thread_.has_sibling() {
- break;
- }
- fc = thread_.get_next_sibling().unwrap();
- }
- }
- }
- for i in &root_set {
- build_threaded(&mut threads, 0, &mut threaded_collection, *i, *i, &collection);
- }
+ let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
let length = collection.len();
@@ -426,12 +69,12 @@ impl Mailbox
let thread = self.threads[self.threaded_collection[i]];
thread.get_message().unwrap()
}
- pub fn get_mail_and_thread(&mut self, i: usize) -> (&mut Mail, Thread) {
+ pub fn get_mail_and_thread(&mut self, i: usize) -> (&mut Envelope, Container) {
let x = &mut self.collection.as_mut_slice()[i];
let thread = self.threads[x.get_thread()];
(x, thread)
}
- pub fn get_thread(&self, i: usize) -> &Thread {
+ pub fn get_thread(&self, i: usize) -> &Container {
&self.threads[i]
}
}
diff --git a/src/mailbox/thread.rs b/src/mailbox/thread.rs
new file mode 100644
index 00000000..1e931e5d
--- /dev/null
+++ b/src/mailbox/thread.rs
@@ -0,0 +1,395 @@
+/*
+ * meli - mailbox threading module.
+ *
+ * Copyright 2017 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* a Container struct is needed to describe the Thread tree forest during creation
+ * of threads. Because of Rust's memory model, we store indexes of other node
+ * instead of references and every reference is passed through the Container owner
+ * (a Vec<Container>).
+ *
+ * message refers to a Envelope entry in a Vec. If it's empty, the Container is
+ * nonexistent in our Mailbox but we know it exists (for example we have a copy
+ * of a reply to a mail but we don't have its copy.
+ */
+use mailbox::email::*;
+use mailbox::Mailbox;
+use error::Result;
+
+extern crate fnv;
+use self::fnv::FnvHashMap;
+use std;
+
+
+
+type UnixTimestamp = i64;
+
+#[derive(Clone, Copy, Debug)]
+pub struct Container {
+ id: usize,
+ message: Option<usize>,
+ parent: Option<usize>,
+ first_child: Option<usize>,
+ next_sibling: Option<usize>,
+ date: UnixTimestamp,
+ indentation: usize,
+ show_subject: bool,
+}
+
+impl Container {
+ pub fn get_message(&self) -> Option<usize> {
+ self.message
+ }
+ pub fn get_parent(&self) -> Option<usize> {
+ self.parent
+ }
+ pub fn has_parent(&self) -> bool {
+ self.parent.is_some()
+ }
+ pub fn get_first_child(&self) -> Option<usize> {
+ self.first_child
+ }
+ pub fn get_next_sibling(&self) -> Option<usize> {
+ self.next_sibling
+ }
+ pub fn has_children(&self) -> bool {
+ self.first_child.is_some()
+ }
+ pub fn has_sibling(&self) -> bool {
+ self.next_sibling.is_some()
+ }
+ pub fn has_message(&self) -> bool {
+ self.message.is_some()
+ }
+ fn set_indentation(&mut self, i: usize) {
+ self.indentation = i;
+ }
+ pub fn get_indentation(&self) -> usize {
+ self.indentation
+ }
+ fn is_descendant(&self, threads: &[Container], other: &Container) -> bool {
+ if self == other {
+ return true;
+ }
+
+ if let Some(v) = self.first_child {
+ if threads[v].is_descendant(threads, other) {
+ return true;
+ }
+ };
+ if let Some(v) = self.next_sibling {
+ if threads[v].is_descendant(threads, other) {
+ return true;
+ }
+ };
+ false
+ }
+ fn set_show_subject(&mut self, set: bool) -> () {
+ self.show_subject = set;
+ }
+ pub fn get_show_subject(&self) -> bool {
+ self.show_subject
+ }
+}
+
+impl PartialEq for Container {
+ fn eq(&self, other: &Container) -> bool {
+ match (self.message, other.message) {
+ (Some(s), Some(o)) => {
+ s == o
+ },
+ _ => {
+ self.id == other.id
+ }
+ }
+ }
+}
+
+fn build_collection(threads: &mut Vec<Container>, id_table: &mut FnvHashMap<std::string::String, usize>, collection: &mut [Envelope]) -> ()
+{
+ for (i, x) in collection.iter_mut().enumerate() {
+ let x_index; /* x's index in threads */
+ let m_id = x.get_message_id_raw().to_string();
+ if id_table.contains_key(&m_id) {
+ let t = id_table[&m_id];
+ /* the already existing Container should be empty, since we're
+ * seeing this message for the first time */
+ if threads[t].message.is_some() {
+ /* skip duplicate message-id, but this should be handled instead */
+ continue;
+ }
+ x_index = t;
+ /* Store this message in the Container's message slot. */
+ threads[t].date = x.get_date();
+ x.set_thread(t);
+ threads[t].message = Some(i);
+ } else {
+ /* Create a new Container object holding this message */
+ x_index = threads.len();
+ threads.push(
+ Container {
+ message: Some(i),
+ id: x_index,
+ parent: None,
+ first_child: None,
+ next_sibling: None,
+ date: x.get_date(),
+ indentation: 0,
+ show_subject: true,
+ });
+ x.set_thread(x_index);
+ id_table.insert(m_id, x_index);
+ }
+ /* For each element in the message's References field:
+ *
+ * Find a Container object for the given Message-ID:
+ * If there's one in id_table use that;
+ * Otherwise, make (and index) one with a null Message
+ *
+ * Link