parent
fb745be27f
commit
15affc60a2
|
@ -23,6 +23,7 @@ base64 = "*"
|
|||
crossbeam = "^0.3.0"
|
||||
fnv = "1.0.3"
|
||||
encoding = "0.2.33"
|
||||
bitflags = "1.0"
|
||||
|
||||
[dependencies.ncurses]
|
||||
features = ["wide"]
|
||||
|
|
|
@ -32,3 +32,6 @@ extern crate chrono;
|
|||
extern crate base64;
|
||||
extern crate memmap;
|
||||
extern crate encoding;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue