From 4119a4285d11f9a8552d554756a0b85c2fac6c35 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 14 Sep 2017 18:08:14 +0300 Subject: [PATCH] add accounts and BackendOps Signed-off-by: Manos Pitsidianakis --- benches/parse.rs | 12 ++ src/bin.rs | 12 +- src/conf/mod.rs | 14 +-- src/error.rs | 8 ++ src/mailbox/accounts.rs | 105 +++++++++++++++++ src/mailbox/backends/maildir.rs | 56 ++++++++- src/mailbox/backends/mod.rs | 33 ++++++ src/mailbox/email/mod.rs | 202 ++++++++++++++++---------------- src/mailbox/email/parser.rs | 32 ++++- src/mailbox/mod.rs | 162 +++++++++++++------------ src/ui/index.rs | 3 +- 11 files changed, 442 insertions(+), 197 deletions(-) create mode 100644 benches/parse.rs create mode 100644 src/mailbox/accounts.rs diff --git a/benches/parse.rs b/benches/parse.rs new file mode 100644 index 000000000..35259a7ac --- /dev/null +++ b/benches/parse.rs @@ -0,0 +1,12 @@ +#![feature(test)] +extern crate melib; + +use melib::mailbox::email::Mail; + +extern crate test; +use self::test::Bencher; + +#[bench] +fn mail_parse(b: &mut Bencher) { + b.iter(|| Mail::from("test/attachment_test") ); +} diff --git a/src/bin.rs b/src/bin.rs index 34e16e02a..c8d527b7d 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -36,17 +36,17 @@ fn main() { let ui = ui::TUI::initialize(); let mut j = 0; let folder_length = set.accounts["norn"].folders.len(); + let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone()); 'main : loop { ncurses::touchwin(ncurses::stdscr()); ncurses::mv(0,0); - let mailbox = Mailbox::new(&set.accounts["norn"].folders[j], - Some(&set.accounts["norn"].sent_folder)); - let mut index: Box = match mailbox { - Ok(v) => { + let mailbox = &mut account[j]; + let mut index: Box = match mailbox.as_ref().unwrap() { + &Ok(ref v) => { Box::new(Index::new(v)) }, - Err(v) => { - Box::new(ErrorWindow::new(v)) + &Err(ref v) => { + Box::new(ErrorWindow::new((*v).clone())) } }; //eprintln!("{:?}", set); diff --git a/src/conf/mod.rs b/src/conf/mod.rs index 93bfc3723..826a6c290 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -26,8 +26,8 @@ use std::io; use std::fs; use std::path::{PathBuf, Path}; -#[derive(Debug)] -enum MailFormat { +#[derive(Debug,Clone)] +pub enum MailFormat { Maildir } @@ -57,8 +57,8 @@ struct FileSettings { accounts: HashMap, } -#[derive(Debug)] -pub struct Account { +#[derive(Debug,Clone)] +pub struct AccountSettings { pub folders: Vec, format: MailFormat, pub sent_folder: String, @@ -66,7 +66,7 @@ pub struct Account { } #[derive(Debug)] pub struct Settings { - pub accounts: HashMap, + pub accounts: HashMap, } @@ -94,7 +94,7 @@ impl FileSettings { impl Settings { pub fn new() -> Settings { let fs = FileSettings::new(); - let mut s: HashMap = HashMap::new(); + let mut s: HashMap = HashMap::new(); for (id, x) in fs.accounts { let mut folders = Vec::new(); @@ -121,7 +121,7 @@ impl Settings { folders.push(path.to_str().unwrap().to_string()); } recurse_folders(&mut folders, &x.folders); - s.insert(id.clone(), Account { + s.insert(id.clone(), AccountSettings { folders: folders, format: MailFormat::from_str(&x.format), sent_folder: x.sent_folder.clone(), diff --git a/src/error.rs b/src/error.rs index d6bcf94fe..d8d028bbd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,6 +23,8 @@ use std::fmt; use std::result; use std::io; +use nom; + pub type Result = result::Result; #[derive(Debug,Clone)] @@ -55,3 +57,9 @@ impl From for MeliError { } } +impl From for MeliError { + #[inline] + fn from(kind: nom::IError) -> MeliError { + MeliError::new(format!("{:?}", kind)) + } +} diff --git a/src/mailbox/accounts.rs b/src/mailbox/accounts.rs new file mode 100644 index 000000000..2d047109e --- /dev/null +++ b/src/mailbox/accounts.rs @@ -0,0 +1,105 @@ +/* + * meli - accounts 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 . + */ + +use mailbox::*; +use conf::AccountSettings; +use std::ops::{IndexMut, Index}; +#[derive(Debug)] +pub struct Account { + name: String, + folders: Vec>>, + + sent_folder: Option, + + + settings: AccountSettings, +} + + +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 mut folders = Vec::with_capacity(settings.folders.len()); + for _ in 0..settings.folders.len() { + folders.push(None); + } + Account { + name: name, + folders: folders, + + sent_folder: sent_folder, + + settings: settings, + } + } +} + +impl Index for Account { + type Output = Option>; + fn index(&self, index: usize) -> &Option> { + &self.folders[index] + } +} + +impl IndexMut for Account +{ + fn index_mut(&mut self, index: usize) -> &mut Option> { + if self.folders[index].is_none() { + eprintln!("building folder {:?}", self.settings.folders[index]); + let path = self.settings.folders[index].clone(); + if self.sent_folder.is_some() { + let id = self.sent_folder.unwrap(); + if id == index { + eprintln!("building sent_folder.."); + self.folders[index] = Some(Mailbox::new(&path, &None)); + eprintln!("Done!"); + } else { + eprintln!("Now building folder {:?} with sent_folder", self.settings.folders[index]); + let (sent, cur) = + { + let ptr = self.folders.as_mut_ptr(); + unsafe { + use std::slice::from_raw_parts_mut; + (from_raw_parts_mut(ptr.offset(id as isize), id+1), + from_raw_parts_mut(ptr.offset(index as isize), index+1)) + } + }; + let sent_path = self.settings.folders[id].clone(); + if sent[0].is_none() { + eprintln!("\tbuilding sent_folder.."); + sent[0] = Some(Mailbox::new(&sent_path, &None)); + eprintln!("\tDone!"); + } + cur[0] = Some(Mailbox::new(&path, &sent[0])); + eprintln!("Done!"); + } + } else { + eprintln!("Now building folder {:?} without sent_folder", self.settings.folders[index]); + self.folders[index] = Some(Mailbox::new(&path, &None)); + eprintln!("Done!"); + }; + } + &mut self.folders[index] + } + + +} diff --git a/src/mailbox/backends/maildir.rs b/src/mailbox/backends/maildir.rs index f9810d6eb..e9808b526 100644 --- a/src/mailbox/backends/maildir.rs +++ b/src/mailbox/backends/maildir.rs @@ -21,16 +21,65 @@ use mailbox::email::Mail; use error::{MeliError, Result}; -use mailbox::backends::MailBackend; +use mailbox::backends::{MailBackend, BackendOp, BackendOpGenerator}; extern crate crossbeam; use std::path::PathBuf; +use memmap::{Mmap, Protection}; pub struct MaildirType { path: String, } +#[derive(Debug,Default)] +pub struct MaildirOp { + path: String, + slice: Option, +} + +impl Clone for MaildirOp { + fn clone(&self) -> Self { + MaildirOp { + path: self.path.clone(), + slice: None, + } + } +} + +impl MaildirOp { + fn new(path: String) -> Self { + MaildirOp { + path: path, + slice: None, + } + } +} + +impl BackendOp for MaildirOp { + fn description(&self) -> String { + format!("Path of file: {}", self.path) + } + fn as_bytes(&mut self) -> Result<&[u8]> { + if self.slice.is_none() { + self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read).unwrap()); + } + Ok(unsafe { self.slice.as_ref().unwrap().as_slice() }) + } + /* + fn fetch_headers(&mut self) -> Result<&[u8]> { + let raw = self.as_bytes()?; + let result = parser::headers_raw(raw).to_full_result()?; + Ok(result) + } + fn fetch_body(&mut self) -> Result<&[u8]> { + let raw = self.as_bytes()?; + let result = parser::headers_raw(raw).to_full_result()?; + Ok(result) + }*/ +} + + impl MailBackend for MaildirType { fn get(&self) -> Result> { self.get_multicore(4) @@ -120,7 +169,10 @@ panic!("didn't parse"); }, let s = scope.spawn(move || { let mut local_r:Vec = Vec::with_capacity(chunk.len()); for e in chunk { - if let Some(e) = Mail::from(e) { + let e_copy = e.to_string(); + if let Some(e) = Mail::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 82e17a9ab..d77aa2a63 100644 --- a/src/mailbox/backends/mod.rs +++ b/src/mailbox/backends/mod.rs @@ -23,6 +23,39 @@ pub mod maildir; use mailbox::email::Mail; use error::Result; +use std::fmt; + pub trait MailBackend { fn get(&self) -> Result>; } + + +/* A BackendOp manages common operations for the various mail backends. They should be created when + * needed */ +pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send { + fn description(&self) -> String; + fn as_bytes(&mut self) -> Result<&[u8]>; + //fn delete(&self) -> (); + //fn copy(&self + //fn fetch_headers(&mut self) -> Result<&[u8]>; + //fn fetch_body(&mut self) -> Result<&[u8]>; +} + +/* BackendOpGenerator is a wrapper for a closure that returns a BackendOp object */ +pub struct BackendOpGenerator(Box Box>); +impl BackendOpGenerator { + pub fn new(b: Box Box>) -> Self { + BackendOpGenerator(b) + } + pub fn generate(&self) -> Box { + self.0() + } +} +unsafe impl Send for BackendOpGenerator {} +unsafe impl Sync for BackendOpGenerator {} +impl fmt::Debug for BackendOpGenerator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "BackendOpGenerator") + } +} + diff --git a/src/mailbox/email/mod.rs b/src/mailbox/email/mod.rs index 64fed939b..7ca9dd15e 100644 --- a/src/mailbox/email/mod.rs +++ b/src/mailbox/email/mod.rs @@ -19,17 +19,18 @@ * along with meli. If not, see . */ -mod parser; +pub mod parser; mod attachments; +use mailbox::backends::BackendOpGenerator; use self::attachments::*; use std::string::String; -use memmap::{Mmap, Protection}; -use std; +//use std; use std::cmp::Ordering; use std::fmt; use std::option::Option; -use std::io::prelude::*; +//use std::io::prelude::*; +use std::ascii::AsciiExt; use chrono; use chrono::TimeZone; @@ -92,9 +93,11 @@ struct References { refs: Vec, } +use std::sync::Arc; /* A very primitive mail object */ -#[derive(Clone,Debug,Default)] -pub struct Mail { +#[derive(Debug, Clone)] +pub struct Mail +{ date: String, from: Option, to: Option, @@ -106,9 +109,13 @@ pub struct Mail { datetime: Option>, thread: usize, + + operation_token: Arc>, } -impl Mail { + +impl Mail +{ pub fn get_date(&self) -> i64 { match self.datetime { Some(v) => v.timestamp(), @@ -247,7 +254,7 @@ impl Mail { pub fn set_datetime(&mut self, new_val: Option>) -> () { self.datetime = new_val; } - pub fn new() -> Self { + pub fn new(token: Box) -> Self { Mail { date: "".to_string(), from: None, @@ -261,106 +268,99 @@ impl Mail { datetime: None, thread: 0, - } + + operation_token: Arc::new(token), + + } } - pub fn from(path: &str) -> Option { - let f = Mmap::open_path(path.to_string(), Protection::Read).unwrap(); - let file = unsafe { f.as_slice() }; - let (headers, body) = match parser::mail(file).to_full_result() { - Ok(v) => v, - Err(_) => { - eprintln!("error in parsing mail"); - let path = std::path::PathBuf::from(path); + pub fn from(operation_token: Box) -> Option { + let mut operation = operation_token.generate(); + let file = operation.as_bytes(); + if file.is_err() { + return None; + } + let (headers, body) = match parser::mail(file.unwrap()).to_full_result() { + Ok(v) => v, + Err(_) => { + let operation = operation_token.generate(); + eprintln!("error in parsing mail\n{}", operation.description()); + return None; + } + }; + let mut mail = Mail::new(operation_token); + let mut in_reply_to = None; + let mut datetime = None; - let mut buffer = Vec::new(); - let _ = std::fs::File::open(path).unwrap().read_to_end(&mut buffer); - eprintln!("\n-------------------------------"); - eprintln!("{}\n", std::string::String::from_utf8_lossy(&buffer)); - eprintln!("-------------------------------\n"); + let mut builder = AttachmentBuilder::new(body); + for (name, value) in headers { + if value.len() == 1 && value.is_empty() { + continue; + } + if name.eq_ignore_ascii_case("to") { + let parse_result = parser::subject(value.as_bytes()); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + "".to_string() + }; + mail.set_to(value); + } else if name.eq_ignore_ascii_case("from") { + let parse_result = parser::subject(value.as_bytes()); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + "".to_string() + }; + mail.set_from(value); + } else if name.eq_ignore_ascii_case("subject") { + let parse_result = parser::subject(value.trim().as_bytes()); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + "".to_string() + }; + mail.set_subject(value); + } else if name.eq_ignore_ascii_case("message-id") { + mail.set_message_id(value); + } else if name.eq_ignore_ascii_case("references") { + { + let parse_result = parser::references(value.as_bytes()); + eprintln!("{:?}", value); + if parse_result.is_done() { + for v in parse_result.to_full_result().unwrap() { + eprintln!("pushing {:?}", v); + mail.push_references(v); + } + } + eprintln!("\n\n"); + } + mail.set_references(value.to_string()); + } else if name.eq_ignore_ascii_case("in-reply-to") { + mail.set_in_reply_to(value); + in_reply_to = Some(value); + } else if name.eq_ignore_ascii_case("date") { + mail.set_date(value.to_string()); + datetime = Some(value.to_string()); + } else if name.eq_ignore_ascii_case("content-transfer-encoding") { + builder.content_transfer_encoding(value); + } else if name.eq_ignore_ascii_case("content-type") { + builder.content_type(value); + } + } + if let Some(ref mut x) = in_reply_to { + mail.push_references(x); + } + mail.set_body(builder.build()); + if let Some(ref mut d) = datetime { + mail.set_datetime(parser::date(d)); + } - return None; } - }; - let mut mail = Mail::new(); - let mut in_reply_to = None; - let mut datetime = None; - - let mut builder = AttachmentBuilder::new(body); - for (name, value) in headers { - if value.len() == 1 && value.is_empty() { - continue; - } - match name { - "To" => { - let parse_result = parser::subject(value.as_bytes()); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - "".to_string() - }; - mail.set_to(value); - }, - "From" => { - let parse_result = parser::subject(value.as_bytes()); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - "".to_string() - }; - mail.set_from(value); - }, - "Subject" => { - let parse_result = parser::subject(value.trim().as_bytes()); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - "".to_string() - }; - mail.set_subject(value); - }, - "Message-ID" | "Message-Id" | "Message-id" | "message-id" => { - mail.set_message_id(value); - }, - "References" => { - { - let parse_result = parser::references(value.as_bytes()); - if parse_result.is_done() { - for v in parse_result.to_full_result().unwrap() { - mail.push_references(v); - } - } - } - mail.set_references(value.to_string()); - }, - "In-Reply-To" | "In-reply-to" | "In-Reply-to" | "in-reply-to" => { - mail.set_in_reply_to(value); - in_reply_to = Some(value); }, - "Date" => { - mail.set_date(value.to_string()); - datetime = Some(value.to_string()); - }, - "Content-Transfer-Encoding" => { - builder.content_transfer_encoding(value); - }, - "Content-Type" => { - builder.content_type(value); - }, - _ => {}, - } - }; - if let Some(ref mut x) = in_reply_to { - mail.push_references(x); - }; - mail.set_body(builder.build()); - if let Some(ref mut d) = datetime { - mail.set_datetime(parser::date(d)); - } - - Some(mail) + Some(mail) } } impl Eq for Mail {} -impl Ord for Mail { +impl Ord for Mail { fn cmp(&self, other: &Mail) -> Ordering { self.get_datetime().cmp(&other.get_datetime()) } diff --git a/src/mailbox/email/parser.rs b/src/mailbox/email/parser.rs index aaee4625c..7ec28e384 100644 --- a/src/mailbox/email/parser.rs +++ b/src/mailbox/email/parser.rs @@ -110,12 +110,22 @@ named!(header<(&str, &str)>, named!(headers>, many1!(complete!(header))); +named!(pub headers_raw<&[u8]>, + take_until1!("\n\n")); + +named!(pub body_raw<&[u8]>, + do_parse!( + take_until1!("\n\n") >> + body: take_while!(call!(|_| true)) >> + ( { body } ))); + + named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>, - separated_pair!(headers, tag!("\n"), take_while!(call!(|_| { true })))); + separated_pair!(headers, tag!("\n"), take_while!(call!(|_| true)))); named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>, do_parse!( opt!(is_a!(" \n\t\r")) >> - pair: pair!(many0!(complete!(header)), take_while!(call!(|_| { true }))) >> + pair: pair!(many0!(complete!(header)), take_while!(call!(|_| true))) >> ( { pair } ))); /* Header parsers */ @@ -250,7 +260,23 @@ named!(pub message_id<&str>, map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8) ); -named!(pub references>, many0!(preceded!(is_not!("<"), message_id))); +fn message_id_peek(input: &[u8]) -> IResult<&[u8],&str> { + let input_length = input.len(); + if input.is_empty() { + IResult::Incomplete(Needed::Size(1)) + } 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'>' { + return IResult::Done(&input[i+1..], from_utf8(&input[0..i+1]).unwrap()); + } + } + IResult::Incomplete(Needed::Unknown) + } +} + +named!(pub references>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek)); named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >, alt_complete!(do_parse!( diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index ae434522a..4cbfb67c2 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -26,6 +26,8 @@ pub mod backends; use mailbox::backends::MailBackend; use mailbox::backends::maildir; use error::Result; +pub mod accounts; +pub use mailbox::accounts::Account; extern crate fnv; @@ -36,7 +38,7 @@ use std; type UnixTimestamp = i64; /*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/ -#[derive(Debug)] +#[derive(Debug,Clone)] pub struct Mailbox{ pub path: String, pub collection: Vec, @@ -135,7 +137,8 @@ impl PartialEq for Thread { } } -fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, collection: &mut [Mail]) -> () { +fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, 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(); @@ -173,7 +176,7 @@ fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, id_table: &mut FnvHashMap { break 'date; }, } } - if threads[curr_ref].parent.is_none() { - threads[curr_ref].parent = Some(parent_id); - } curr_ref = parent_id; } } } -impl Mailbox { - pub fn new(path: &str, sent_folder: Option<&str>) -> Result { +impl Mailbox +{ + pub fn new(path: &str, sent_folder: &Option>) -> Result { let mut collection: Vec = maildir::MaildirType::new(path).get()?; /* To reconstruct thread information from the mails we need: */ @@ -260,78 +261,84 @@ impl Mailbox { * - if a message in this folder is a reply to a message we sent, we will add it to the * threading */ - if sent_folder.is_some() { - for mut x in maildir::MaildirType::new(sent_folder.unwrap()).get().unwrap() { - 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())) { - collection.push(x.clone()); - idx += 1; - } - 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-1); - assert!(threads[c].has_children()); - threads[c].date = x.get_date(); - x.set_thread(c); - } - 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()) { - threads.push( - Thread { - message: Some(idx-1), - 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 - } else { - id_table[x.get_message_id_raw()] - }; - 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(); + + 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; } - 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; }, - } - } } } } - /* 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 */ + /* 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() { @@ -348,7 +355,8 @@ impl Mailbox { /* Group messages together by thread in a collection so we can print them together */ let mut threaded_collection: Vec = Vec::with_capacity(collection.len()); - fn build_threaded(threads: &mut Vec, indentation: usize, threaded: &mut Vec, i: usize, root_subject_idx: usize, collection: &[Mail]) { + fn build_threaded(threads: &mut Vec, indentation: usize, threaded: &mut Vec, 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(); diff --git a/src/ui/index.rs b/src/ui/index.rs index 3288487c5..1377f46c9 100644 --- a/src/ui/index.rs +++ b/src/ui/index.rs @@ -313,7 +313,8 @@ impl Window for Index { } } impl Index { - pub fn new(mailbox: Mailbox) -> Index { + pub fn new(mailbox: &Mailbox) -> Index { + let mailbox = (*mailbox).clone(); let mut screen_height = 0; let mut screen_width = 0; /* Get the screen bounds. */