diff --git a/Cargo.toml b/Cargo.toml index c079f777..e70c542f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ base64 = "*" crossbeam = "^0.3.0" fnv = "1.0.3" encoding = "0.2.33" +bitflags = "1.0" [dependencies.ncurses] features = ["wide"] diff --git a/src/lib.rs b/src/lib.rs index 4674e990..1bd073b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,3 +32,6 @@ extern crate chrono; extern crate base64; extern crate memmap; extern crate encoding; + +#[macro_use] +extern crate bitflags; diff --git a/src/mailbox/backends/maildir.rs b/src/mailbox/backends/maildir.rs index aa417ee2..2bd7c29d 100644 --- a/src/mailbox/backends/maildir.rs +++ b/src/mailbox/backends/maildir.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use mailbox::email::Envelope; +use mailbox::email::{Envelope, Flag}; use error::{MeliError, Result}; use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend}; use mailbox::email::parser; @@ -79,6 +79,31 @@ impl BackendOp for MaildirOp { let result = parser::headers_raw(raw).to_full_result()?; Ok(result) } + fn fetch_flags(&self) -> Flag { + let mut flag = Flag::default(); + let path = PathBuf::from(&self.path); + let filename = path.file_name().unwrap().to_str().unwrap(); + if !filename.contains(":2,") { + return flag; + } + + for f in filename.chars().rev() { + match f { + ',' => break, + 'P' => flag |= Flag::PASSED, + 'R' => flag |= Flag::REPLIED, + 'S' => flag |= Flag::SEEN, + 'T' => flag |= Flag::TRASHED, + 'D' => flag |= Flag::DRAFT, + 'F' => flag |= Flag::FLAGGED, + _ => panic!(), + } + + } + + + flag + } } diff --git a/src/mailbox/backends/mod.rs b/src/mailbox/backends/mod.rs index 8bf987d5..7705af04 100644 --- a/src/mailbox/backends/mod.rs +++ b/src/mailbox/backends/mod.rs @@ -20,7 +20,7 @@ */ pub mod maildir; -use mailbox::email::Envelope; +use mailbox::email::{Envelope, Flag}; use error::Result; use std::fmt; @@ -59,6 +59,9 @@ pub trait MailBackend { /// fn fetch_body(&mut self) -> Result<&[u8]> { /// unimplemented!() /// } +/// fn fetch_flags((&self) -> Flag { +/// unimplemented!() +/// } /// } /// /// let foogen = BackendOpGenerator::new(Box::new(|| Box::new(FooOp {}))); @@ -73,6 +76,7 @@ pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send { //fn copy(&self fn fetch_headers(&mut self) -> Result<&[u8]>; fn fetch_body(&mut self) -> Result<&[u8]>; + fn fetch_flags(&self) -> Flag; } /// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object diff --git a/src/mailbox/email/mod.rs b/src/mailbox/email/mod.rs index 075fdbd5..9a8e6333 100644 --- a/src/mailbox/email/mod.rs +++ b/src/mailbox/email/mod.rs @@ -106,6 +106,19 @@ struct References { refs: Vec, } + +bitflags! { + #[derive(Default)] + pub struct Flag: u8 { + const PASSED = 0b00000001; + const REPLIED = 0b00000010; + const SEEN = 0b00000100; + const TRASHED = 0b00001000; + const DRAFT = 0b00010000; + const FLAGGED = 0b00100000; + } +} + /* A very primitive mail object */ #[derive(Debug, Clone)] pub struct Envelope { @@ -123,6 +136,8 @@ pub struct Envelope { thread: usize, operation_token: Arc>, + + flags: Flag, } @@ -314,6 +329,12 @@ impl Envelope { self.timestamp = v.timestamp(); } } + pub fn get_flags(&self) -> Flag { + self.flags + } + pub fn is_seen(&self) -> bool { + !(self.flags & Flag::SEEN).is_empty() + } pub fn new(token: Box) -> Self { Envelope { date: "".to_string(), @@ -331,20 +352,27 @@ impl Envelope { thread: 0, operation_token: Arc::new(token), + flags: Flag::default(), } } pub fn from(operation_token: Box) -> Option { - let mut operation = operation_token.generate(); + let operation = operation_token.generate(); + let mut e = Envelope::new(operation_token); + e.flags = operation.fetch_flags(); + Some(e) + } + + pub fn populate_headers(&mut self) -> () { + let mut operation = self.operation_token.generate(); let headers = match parser::headers(operation.fetch_headers().unwrap()).to_full_result() { Ok(v) => v, _ => { - let operation = operation_token.generate(); + let operation = self.operation_token.generate(); eprintln!("error in parsing mail\n{}", operation.description()); - return None; + return; } }; - let mut mail = Envelope::new(operation_token); let mut in_reply_to = None; let mut datetime = None; @@ -359,7 +387,7 @@ impl Envelope { } else { "".to_string() }; - mail.set_to(value); + self.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() { @@ -367,7 +395,7 @@ impl Envelope { } else { "".to_string() }; - mail.set_from(value); + self.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() { @@ -375,35 +403,33 @@ impl Envelope { } else { "".to_string() }; - mail.set_subject(value); + self.set_subject(value); } else if name.eq_ignore_ascii_case("message-id") { - mail.set_message_id(value); + self.set_message_id(value); } else if name.eq_ignore_ascii_case("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); + self.push_references(v); } } } - mail.set_references(value.to_string()); + self.set_references(value.to_string()); } else if name.eq_ignore_ascii_case("in-reply-to") { - mail.set_in_reply_to(value); + self.set_in_reply_to(value); in_reply_to = Some(value); } else if name.eq_ignore_ascii_case("date") { - mail.set_date(value.to_string()); + self.set_date(value.to_string()); datetime = Some(value.to_string()); } } if let Some(ref mut x) = in_reply_to { - mail.push_references(x); + self.push_references(x); } if let Some(ref mut d) = datetime { - mail.set_datetime(parser::date(d)); + self.set_datetime(parser::date(d)); } - - Some(mail) } } diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index 37f46055..c380fcb8 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -34,7 +34,7 @@ use mailbox::thread::{build_threads, Container}; use std::option::Option; -/*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/ +/// `Mailbox` represents a folder of mail. Currently only `Maildir` is supported. #[derive(Debug, Clone)] pub struct Mailbox { pub path: String, @@ -48,6 +48,9 @@ pub struct Mailbox { impl Mailbox { pub fn new(path: &str, sent_folder: &Option>) -> Result { let mut collection: Vec = maildir::MaildirType::new(path).get()?; + for e in &mut collection { + e.populate_headers(); + } collection.sort_by(|a, b| a.get_date().cmp(&b.get_date())); let (threads, threaded_collection) = build_threads(&mut collection, sent_folder); diff --git a/src/mailbox/thread.rs b/src/mailbox/thread.rs index bb8021a9..891cda72 100644 --- a/src/mailbox/thread.rs +++ b/src/mailbox/thread.rs @@ -19,15 +19,6 @@ * along with meli. If not, see . */ -/* 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). - * - * 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; @@ -40,6 +31,13 @@ use std; type UnixTimestamp = i64; +/// 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 Envelopes inside a collection instead of +/// references and every reference is passed through the `Container` owner (a `Vec`). +// +/// `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. #[derive(Clone, Copy, Debug)] pub struct Container { id: usize, @@ -230,6 +228,7 @@ fn build_collection( } +/// Builds threads from a collection. pub fn build_threads( collection: &mut Vec, sent_folder: &Option>, diff --git a/src/ui/index.rs b/src/ui/index.rs index 456b65e1..e1b24f0b 100644 --- a/src/ui/index.rs +++ b/src/ui/index.rs @@ -338,7 +338,14 @@ impl Window for Index { } impl Index { pub fn new(mailbox: &Mailbox) -> Index { - let mailbox = (*mailbox).clone(); + let mailbox = mailbox.clone(); + let mut unread_count = 0; + for e in &mailbox.collection { + if !e.is_seen() { + unread_count += 1; + } + } + eprintln!("unread count {}", unread_count); let mut screen_height = 0; let mut screen_width = 0; /* Get the screen bounds. */