add flag support

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
embed
Manos Pitsidianakis 2017-09-16 19:15:51 +03:00
parent fb745be27f
commit 15affc60a2
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
8 changed files with 97 additions and 29 deletions

View File

@ -23,6 +23,7 @@ base64 = "*"
crossbeam = "^0.3.0"
fnv = "1.0.3"
encoding = "0.2.33"
bitflags = "1.0"
[dependencies.ncurses]
features = ["wide"]

View File

@ -32,3 +32,6 @@ extern crate chrono;
extern crate base64;
extern crate memmap;
extern crate encoding;
#[macro_use]
extern crate bitflags;

View File

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
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
}
}

View File

@ -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

View File

@ -106,6 +106,19 @@ struct References {
refs: Vec<MessageID>,
}
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<Box<BackendOpGenerator>>,
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<BackendOpGenerator>) -> 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<BackendOpGenerator>) -> Option<Envelope> {
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)
}
}

View File

@ -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<Mailbox>>) -> Result<Mailbox> {
let mut collection: Vec<Envelope> = 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);

View File

@ -19,15 +19,6 @@
* 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;
@ -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<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.
#[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<Envelope>,
sent_folder: &Option<Result<Mailbox>>,

View File

@ -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. */