Move backend logic to backend and keep Envelope abstract

embed
Manos Pitsidianakis 2018-08-12 16:55:45 +03:00
parent e316640f68
commit 5889494e9e
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
11 changed files with 344 additions and 253 deletions

View File

@ -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<Folder> = backend.folders();
let mut folders: Vec<Option<Result<Mailbox>>> = Vec::with_capacity(ref_folders.len());
let mut workers: Vec<Worker> = Vec::new();

View File

@ -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<Mutex<FnvHashMap<u64, (usize, String)>>>,
hash: u64,
slice: Option<Mmap>,
}
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<Mutex<FnvHashMap<u64, (usize, String)>>>) -> 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<MaildirFolder>,
hash_index: Arc<Mutex<FnvHashMap<u64, (usize, String)>>>,
path: String,
}
@ -161,7 +179,7 @@ impl MailBackend for MaildirType {
fn folders(&self) -> Vec<Folder> {
self.folders.iter().map(|f| f.clone()).collect()
}
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
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<BackendOp> {
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<Result<Vec<Envelope>>> {
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
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<Envelope> = 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();
}

View File

@ -92,9 +92,10 @@ impl RefreshEventConsumer {
}
}
pub trait MailBackend: ::std::fmt::Debug {
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
fn folders(&self) -> Vec<Folder>;
fn operation(&self, hash: u64) -> Box<BackendOp>;
//login function
}

View File

@ -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<u8> },
@ -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,

View File

@ -25,7 +25,7 @@ use std::str;
pub use mailbox::email::attachment_types::*;
#[derive(Clone)]
#[derive(Clone, Serialize)]
pub enum AttachmentType {
Data {
tag: Vec<u8>,
@ -71,7 +71,7 @@ pub struct AttachmentBuilder {
raw: Vec<u8>,
}
#[derive(Clone)]
#[derive(Clone, Serialize)]
pub struct Attachment {
content_type: (ContentType, ContentSubType),
content_transfer_encoding: ContentTransferEncoding,

View File

@ -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<u8>,
display_name: StrBuilder,
mailbox_list: Vec<Address>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize)]
pub struct MailboxAddress {
raw: Vec<u8>,
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<u8>, 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<u8>,
refs: Vec<MessageID>,
}
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<Address>,
@ -248,19 +247,18 @@ pub struct Envelope {
in_reply_to: Option<MessageID>,
references: Option<References>,
datetime: Option<chrono::DateTime<chrono::FixedOffset>>,
timestamp: u64,
thread: usize,
operation_token: Arc<Box<BackendOpGenerator>>,
hash: u64,
flags: Flag,
}
impl Envelope {
pub fn new(token: Box<BackendOpGenerator>) -> 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<BackendOpGenerator>) -> Option<Envelope> {
let operation = operation_token.generate();
let mut e = Envelope::new(operation_token);
pub fn from_token(operation: Box<BackendOp>, hash: u64) -> Option<Envelope> {
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<BackendOpGenerator>) {
self.operation_token = Arc::new(operation_token);
pub fn hash(&self) -> u64 {
self.hash
}
pub fn populate_headers(&mut self, mut operation: Box<BackendOp>) -> 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<chrono::FixedOffset> {
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<String> = self.to.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
}
pub fn bytes(&self) -> Vec<u8> {
let mut operation = self.operation_token.generate();
pub fn bytes(&self, mut operation: Box<BackendOp>) -> Vec<u8> {
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<BackendOp>) -> 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<chrono::FixedOffset>) -> () {
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<BackendOp>) -> 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<BackendOp>) -> Result<()> {
self.set_flag(Flag::SEEN, operation)
}
pub fn is_seen(&self) -> bool {
!(self.flags & Flag::SEEN).is_empty()
self.flags.contains(Flag::SEEN)
}
}

View File

@ -128,9 +128,6 @@ named!(
named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>,
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<Vec<Address>>, 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 <epilys@postretch>";
{
let s = b"Name <user@domain>";
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<GroupAddress>, 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<Address>, ws!(alt_complete!(mailbox | group)));
#[test]
fn test_address() {
let s = b"Manos Pitsidianakis <el13635@mail.ntua.gr>,
qemu-devel <qemu-devel@nongnu.org>, qemu-block <qemu-block@nongnu.org>,
Alberto Garcia <berto@igalia.com>, Stefan Hajnoczi <stefanha@redhat.com>";
let s = b"Obit Oppidum <user@domain>,
list <list@domain.tld>, list2 <list2@domain.tld>,
Bobit Boppidum <user@otherdomain.com>, Cobit Coppidum <user2@otherdomain.com>";
println!("{:?}", rfc2822address_list(s).unwrap());
}
@ -552,11 +554,11 @@ named!(pub phrase<Vec<u8>>, 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<Vec<u8>>,
// do_parse!(
// bytes: many0!(alt_complete!(
// preceded!(tag!("=\n"), quoted_printable_byte) |
// preceded!(tag!("=\n"), le_u8) |
// quoted_printable_byte |
// le_u8)) >>
// ( {
// bytes
// } )
// )
//);

View File

@ -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)) => {

View File

@ -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<BackendOp>,
) -> 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

View File

@ -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<Container> = &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<BackendOp>,
) -> 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

View File

@ -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::<usize>().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<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) {
u.as_str().to_string()