diff --git a/src/lib.rs b/src/lib.rs index 1e61980e4..177c78c81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,4 +39,6 @@ extern crate bitflags; pub use mailbox::*; pub use conf::*; -pub use mailbox::backends::{RefreshEventConsumer, Backends}; +pub use mailbox::backends::{RefreshEventConsumer, RefreshEvent, Backends}; +pub use mailbox::email::{Envelope, Flag}; +pub use error::{Result, MeliError}; diff --git a/src/mailbox/backends/maildir.rs b/src/mailbox/backends/maildir.rs index f655bb476..9a64a2301 100644 --- a/src/mailbox/backends/maildir.rs +++ b/src/mailbox/backends/maildir.rs @@ -38,6 +38,7 @@ extern crate crossbeam; use std::path::PathBuf; use memmap::{Mmap, Protection}; +/// `BackendOp` implementor for Maildir #[derive(Debug, Default)] pub struct MaildirOp { path: String, @@ -113,6 +114,7 @@ impl BackendOp for MaildirOp { } +/// Maildir backend https://cr.yp.to/proto/maildir.html #[derive(Debug)] pub struct MaildirType { path: String, @@ -215,7 +217,7 @@ impl MaildirType { let m = match Email::parse(&buffer) { Ok((v, rest)) => match rest.len() { 0 => v, - _ => + _ => { eprintln!("{:?}", String::from_utf8(rest.to_vec()).unwrap()); panic!("didn't parse"); }, }, diff --git a/src/mailbox/backends/mod.rs b/src/mailbox/backends/mod.rs index f39d48f43..592fa45a0 100644 --- a/src/mailbox/backends/mod.rs +++ b/src/mailbox/backends/mod.rs @@ -95,7 +95,8 @@ pub trait MailBackend: ::std::fmt::Debug { /// # Example /// ``` /// use melib::mailbox::backends::{BackendOp, BackendOpGenerator}; -/// use melib::error::Result; +/// use melib::Result; +/// use melib::{Envelope, Flag}; /// /// #[derive(Debug)] /// struct FooOp {} @@ -113,7 +114,7 @@ pub trait MailBackend: ::std::fmt::Debug { /// fn fetch_body(&mut self) -> Result<&[u8]> { /// unimplemented!() /// } -/// fn fetch_flags((&self) -> Flag { +/// fn fetch_flags(&self) -> Flag { /// unimplemented!() /// } /// } diff --git a/src/mailbox/email/mod.rs b/src/mailbox/email/mod.rs index 00df63909..c862d0f69 100644 --- a/src/mailbox/email/mod.rs +++ b/src/mailbox/email/mod.rs @@ -36,19 +36,24 @@ use std::ascii::AsciiExt; use chrono; use chrono::TimeZone; -/* Helper struct to return slices from a struct on demand */ +/// Helper struct to return slices from a struct field on demand. #[derive(Clone, Debug)] struct StrBuilder { offset: usize, length: usize, } +/// Structs implementing this trait must contain a `StrBuilder` field. pub trait StrBuild { - fn new(&str, &str) -> Self; + /// Create a new `Self` out of a string and a slice + fn new(string: &str, slice: &str) -> Self; + /// Get the slice part of the string fn get_raw(&self) -> &str; + /// Get the entire string as a slice fn get_val(&self) -> &str; } +/// `MessageID` is accessed through the `StrBuild` trait. #[derive(Clone)] pub struct MessageID(String, StrBuilder); @@ -119,7 +124,13 @@ bitflags! { } } -/* A very primitive mail object */ +/// `Envelope` represents all the data of an email we need to know. +/// +/// Attachments (the email's body) is parsed on demand with `get_body`. +/// +/// Access to the underlying email object in the account's backend (for example the file or the +/// entry in an IMAP server) is given through `operation_token`. For more information see +/// `BackendOp`. #[derive(Debug, Clone)] pub struct Envelope { date: String, @@ -142,7 +153,110 @@ pub struct Envelope { impl Envelope { - pub fn get_date(&self) -> i64 { + pub fn new(token: Box) -> Self { + Envelope { + date: "".to_string(), + from: None, + to: None, + body: None, + subject: None, + message_id: None, + in_reply_to: None, + references: None, + + datetime: None, + timestamp: 0, + + thread: 0, + + operation_token: Arc::new(token), + flags: Flag::default(), + } + } + pub fn from(operation_token: Box) -> Option { + 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 = self.operation_token.generate(); + eprintln!("error in parsing mail\n{}", operation.description()); + return; + } + }; + + let mut in_reply_to = None; + let mut datetime = None; + + 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() + }; + 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() { + parse_result.to_full_result().unwrap() + } else { + "".to_string() + }; + 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() { + parse_result.to_full_result().unwrap() + } else { + "".to_string() + }; + self.set_subject(value); + } else if name.eq_ignore_ascii_case("message-id") { + 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() { + self.push_references(v); + } + } + } + self.set_references(value.to_string()); + } else if name.eq_ignore_ascii_case("in-reply-to") { + self.set_in_reply_to(value); + in_reply_to = Some(value); + } else if name.eq_ignore_ascii_case("date") { + self.set_date(value.to_string()); + datetime = Some(value.to_string()); + } + } + /* + * https://tools.ietf.org/html/rfc5322#section-3.6.4 + * + * if self.message_id.is_none() { ... + */ +if let Some(ref mut x) = in_reply_to { + self.push_references(x); +} +if let Some(ref mut d) = datetime { + if let Some(d) = parser::date(d) { + self.set_datetime(d); + } +} +} +pub fn get_date(&self) -> i64 { self.timestamp } pub fn get_datetime(&self) -> chrono::DateTime { @@ -323,11 +437,9 @@ impl Envelope { pub fn set_thread(&mut self, new_val: usize) -> () { self.thread = new_val; } - pub fn set_datetime(&mut self, new_val: Option>) -> () { - self.datetime = new_val; - if let Some(v) = self.datetime { - self.timestamp = v.timestamp(); - } + pub fn set_datetime(&mut self, new_val: chrono::DateTime) -> () { + self.datetime = Some(new_val); + self.timestamp = new_val.timestamp(); } pub fn get_flags(&self) -> Flag { self.flags @@ -335,107 +447,6 @@ impl Envelope { pub fn is_seen(&self) -> bool { !(self.flags & Flag::SEEN).is_empty() } - pub fn new(token: Box) -> Self { - Envelope { - date: "".to_string(), - from: None, - to: None, - body: None, - subject: None, - message_id: None, - in_reply_to: None, - references: None, - - datetime: None, - timestamp: 0, - - thread: 0, - - operation_token: Arc::new(token), - flags: Flag::default(), - } - } - pub fn from(operation_token: Box) -> Option { - 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 = self.operation_token.generate(); - eprintln!("error in parsing mail\n{}", operation.description()); - return; - } - }; - - let mut in_reply_to = None; - let mut datetime = None; - - 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() - }; - 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() { - parse_result.to_full_result().unwrap() - } else { - "".to_string() - }; - 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() { - parse_result.to_full_result().unwrap() - } else { - "".to_string() - }; - self.set_subject(value); - } else if name.eq_ignore_ascii_case("message-id") { - 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() { - self.push_references(v); - } - } - } - self.set_references(value.to_string()); - } else if name.eq_ignore_ascii_case("in-reply-to") { - self.set_in_reply_to(value); - in_reply_to = Some(value); - } else if name.eq_ignore_ascii_case("date") { - self.set_date(value.to_string()); - datetime = Some(value.to_string()); - } - } - /* - * https://tools.ietf.org/html/rfc5322#section-3.6.4 - * - * if self.message_id.is_none() { ... - */ - if let Some(ref mut x) = in_reply_to { - self.push_references(x); - } - if let Some(ref mut d) = datetime { - self.set_datetime(parser::date(d)); - } - } } impl Eq for Envelope {} diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index f91a2513e..a35cc821e 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -27,13 +27,13 @@ use mailbox::backends::MailBackend; use error::Result; pub mod accounts; pub use mailbox::accounts::Account; -mod thread; -use mailbox::thread::{build_threads, Container}; +pub mod thread; +pub use mailbox::thread::{build_threads, Container}; use std::option::Option; -/// `Mailbox` represents a folder of mail. Currently only `Maildir` is supported. +/// `Mailbox` represents a folder of mail. #[derive(Debug, Clone)] pub struct Mailbox { pub path: String, diff --git a/src/ui/index.rs b/src/ui/index.rs index 8b8ed12d1..3ef722695 100644 --- a/src/ui/index.rs +++ b/src/ui/index.rs @@ -18,9 +18,7 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ -use mailbox::email::Envelope; -use mailbox::*; -use error::MeliError; +use super::*; use super::pager::Pager; use std::error::Error; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 9da9dc470..2c0b38fbc 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -24,18 +24,27 @@ pub mod pager; extern crate ncurses; extern crate melib; -use melib::mailbox::backends::RefreshEvent; +use melib::*; /* Color pairs; foreground && background. */ +/// Default color. pub static COLOR_PAIR_DEFAULT: i16 = 1; +/// Highlighted cursor line in index view. pub static COLOR_PAIR_CURSOR: i16 = 2; +/// Header colour in pager view. pub static COLOR_PAIR_HEADERS: i16 = 3; +/// Indentation symbol color in index view. pub static COLOR_PAIR_THREAD_INDENT: i16 = 4; +/// Line color for odd entries in index view. pub static COLOR_PAIR_THREAD_ODD: i16 = 5; +/// Line color for even entries in index view. pub static COLOR_PAIR_THREAD_EVEN: i16 = 6; +/// Line color for unread odd entries in index view. pub static COLOR_PAIR_UNREAD_ODD: i16 = 7; +/// Line color for unread even entries in index view. pub static COLOR_PAIR_UNREAD_EVEN: i16 = 8; +/// Dummy object to provide `ncurses` initialization and destruction. pub struct TUI; impl TUI { @@ -71,8 +80,12 @@ impl Drop for TUI { } } +/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads +/// to the main process. pub enum ThreadEvent { + /// User input. Input(ncurses::WchResult), + /// A watched folder has been refreshed. RefreshMailbox{ name: String }, //Decode { _ }, // For gpg2 signature check } diff --git a/src/ui/pager.rs b/src/ui/pager.rs index 27871094e..8d5d7c122 100644 --- a/src/ui/pager.rs +++ b/src/ui/pager.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use mailbox; +use super::*; extern crate ncurses; @@ -35,7 +35,7 @@ pub struct Pager { } impl Pager { - pub fn new(parent: ncurses::WINDOW, entry: &mut mailbox::Envelope) -> Pager { + pub fn new(parent: ncurses::WINDOW, entry: &mut Envelope) -> Pager { let mut screen_height = 0; let mut screen_width = 0; ncurses::getmaxyx(parent, &mut screen_height, &mut screen_width); @@ -130,7 +130,7 @@ impl Pager { w - 1, ); } - fn print_entry_headers(win: ncurses::WINDOW, mail: &mut mailbox::Envelope) -> i32 { + fn print_entry_headers(win: ncurses::WINDOW, mail: &mut Envelope) -> i32 { let mut i = 0; ncurses::wattron(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_HEADERS)); ncurses::waddstr(win, "Date: "); @@ -171,7 +171,7 @@ impl Pager { } fn print_entry_content( win: ncurses::WINDOW, - mail: &mut mailbox::Envelope, + mail: &mut Envelope, height: i32, ) -> (ncurses::WINDOW, i32, i32) { let mut h = 0; @@ -208,7 +208,7 @@ impl Pager { } fn print_entry( win: ncurses::WINDOW, - mail: &mut mailbox::Envelope, + mail: &mut Envelope, ) -> (ncurses::WINDOW, i32, i32) { let header_height = Pager::print_entry_headers(win, mail); Pager::print_entry_content(win, mail, header_height + 2)