From 5889494e9e4d6c51aa87e6433f2dfdbfb8b4846a Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 12 Aug 2018 16:55:45 +0300 Subject: [PATCH] Move backend logic to backend and keep Envelope abstract --- melib/src/mailbox/accounts.rs | 2 +- melib/src/mailbox/backends/maildir.rs | 176 ++++++++++------ melib/src/mailbox/backends/mod.rs | 3 +- melib/src/mailbox/email/attachment_types.rs | 10 +- melib/src/mailbox/email/attachments.rs | 4 +- melib/src/mailbox/email/mod.rs | 210 ++++++++++---------- melib/src/mailbox/email/parser.rs | 71 +++---- src/bin.rs | 2 +- ui/src/components/mail/listing/compact.rs | 44 ++-- ui/src/components/mail/listing/mod.rs | 56 ++++-- ui/src/components/mail/view/mod.rs | 19 +- 11 files changed, 344 insertions(+), 253 deletions(-) diff --git a/melib/src/mailbox/accounts.rs b/melib/src/mailbox/accounts.rs index 7cc49497a..25068c160 100644 --- a/melib/src/mailbox/accounts.rs +++ b/melib/src/mailbox/accounts.rs @@ -48,7 +48,7 @@ pub struct Account { impl Account { pub fn new(name: String, settings: AccountSettings, map: &Backends) -> Self { - let backend = map.get(settings.format())(&settings); + let mut backend = map.get(settings.format())(&settings); let ref_folders: Vec = backend.folders(); let mut folders: Vec>> = Vec::with_capacity(ref_folders.len()); let mut workers: Vec = Vec::new(); diff --git a/melib/src/mailbox/backends/maildir.rs b/melib/src/mailbox/backends/maildir.rs index ce41f0df5..1cdd25892 100644 --- a/melib/src/mailbox/backends/maildir.rs +++ b/melib/src/mailbox/backends/maildir.rs @@ -23,7 +23,7 @@ use async::*; use conf::AccountSettings; use error::{MeliError, Result}; use mailbox::backends::{ - BackendFolder, BackendOp, BackendOpGenerator, Folder, MailBackend, RefreshEvent, + BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer, }; use mailbox::email::parser; @@ -43,41 +43,52 @@ extern crate crossbeam; use memmap::{Mmap, Protection}; use std::collections::hash_map::DefaultHasher; use std::fs; +use std::sync::{Mutex, Arc}; use std::hash::Hasher; use std::path::{Path, PathBuf}; +extern crate fnv; +use self::fnv::FnvHashMap; /// `BackendOp` implementor for Maildir -#[derive(Debug, Default)] +#[derive(Debug)] pub struct MaildirOp { - path: String, + hash_index: Arc>>, + hash: u64, slice: Option, } impl Clone for MaildirOp { fn clone(&self) -> Self { MaildirOp { - path: self.path.clone(), + hash_index: self.hash_index.clone(), + hash: self.hash.clone(), slice: None, } } } impl MaildirOp { - pub fn new(path: String) -> Self { + pub fn new(hash: u64, hash_index: Arc>>) -> Self { MaildirOp { - path: path, + hash_index, + hash, slice: None, } } + fn path(&self) -> String { + let hash_index = self.hash_index.clone(); + let map = hash_index.lock().unwrap(); + map.get(&self.hash).unwrap().1.clone() + } } -impl BackendOp for MaildirOp { +impl<'a> BackendOp for MaildirOp { fn description(&self) -> String { - format!("Path of file: {}", self.path) + format!("Path of file:")// self.0ipath) } fn as_bytes(&mut self) -> Result<&[u8]> { if self.slice.is_none() { - self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read)?); + self.slice = Some(Mmap::open_path(self.path(), Protection::Read)?); } /* Unwrap is safe since we use ? above. */ Ok(unsafe { self.slice.as_ref().unwrap().as_slice() }) @@ -94,13 +105,12 @@ impl BackendOp for MaildirOp { } 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,") { + let path = self.path(); + if !path.contains(":2,") { return flag; } - for f in filename.chars().rev() { + for f in path.chars().rev() { match f { ',' => break, 'D' => flag |= Flag::DRAFT, @@ -115,13 +125,18 @@ impl BackendOp for MaildirOp { flag } + fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> { - let idx: usize = self.path.rfind(":2,").ok_or(MeliError::new(format!( - "Invalid email filename: {:?}", - self - )))? + 3; - let mut new_name: String = self.path[..idx].to_string(); + let path = self.path(); + let idx: usize = path.rfind(":2,").ok_or(MeliError::new(format!( + "Invalid email filename: {:?}", + self + )))? + 3; + let mut new_name: String = path[..idx].to_string(); let mut flags = self.fetch_flags(); + if !(flags & *f).is_empty() { + return Ok(()); + } flags.toggle(*f); if !(flags & Flag::DRAFT).is_empty() { new_name.push('D'); @@ -142,10 +157,11 @@ impl BackendOp for MaildirOp { new_name.push('T'); } - fs::rename(&self.path, &new_name)?; - envelope.set_operation_token(Box::new(BackendOpGenerator::new(Box::new(move || { - Box::new(MaildirOp::new(new_name.clone())) - })))); + fs::rename(&path, &new_name)?; + let hash = envelope.hash(); + let hash_index = self.hash_index.clone(); + let mut map = hash_index.lock().unwrap(); + map.get_mut(&hash).unwrap().1 = new_name; Ok(()) } } @@ -154,6 +170,8 @@ impl BackendOp for MaildirOp { #[derive(Debug)] pub struct MaildirType { folders: Vec, + hash_index: Arc>>, + path: String, } @@ -161,7 +179,7 @@ impl MailBackend for MaildirType { fn folders(&self) -> Vec { self.folders.iter().map(|f| f.clone()).collect() } - fn get(&self, folder: &Folder) -> Async>> { + fn get(&mut self, folder: &Folder) -> Async>> { self.multicore(4, folder) } fn watch(&self, sender: RefreshEventConsumer) -> Result<()> { @@ -188,25 +206,25 @@ impl MailBackend for MaildirType { match rx.recv() { Ok(event) => match event { DebouncedEvent::Create(mut pathbuf) - | DebouncedEvent::Remove(mut pathbuf) => { - let path = if pathbuf.is_dir() { - if pathbuf.ends_with("cur") | pathbuf.ends_with("new") { + | DebouncedEvent::Remove(mut pathbuf) => { + let path = if pathbuf.is_dir() { + if pathbuf.ends_with("cur") | pathbuf.ends_with("new") { + pathbuf.pop(); + } + pathbuf.to_str().unwrap() + } else { pathbuf.pop(); - } - pathbuf.to_str().unwrap() - } else { - pathbuf.pop(); - pathbuf.parent().unwrap().to_str().unwrap() - }; - eprintln!(" got event in {}", path); + pathbuf.parent().unwrap().to_str().unwrap() + }; + eprintln!(" got event in {}", path); - let mut hasher = DefaultHasher::new(); - hasher.write(path.as_bytes()); - sender.send(RefreshEvent { - folder: format!("{}", path), - hash: hasher.finish(), - }); - } + let mut hasher = DefaultHasher::new(); + hasher.write(path.as_bytes()); + sender.send(RefreshEvent { + folder: format!("{}", path), + hash: hasher.finish(), + }); + } _ => {} }, Err(e) => eprintln!("watch error: {:?}", e), @@ -215,6 +233,9 @@ impl MailBackend for MaildirType { })?; Ok(()) } + fn operation(&self, hash: u64) -> Box { + Box::new(MaildirOp::new(hash, self.hash_index.clone())) + } } impl MaildirType { @@ -235,7 +256,7 @@ impl MaildirType { path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), path_children, - ) { + ) { folders.push(f); children.push(folders.len() - 1); } @@ -251,13 +272,14 @@ impl MaildirType { path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), Vec::with_capacity(0), - ) { + ) { folders.push(f); } } folders[0].children = recurse_folders(&mut folders, &path); MaildirType { folders, + hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(0, Default::default()))), path: f.root_folder().to_string(), } } @@ -270,7 +292,7 @@ impl MaildirType { unreachable!() } - pub fn multicore(&self, cores: usize, folder: &Folder) -> Async>> { + pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async>> { let mut w = AsyncBuilder::new(); let handle = { let tx = w.tx(); @@ -278,6 +300,8 @@ impl MaildirType { let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)]; let path = folder.path().to_string(); let name = format!("parsing {:?}", folder.name()); + let map = self.hash_index.clone(); + let map2 = self.hash_index.clone(); thread::Builder::new() .name(name) @@ -305,25 +329,48 @@ impl MaildirType { }; for chunk in files.chunks(chunk_size) { let mut tx = tx.clone(); + let map = map.clone(); let s = scope.spawn(move || { let len = chunk.len(); let size = if len <= 100 { 100 } else { (len / 100) * 100 }; - let mut local_r: Vec< - Envelope, - > = Vec::with_capacity(chunk.len()); + let mut local_r: Vec = Vec::with_capacity(chunk.len()); for c in chunk.chunks(size) { + let map = map.clone(); let len = c.len(); - for e in c { - let e_copy = e.to_string(); - if let Some(mut e) = Envelope::from_token(Box::new( - BackendOpGenerator::new(Box::new(move || { - Box::new(MaildirOp::new(e_copy.clone())) - })), - )) { - if e.populate_headers().is_err() { - continue; - } - local_r.push(e); + for file in c { + let e_copy = file.to_string(); + /* + * get hash + * lock map + * see if its inside otherwise add it + * check cache + * generate Envelope + * add to local_r + */ + { + let mut hasher = DefaultHasher::new(); + let hash = { + let slice = Mmap::open_path(&e_copy, Protection::Read).unwrap(); + /* Unwrap is safe since we use ? above. */ + hasher.write( + unsafe { slice.as_slice() }); + hasher.finish() + }; + { + let mut map = map.lock().unwrap(); + if (*map).contains_key(&hash) { + continue; + } + (*map).insert(hash, (0, e_copy)); + } + // TODO: Check cache + let op = Box::new(MaildirOp::new(hash, map.clone())); + if let Some(mut e) = Envelope::from_token(op, hash) { + local_r.push(e); + } else { + continue; + } + } } tx.send(AsyncStatus::ProgressReport(len)); @@ -338,13 +385,20 @@ impl MaildirType { let mut result = t.join(); r.append(&mut result); } + let mut map = map2.lock().unwrap(); + for (idx, e) in r.iter().enumerate() { + let mut y = (*map)[&e.hash()].clone(); + y.0 = idx; + (*map).insert(e.hash(),y); + } tx.send(AsyncStatus::Finished); Ok(r) }) - .unwrap() + .unwrap() }; w.build(handle) } + } #[derive(Debug, Default)] @@ -379,9 +433,9 @@ impl MaildirFolder { p.push(d); if !p.is_dir() { return Err(MeliError::new(format!( - "{} is not a valid maildir folder", - path - ))); + "{} is not a valid maildir folder", + path + ))); } p.pop(); } diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index f2e7fcc9c..189bc3043 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -92,9 +92,10 @@ impl RefreshEventConsumer { } } pub trait MailBackend: ::std::fmt::Debug { - fn get(&self, folder: &Folder) -> Async>>; + fn get(&mut self, folder: &Folder) -> Async>>; fn watch(&self, sender: RefreshEventConsumer) -> Result<()>; fn folders(&self) -> Vec; + fn operation(&self, hash: u64) -> Box; //login function } diff --git a/melib/src/mailbox/email/attachment_types.rs b/melib/src/mailbox/email/attachment_types.rs index 8712e7d22..8d9fb39fa 100644 --- a/melib/src/mailbox/email/attachment_types.rs +++ b/melib/src/mailbox/email/attachment_types.rs @@ -2,7 +2,7 @@ use mailbox::email::parser::BytesExt; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::str; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize)] pub enum Charset { Ascii, UTF8, @@ -52,7 +52,7 @@ impl<'a> From<&'a [u8]> for Charset { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub enum MultipartType { Mixed, Alternative, @@ -73,7 +73,7 @@ impl Display for MultipartType { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub enum ContentType { Text { charset: Charset }, Multipart { boundary: Vec }, @@ -108,7 +108,7 @@ impl ContentType { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub enum ContentSubType { Plain, Html, @@ -134,7 +134,7 @@ impl Display for ContentSubType { } } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub enum ContentTransferEncoding { _8Bit, _7Bit, diff --git a/melib/src/mailbox/email/attachments.rs b/melib/src/mailbox/email/attachments.rs index 8f0cfea39..163a09e35 100644 --- a/melib/src/mailbox/email/attachments.rs +++ b/melib/src/mailbox/email/attachments.rs @@ -25,7 +25,7 @@ use std::str; pub use mailbox::email::attachment_types::*; -#[derive(Clone)] +#[derive(Clone, Serialize)] pub enum AttachmentType { Data { tag: Vec, @@ -71,7 +71,7 @@ pub struct AttachmentBuilder { raw: Vec, } -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct Attachment { content_type: (ContentType, ContentSubType), content_transfer_encoding: ContentTransferEncoding, diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index 14814a485..d148fa27a 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -29,7 +29,7 @@ pub mod parser; use parser::BytesExt; use error::{MeliError, Result}; -use mailbox::backends::BackendOpGenerator; +use mailbox::backends::BackendOp; use std::borrow::Cow; use std::cmp::Ordering; @@ -38,26 +38,25 @@ use std::fmt; use std::hash::Hasher; use std::option::Option; use std::string::String; -use std::sync::Arc; use chrono; use chrono::TimeZone; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct GroupAddress { raw: Vec, display_name: StrBuilder, mailbox_list: Vec
, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct MailboxAddress { raw: Vec, display_name: StrBuilder, address_spec: StrBuilder, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub enum Address { Mailbox(MailboxAddress), Group(GroupAddress), @@ -109,7 +108,7 @@ impl fmt::Display for Address { } /// Helper struct to return slices from a struct field on demand. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] struct StrBuilder { offset: usize, length: usize, @@ -134,7 +133,7 @@ impl StrBuilder { } /// `MessageID` is accessed through the `StrBuild` trait. -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct MessageID(Vec, StrBuilder); impl StrBuild for MessageID { @@ -185,14 +184,14 @@ impl fmt::Debug for MessageID { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] struct References { raw: Vec, refs: Vec, } bitflags! { - #[derive(Default)] + #[derive(Default, Serialize)] pub struct Flag: u8 { const PASSED = 0b00000001; const REPLIED = 0b00000010; @@ -222,7 +221,7 @@ impl EnvelopeBuilder { /* * 1. Check for date. Default is now - * 2. + * 2. Envelope { @@ -237,7 +236,7 @@ impl EnvelopeBuilder { /// 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)] +#[derive(Debug, Clone, Serialize)] pub struct Envelope { date: String, from: Vec
, @@ -248,19 +247,18 @@ pub struct Envelope { in_reply_to: Option, references: Option, - datetime: Option>, timestamp: u64, thread: usize, - operation_token: Arc>, + hash: u64, flags: Flag, } impl Envelope { - pub fn new(token: Box) -> Self { + pub fn new(hash: u64) -> Self { Envelope { - date: "".to_string(), + date: String::new(), from: Vec::new(), to: Vec::new(), body: None, @@ -269,103 +267,105 @@ impl Envelope { in_reply_to: None, references: None, - datetime: None, timestamp: 0, thread: 0, - operation_token: Arc::new(token), + hash, flags: Flag::default(), } } - pub fn from_token(operation_token: Box) -> Option { - let operation = operation_token.generate(); - let mut e = Envelope::new(operation_token); + pub fn from_token(operation: Box, hash: u64) -> Option { + let mut e = Envelope::new(hash); e.flags = operation.fetch_flags(); - Some(e) + let res = e.populate_headers(operation).ok(); + if res.is_some() { + Some(e) + } else { + None + } } - pub fn set_operation_token(&mut self, operation_token: Box) { - self.operation_token = Arc::new(operation_token); + pub fn hash(&self) -> u64 { + self.hash } + pub fn populate_headers(&mut self, mut operation: Box) -> Result<()> { + { + let headers = match parser::headers(operation.fetch_headers()?).to_full_result() { + Ok(v) => v, + Err(e) => { + eprintln!("error in parsing mail\n"); + return Err(MeliError::from(e)); + } + }; - pub fn populate_headers(&mut self) -> Result<()> { - let mut operation = self.operation_token.generate(); - let headers = match parser::headers(operation.fetch_headers()?).to_full_result() { - Ok(v) => v, - Err(e) => { - let operation = self.operation_token.generate(); - eprintln!("error in parsing mail\n{}", operation.description()); - return Err(MeliError::from(e)); - } - }; + let mut in_reply_to = None; + let mut datetime = None; - 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(b"to") { - let parse_result = parser::rfc2822address_list(value); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - Vec::new() - }; - self.set_to(value); - } else if name.eq_ignore_ascii_case(b"from") { - let parse_result = parser::rfc2822address_list(value); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - Vec::new() - }; - self.set_from(value); - } else if name.eq_ignore_ascii_case(b"subject") { - let parse_result = parser::phrase(value.trim()); - let value = if parse_result.is_done() { - parse_result.to_full_result().unwrap() - } else { - "".into() - }; - self.set_subject(value); - } else if name.eq_ignore_ascii_case(b"message-id") { - self.set_message_id(value); - } else if name.eq_ignore_ascii_case(b"references") { - { - let parse_result = parser::references(value); - if parse_result.is_done() { - for v in parse_result.to_full_result().unwrap() { - self.push_references(v); + for (name, value) in headers { + if value.len() == 1 && value.is_empty() { + continue; + } + if name.eq_ignore_ascii_case(b"to") { + let parse_result = parser::rfc2822address_list(value); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + Vec::new() + }; + self.set_to(value); + } else if name.eq_ignore_ascii_case(b"from") { + let parse_result = parser::rfc2822address_list(value); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + Vec::new() + }; + self.set_from(value); + } else if name.eq_ignore_ascii_case(b"subject") { + let parse_result = parser::phrase(value.trim()); + let value = if parse_result.is_done() { + parse_result.to_full_result().unwrap() + } else { + "".into() + }; + self.set_subject(value); + } else if name.eq_ignore_ascii_case(b"message-id") { + self.set_message_id(value); + } else if name.eq_ignore_ascii_case(b"references") { + { + let parse_result = parser::references(value); + if parse_result.is_done() { + for v in parse_result.to_full_result().unwrap() { + self.push_references(v); + } } } + self.set_references(value); + } else if name.eq_ignore_ascii_case(b"in-reply-to") { + self.set_in_reply_to(value); + in_reply_to = Some(value); + } else if name.eq_ignore_ascii_case(b"date") { + self.set_date(value); + datetime = Some(value); } - self.set_references(value); - } else if name.eq_ignore_ascii_case(b"in-reply-to") { - self.set_in_reply_to(value); - in_reply_to = Some(value); - } else if name.eq_ignore_ascii_case(b"date") { - self.set_date(value); - datetime = Some(value); } - } - /* - * 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); + /* + * 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); + } + } + } if self.message_id.is_none() { let mut h = DefaultHasher::new(); - h.write(&self.bytes()); + h.write(&self.bytes(operation)); self.set_message_id(format!("<{:x}>", h.finish()).as_bytes()); } Ok(()) @@ -375,11 +375,12 @@ impl Envelope { } pub fn datetime(&self) -> chrono::DateTime { - self.datetime.unwrap_or_else(|| { + if let Some(d) = parser::date(&self.date.as_bytes()) { + return d; + } chrono::FixedOffset::west(0) .ymd(1970, 1, 1) .and_hms(0, 0, 0) - }) } pub fn date_as_str(&self) -> &str { &self.date @@ -399,21 +400,18 @@ impl Envelope { let _strings: Vec = self.to.iter().map(|a| format!("{}", a)).collect(); _strings.join(", ") } - pub fn bytes(&self) -> Vec { - let mut operation = self.operation_token.generate(); + pub fn bytes(&self, mut operation: Box) -> Vec { operation .as_bytes() .map(|v| v.into()) .unwrap_or_else(|_| Vec::new()) } - pub fn body(&self) -> Attachment { - let mut operation = self.operation_token.generate(); + pub fn body(&self, mut operation: Box) -> Attachment { let file = operation.as_bytes(); let (headers, body) = match parser::mail(file.unwrap()).to_full_result() { Ok(v) => v, Err(_) => { - let operation = self.operation_token.generate(); - eprintln!("error in parsing mail\n{}", operation.description()); + eprintln!("error in parsing mail\n"); let error_msg = b"Mail cannot be shown because of errors."; let mut builder = AttachmentBuilder::new(error_msg); return builder.build(); @@ -564,11 +562,9 @@ impl Envelope { self.thread = new_val; } pub fn set_datetime(&mut self, new_val: chrono::DateTime) -> () { - self.datetime = Some(new_val); self.timestamp = new_val.timestamp() as u64; } - pub fn set_flag(&mut self, f: Flag) -> Result<()> { - let mut operation = self.operation_token.generate(); + pub fn set_flag(&mut self, f: Flag, mut operation: Box) -> Result<()> { operation.set_flag(self, &f)?; self.flags |= f; Ok(()) @@ -576,11 +572,11 @@ impl Envelope { pub fn flags(&self) -> Flag { self.flags } - pub fn set_seen(&mut self) -> Result<()> { - self.set_flag(Flag::SEEN) + pub fn set_seen(&mut self, operation: Box) -> Result<()> { + self.set_flag(Flag::SEEN, operation) } pub fn is_seen(&self) -> bool { - !(self.flags & Flag::SEEN).is_empty() + self.flags.contains(Flag::SEEN) } } diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index b1eb4dd2c..e26a0de82 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -128,9 +128,6 @@ named!( named!(pub headers>, many1!(complete!(header))); -//named!(pub headers_raw<&[u8]>, -//take_until1!("\n\n")); - pub fn headers_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { if input.is_empty() { return IResult::Incomplete(Needed::Unknown); @@ -417,22 +414,19 @@ named!( named!(mailbox_list>, many0!(mailbox)); #[test] -fn test_mailbox() { +fn test_addresses() { { - let s = b"epilys@postretch"; + let s = b"user@domain"; let r = mailbox(s).unwrap().1; match r { Address::Mailbox(ref m) => { - println!( - "----\n`{}`, `{}`\n----", - m.display_name.display(&m.raw), - m.address_spec.display(&m.raw) - ); - } - _ => {} + assert!(m.display_name == b"" && m.address_spec == b"user@domain"); + }, + _ => assert!(false), } } - let s = b"Manos "; + { + let s = b"Name "; eprintln!("{:?}", display_addr(s).unwrap()); let r = display_addr(s).unwrap().1; match r { @@ -446,17 +440,25 @@ fn test_mailbox() { _ => {} } } + { + let s = b"user@domain"; + let r = mailbox(s).unwrap().1; + match r { + Address::Mailbox(ref m) => { + println!( + "----\n`{}`, `{}`\n----", + m.display_name.display(&m.raw), + m.address_spec.display(&m.raw) + ); + } + _ => {} + } + } +} -//named!(group_t, ws!( do_parse!( -// display_name: take_until1!(":") >> -// mailbox_list: many0!(mailbox) >> -// end: is_a!(";") >> -// ({ -// -// }) -// ))); -// - +/* + * group of recipients eg. undisclosed-recipients; + */ fn group(input: &[u8]) -> IResult<&[u8], Address> { let mut flag = false; let mut dlength = 0; @@ -499,9 +501,9 @@ named!(address
, ws!(alt_complete!(mailbox | group))); #[test] fn test_address() { - let s = b"Manos Pitsidianakis , - qemu-devel , qemu-block , - Alberto Garcia , Stefan Hajnoczi "; + let s = b"Obit Oppidum , + list , list2 , + Bobit Boppidum , Cobit Coppidum "; println!("{:?}", rfc2822address_list(s).unwrap()); } @@ -552,11 +554,11 @@ named!(pub phrase>, ws!(do_parse!( #[test] fn test_phrase() { - let phrase_s = "list.free.de mailing list memberships reminder".as_bytes(); + let phrase_s = "mailing list memberships reminder".as_bytes(); assert_eq!( ( &b""[..], - "list.free.de mailing list memberships reminder".to_string() + "mailing list memberships reminder".to_string() ), phrase(phrase_s).unwrap() ); @@ -739,16 +741,3 @@ named!(pub content_type< (&[u8], &[u8], Vec<(&[u8], &[u8])>) >, (_type, _subtype, parameters) } ) )); - -//named!(pub quoted_printable_text>, -// do_parse!( -// bytes: many0!(alt_complete!( -// preceded!(tag!("=\n"), quoted_printable_byte) | -// preceded!(tag!("=\n"), le_u8) | -// quoted_printable_byte | -// le_u8)) >> -// ( { -// bytes -// } ) -// ) -//); diff --git a/src/bin.rs b/src/bin.rs index 4aff6265c..817abd48f 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -182,7 +182,7 @@ fn main() { }, ThreadEvent::RefreshMailbox { hash : h } => { state.hash_to_folder(h); - //state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())}); + state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(String::from("Update in mailbox"))}); state.redraw(); }, ThreadEvent::UIEvent(UIEventType::ChangeMode(f)) => { diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 71159b3da..46e84d86a 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -20,6 +20,7 @@ */ use super::*; +use melib::mailbox::backends::BackendOp; const MAX_COLS: usize = 500; /// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the thread's content in a @@ -89,7 +90,7 @@ impl CompactMailListing { break; } } - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] + let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] .as_ref() .unwrap(); @@ -188,6 +189,7 @@ impl CompactMailListing { container, &indentations, len, + context.accounts[self.cursor_pos.0].backend.operation(envelope.hash()), ), &mut content, fg_color, @@ -347,6 +349,7 @@ impl CompactMailListing { container: &Container, indentations: &[bool], idx_width: usize, + op: Box, ) -> String { let has_sibling = container.has_sibling(); let has_parent = container.has_parent(); @@ -383,7 +386,7 @@ impl CompactMailListing { if show_subject { s.push_str(&format!("{:.85}", envelope.subject())); } - let attach_count = envelope.body().count_attachments(); + let attach_count = envelope.body(op).count_attachments(); if attach_count > 1 { s.push_str(&format!(" {}∞ ", attach_count - 1)); } @@ -439,17 +442,34 @@ impl Component for CompactMailListing { let threaded = context.accounts[self.cursor_pos.0] .runtime_settings .threaded; - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] - .as_mut() - .unwrap(); - let envelope: &mut Envelope = if threaded { - let i = mailbox.threaded_mail(idx); - &mut mailbox.collection[i] - } else { - &mut mailbox.collection[idx] + let account = &mut context.accounts[self.cursor_pos.0]; + let (hash, is_seen) = { + let mailbox = &mut account[self.cursor_pos.1] + .as_mut() + .unwrap(); + let envelope: &mut Envelope = if threaded { + let i = mailbox.threaded_mail(idx); + &mut mailbox.collection[i] + } else { + &mut mailbox.collection[idx] + }; + (envelope.hash(), envelope.is_seen()) }; - if !envelope.is_seen() { - envelope.set_seen().unwrap(); + if is_seen { + let op = { + let backend = &account.backend; + backend.operation(hash) + }; + let mailbox = &mut account[self.cursor_pos.1] + .as_mut() + .unwrap(); + let envelope: &mut Envelope = if threaded { + let i = mailbox.threaded_mail(idx); + &mut mailbox.collection[i] + } else { + &mut mailbox.collection[idx] + }; + envelope.set_seen(op).unwrap(); true } else { false diff --git a/ui/src/components/mail/listing/mod.rs b/ui/src/components/mail/listing/mod.rs index 55aebc54e..21863ac63 100644 --- a/ui/src/components/mail/listing/mod.rs +++ b/ui/src/components/mail/listing/mod.rs @@ -21,6 +21,7 @@ use super::*; +use melib::mailbox::backends::BackendOp; mod compact; pub use self::compact::*; @@ -107,7 +108,7 @@ impl MailListing { break; } } - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] + let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] .as_ref() .unwrap(); @@ -139,10 +140,18 @@ impl MailListing { let threads: &Vec = &mailbox.threads; local_collection.sort_by(|a, b| match self.sort { (SortField::Date, SortOrder::Desc) => { - mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date()) + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + mb.date().cmp(&ma.date()) } (SortField::Date, SortOrder::Asc) => { - mailbox.thread(*a).date().cmp(&mailbox.thread(*b).date()) + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + ma.date().cmp(&mb.date()) } (SortField::Subject, SortOrder::Desc) => { let a = mailbox.thread(*a); @@ -211,6 +220,7 @@ impl MailListing { container, &indentations, len, + context.accounts[self.cursor_pos.0].backend.operation(envelope.hash()) ), &mut content, fg_color, @@ -411,6 +421,7 @@ impl MailListing { container: &Container, indentations: &[bool], idx_width: usize, + op: Box, ) -> String { let has_sibling = container.has_sibling(); let has_parent = container.has_parent(); @@ -447,7 +458,7 @@ impl MailListing { if show_subject { s.push_str(&format!("{:.85}", envelope.subject())); } - let attach_count = envelope.body().count_attachments(); + let attach_count = envelope.body(op).count_attachments(); if attach_count > 1 { s.push_str(&format!(" {}∞ ", attach_count - 1)); } @@ -503,17 +514,34 @@ impl Component for MailListing { let threaded = context.accounts[self.cursor_pos.0] .runtime_settings .threaded; - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] - .as_mut() - .unwrap(); - let envelope: &mut Envelope = if threaded { - let i = mailbox.threaded_mail(idx); - &mut mailbox.collection[i] - } else { - &mut mailbox.collection[idx] + let account = &mut context.accounts[self.cursor_pos.0]; + let (hash, is_seen) = { + let mailbox = &mut account[self.cursor_pos.1] + .as_mut() + .unwrap(); + let envelope: &mut Envelope = if threaded { + let i = mailbox.threaded_mail(idx); + &mut mailbox.collection[i] + } else { + &mut mailbox.collection[idx] + }; + (envelope.hash(), envelope.is_seen()) }; - if !envelope.is_seen() { - envelope.set_seen().unwrap(); + if !is_seen { + let op = { + let backend = &account.backend; + backend.operation(hash) + }; + let mailbox = &mut account[self.cursor_pos.1] + .as_mut() + .unwrap(); + let envelope: &mut Envelope = if threaded { + let i = mailbox.threaded_mail(idx); + &mut mailbox.collection[i] + } else { + &mut mailbox.collection[idx] + }; + envelope.set_seen(op).unwrap(); true } else { false diff --git a/ui/src/components/mail/view/mod.rs b/ui/src/components/mail/view/mod.rs index 25c7733e6..724e0657c 100644 --- a/ui/src/components/mail/view/mod.rs +++ b/ui/src/components/mail/view/mod.rs @@ -303,11 +303,12 @@ impl Component for MailView { if self.dirty { let mailbox_idx = self.coordinates; // coordinates are mailbox idxs - let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1] + let mailbox = &context.accounts[mailbox_idx.0][mailbox_idx.1] .as_ref() .unwrap(); let envelope: &Envelope = &mailbox.collection[envelope_idx]; - let body = envelope.body(); + let op = context.accounts[mailbox_idx.0].backend.operation(envelope.hash()); + let body = envelope.body(op); match self.mode { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { self.subview = Some(Box::new(HtmlView::new(decode( @@ -372,9 +373,9 @@ impl Component for MailView { self.cmd_buf.clear(); { - let accounts = &mut context.accounts; + let accounts = &context.accounts; let threaded = accounts[self.coordinates.0].runtime_settings.threaded; - let mailbox = &mut accounts[self.coordinates.0][self.coordinates.1] + let mailbox = &accounts[self.coordinates.0][self.coordinates.1] .as_ref() .unwrap(); let envelope_idx: usize = if threaded { @@ -384,7 +385,8 @@ impl Component for MailView { }; let envelope: &Envelope = &mailbox.collection[envelope_idx]; - if let Some(u) = envelope.body().attachments().get(lidx) { + let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash()); + if let Some(u) = envelope.body(op).attachments().get(lidx) { match u.content_type().0 { ContentType::Text { .. } => { self.mode = ViewMode::Attachment(lidx); @@ -443,9 +445,9 @@ impl Component for MailView { let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); let url = { - let accounts = &mut context.accounts; + let accounts = &context.accounts; let threaded = accounts[self.coordinates.0].runtime_settings.threaded; - let mailbox = &mut accounts[self.coordinates.0][self.coordinates.1] + let mailbox = &accounts[self.coordinates.0][self.coordinates.1] .as_ref() .unwrap(); let envelope_idx: usize = if threaded { @@ -456,7 +458,8 @@ impl Component for MailView { let envelope: &Envelope = &mailbox.collection[envelope_idx]; let finder = LinkFinder::new(); - let mut t = envelope.body().text().to_string(); + let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash()); + let mut t = envelope.body(op).text().to_string(); let links: Vec = finder.links(&t).collect(); if let Some(u) = links.get(lidx) { u.as_str().to_string()