Run clippy lints

embed
Manos Pitsidianakis 2018-08-23 15:36:52 +03:00
parent b617fc0136
commit 2b6d1e0dbf
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
29 changed files with 860 additions and 655 deletions

View File

@ -58,6 +58,12 @@ pub struct Async<T> {
rx: chan::Receiver<AsyncStatus>, rx: chan::Receiver<AsyncStatus>,
} }
impl Default for AsyncBuilder {
fn default() -> Self {
AsyncBuilder::new()
}
}
impl AsyncBuilder { impl AsyncBuilder {
pub fn new() -> Self { pub fn new() -> Self {
let (sender, receiver) = chan::sync(::std::mem::size_of::<AsyncStatus>()); let (sender, receiver) = chan::sync(::std::mem::size_of::<AsyncStatus>());
@ -117,6 +123,6 @@ impl<T> Async<T> {
} }
let v = self.worker.take().unwrap().join().unwrap(); let v = self.worker.take().unwrap().join().unwrap();
self.value = Some(v); self.value = Some(v);
return Ok(AsyncStatus::Finished); Ok(AsyncStatus::Finished)
} }
} }

View File

@ -19,15 +19,14 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
extern crate xdg;
extern crate bincode; extern crate bincode;
extern crate xdg;
use async::*; use async::*;
use conf::AccountSettings; use conf::AccountSettings;
use error::{MeliError, Result}; use error::{MeliError, Result};
use mailbox::backends::{ use mailbox::backends::{
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer,
RefreshEventConsumer,
}; };
use mailbox::email::parser; use mailbox::email::parser;
use mailbox::email::{Envelope, Flag}; use mailbox::email::{Envelope, Flag};
@ -46,11 +45,11 @@ extern crate crossbeam;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::fs; use std::fs;
use std::hash::{Hash, Hasher};
use std::io; use std::io;
use std::io::Read; use std::io::Read;
use std::sync::{Mutex, Arc};
use std::hash::{Hasher, Hash};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
extern crate fnv; extern crate fnv;
use self::fnv::FnvHashMap; use self::fnv::FnvHashMap;
@ -66,7 +65,7 @@ impl Clone for MaildirOp {
fn clone(&self) -> Self { fn clone(&self) -> Self {
MaildirOp { MaildirOp {
hash_index: self.hash_index.clone(), hash_index: self.hash_index.clone(),
hash: self.hash.clone(), hash: self.hash,
slice: None, slice: None,
} }
} }
@ -89,7 +88,7 @@ impl MaildirOp {
impl<'a> BackendOp for MaildirOp { impl<'a> BackendOp for MaildirOp {
fn description(&self) -> String { fn description(&self) -> String {
format!("Path of file:")// self.0ipath) format!("Path of file: {}", self.path())
} }
fn as_bytes(&mut self) -> Result<&[u8]> { fn as_bytes(&mut self) -> Result<&[u8]> {
if self.slice.is_none() { if self.slice.is_none() {
@ -133,10 +132,10 @@ impl<'a> BackendOp for MaildirOp {
fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> { fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> {
let path = self.path(); let path = self.path();
let idx: usize = path.rfind(":2,").ok_or(MeliError::new(format!( let idx: usize = path
"Invalid email filename: {:?}", .rfind(":2,")
self .ok_or_else(|| MeliError::new(format!("Invalid email filename: {:?}", self)))?
)))? + 3; + 3;
let mut new_name: String = path[..idx].to_string(); let mut new_name: String = path[..idx].to_string();
let mut flags = self.fetch_flags(); let mut flags = self.fetch_flags();
if !(flags & *f).is_empty() { if !(flags & *f).is_empty() {
@ -212,24 +211,24 @@ impl MailBackend for MaildirType {
match rx.recv() { match rx.recv() {
Ok(event) => match event { Ok(event) => match event {
DebouncedEvent::Create(mut pathbuf) DebouncedEvent::Create(mut pathbuf)
| DebouncedEvent::Remove(mut pathbuf) => { | DebouncedEvent::Remove(mut pathbuf) => {
if pathbuf.is_dir() { if pathbuf.is_dir() {
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") { if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
pathbuf.pop();
}
} else {
pathbuf.pop(); pathbuf.pop();
pathbuf.pop(); }
}; } else {
eprintln!(" got event in {}", pathbuf.display()); pathbuf.pop();
pathbuf.pop();
};
eprintln!(" got event in {}", pathbuf.display());
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
pathbuf.hash(&mut hasher); pathbuf.hash(&mut hasher);
sender.send(RefreshEvent { sender.send(RefreshEvent {
folder: format!("{}", pathbuf.display()), folder: format!("{}", pathbuf.display()),
hash: hasher.finish(), hash: hasher.finish(),
}); });
} }
_ => {} _ => {}
}, },
Err(e) => eprintln!("watch error: {:?}", e), Err(e) => eprintln!("watch error: {:?}", e),
@ -261,7 +260,7 @@ impl MaildirType {
path.to_str().unwrap().to_string(), path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(),
path_children, path_children,
) { ) {
folders.push(f); folders.push(f);
children.push(folders.len() - 1); children.push(folders.len() - 1);
} }
@ -277,7 +276,7 @@ impl MaildirType {
path.to_str().unwrap().to_string(), path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(),
Vec::with_capacity(0), Vec::with_capacity(0),
) { ) {
folders.push(f); folders.push(f);
} }
} }
@ -285,7 +284,10 @@ impl MaildirType {
MaildirType { MaildirType {
name: f.name().to_string(), name: f.name().to_string(),
folders, folders,
hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(0, Default::default()))), hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
0,
Default::default(),
))),
path: f.root_folder().to_string(), path: f.root_folder().to_string(),
} }
} }
@ -342,28 +344,33 @@ impl MaildirType {
let s = scope.builder().name(name.clone()).spawn(move || { let s = scope.builder().name(name.clone()).spawn(move || {
let len = chunk.len(); let len = chunk.len();
let size = if len <= 100 { 100 } else { (len / 100) * 100 }; 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) { for c in chunk.chunks(size) {
let map = map.clone(); let map = map.clone();
let len = c.len(); let len = c.len();
for file in c { for file in c {
let ri = file.rfind("/").unwrap() + 1; let ri = file.rfind('/').unwrap() + 1;
let file_name = &file[ri..]; let file_name = &file[ri..];
if let Some(cached) = cache_dir.find_cache_file(file_name) { if let Some(cached) =
cache_dir.find_cache_file(file_name)
{
// TODO:: error checking // TODO:: error checking
let reader = io::BufReader::new(fs::File::open(cached).unwrap()); let reader = io::BufReader::new(
fs::File::open(cached).unwrap(),
);
let env: Envelope = bincode::deserialize_from(reader).unwrap(); let env: Envelope = bincode::deserialize_from(reader).unwrap();
{ {
let mut map = map.lock().unwrap(); let mut map = map.lock().unwrap();
let hash = env.hash(); let hash = env.hash();
if (*map).contains_key(&hash) { if (*map).contains_key(&hash) {
continue;
}
(*map).insert(hash, (0, file.to_string()));
local_r.push(env);
continue; continue;
} }
(*map).insert(hash, (0, file.to_string()));
local_r.push(env);
continue;
}
} }
let e_copy = file.to_string(); let e_copy = file.to_string();
/* /*
@ -376,41 +383,48 @@ impl MaildirType {
*/ */
{ {
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
let hash = { let hash = {
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut f = fs::File::open(&e_copy).expect(&format!("Can't open {}", e_copy)); let mut f = fs::File::open(&e_copy)
f.read_to_end(&mut buf).expect(&format!("Can't read {}", e_copy)); .unwrap_or_else(|_| {
/* Unwrap is safe since we use ? above. */ panic!("Can't open {}", e_copy)
hasher.write(&buf); });
hasher.finish() f.read_to_end(&mut buf).unwrap_or_else(|_| {
}; panic!("Can't read {}", e_copy)
{ });
let mut map = map.lock().unwrap(); /* Unwrap is safe since we use ? above. */
if (*map).contains_key(&hash) { hasher.write(&buf);
continue; hasher.finish()
}
(*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) {
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
let f = match fs::File::create(cached) {
Ok(f) => f,
Err(e) => {
panic!("{}",e);
}
}; };
let writer = io::BufWriter::new(f); {
bincode::serialize_into(writer, &e).unwrap(); let mut map = map.lock().unwrap();
} if (*map).contains_key(&hash) {
local_r.push(e);
} else {
continue; 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)
{
if let Ok(cached) =
cache_dir.place_cache_file(file_name)
{
let f = match fs::File::create(cached) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &e)
.unwrap();
}
local_r.push(e);
} else {
continue;
}
} }
} }
tx.send(AsyncStatus::ProgressReport(len)); tx.send(AsyncStatus::ProgressReport(len));
@ -429,16 +443,15 @@ impl MaildirType {
for (idx, e) in r.iter().enumerate() { for (idx, e) in r.iter().enumerate() {
let mut y = (*map)[&e.hash()].clone(); let mut y = (*map)[&e.hash()].clone();
y.0 = idx; y.0 = idx;
(*map).insert(e.hash(),y); (*map).insert(e.hash(), y);
} }
tx.send(AsyncStatus::Finished); tx.send(AsyncStatus::Finished);
Ok(r) Ok(r)
}) })
.unwrap() .unwrap()
}; };
w.build(handle) w.build(handle)
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -459,7 +472,7 @@ impl MaildirFolder {
hash: h.finish(), hash: h.finish(),
name: file_name, name: file_name,
path: pathbuf, path: pathbuf,
children: children, children,
}; };
ret.is_valid()?; ret.is_valid()?;
Ok(ret) Ok(ret)
@ -474,9 +487,9 @@ impl MaildirFolder {
p.push(d); p.push(d);
if !p.is_dir() { if !p.is_dir() {
return Err(MeliError::new(format!( return Err(MeliError::new(format!(
"{} is not a valid maildir folder", "{} is not a valid maildir folder",
path.display() path.display()
))); )));
} }
p.pop(); p.pop();
} }

View File

@ -44,6 +44,12 @@ pub struct Backends {
map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>, map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>,
} }
impl Default for Backends {
fn default() -> Self {
Backends::new()
}
}
impl Backends { impl Backends {
pub fn new() -> Self { pub fn new() -> Self {
let mut b = Backends { let mut b = Backends {

View File

@ -1,5 +1,5 @@
use mailbox::email::parser::BytesExt;
use mailbox::email::attachments::Attachment; use mailbox::email::attachments::Attachment;
use mailbox::email::parser::BytesExt;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str; use std::str;
@ -20,13 +20,11 @@ impl SliceBuild {
//fn length(&self) -> usize { //fn length(&self) -> usize {
// self.end - self.offset + 1 // self.end - self.offset + 1
//} //}
pub fn get<'a>(&self, slice:&'a [u8]) -> &'a [u8] { pub fn get<'a>(&self, slice: &'a [u8]) -> &'a [u8] {
&slice[self.offset..self.end] &slice[self.offset..self.end]
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Charset { pub enum Charset {
Ascii, Ascii,
@ -72,7 +70,7 @@ impl<'a> From<&'a [u8]> for Charset {
_ => { _ => {
eprintln!("unknown tag is {:?}", str::from_utf8(b)); eprintln!("unknown tag is {:?}", str::from_utf8(b));
Charset::Ascii Charset::Ascii
}, }
} }
} }
} }
@ -102,10 +100,19 @@ impl Display for MultipartType {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ContentType { pub enum ContentType {
Text { kind: Text, charset: Charset }, Text {
Multipart { boundary: SliceBuild, kind: MultipartType, subattachments: Vec<Attachment>}, kind: Text,
charset: Charset,
},
Multipart {
boundary: SliceBuild,
kind: MultipartType,
subattachments: Vec<Attachment>,
},
MessageRfc822, MessageRfc822,
Unsupported { tag: Vec<u8> }, Unsupported {
tag: Vec<u8>,
},
} }
impl Default for ContentType { impl Default for ContentType {
@ -137,7 +144,10 @@ impl ContentType {
} }
} }
pub fn is_text_html(&self) -> bool { pub fn is_text_html(&self) -> bool {
if let ContentType::Text { kind: Text::Html, .. } = self { if let ContentType::Text {
kind: Text::Html, ..
} = self
{
true true
} else { } else {
false false

View File

@ -19,9 +19,9 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
use data_encoding::BASE64_MIME; use data_encoding::BASE64_MIME;
use mailbox::email::EnvelopeWrapper;
use mailbox::email::parser; use mailbox::email::parser;
use mailbox::email::parser::BytesExt; use mailbox::email::parser::BytesExt;
use mailbox::email::EnvelopeWrapper;
use std::fmt; use std::fmt;
use std::str; use std::str;
@ -84,7 +84,7 @@ impl AttachmentBuilder {
let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize); let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize);
let boundary = SliceBuild::new(offset, _boundary.len()); let boundary = SliceBuild::new(offset, _boundary.len());
let subattachments = Self::subattachments(&self.raw, boundary.get(&value)); let subattachments = Self::subattachments(&self.raw, boundary.get(&value));
assert!(subattachments.len() > 0); assert!(!subattachments.is_empty());
self.content_type = ContentType::Multipart { self.content_type = ContentType::Multipart {
boundary, boundary,
kind: if cst.eq_ignore_ascii_case(b"mixed") { kind: if cst.eq_ignore_ascii_case(b"mixed") {
@ -96,7 +96,7 @@ impl AttachmentBuilder {
} else { } else {
Default::default() Default::default()
}, },
subattachments subattachments,
}; };
} else if ct.eq_ignore_ascii_case(b"text") { } else if ct.eq_ignore_ascii_case(b"text") {
self.content_type = ContentType::Text { self.content_type = ContentType::Text {
@ -105,28 +105,28 @@ impl AttachmentBuilder {
}; };
for (n, v) in params { for (n, v) in params {
if n.eq_ignore_ascii_case(b"charset") { if n.eq_ignore_ascii_case(b"charset") {
match self.content_type { if let ContentType::Text {
ContentType::Text { charset: ref mut c, .. } => { charset: ref mut c, ..
*c = Charset::from(v); } = self.content_type
}, {
_ => {}, *c = Charset::from(v);
} }
break; break;
} }
} }
if cst.eq_ignore_ascii_case(b"html") { if cst.eq_ignore_ascii_case(b"html") {
match self.content_type { if let ContentType::Text {
ContentType::Text { kind: ref mut k, .. } => { kind: ref mut k, ..
*k = Text::Html; } = self.content_type
}, {
_ => {}, *k = Text::Html;
} }
} else if !cst.eq_ignore_ascii_case(b"plain") { } else if !cst.eq_ignore_ascii_case(b"plain") {
match self.content_type { if let ContentType::Text {
ContentType::Text { kind: ref mut k, .. } => { kind: ref mut k, ..
*k = Text::Other { tag: cst.into() }; } = self.content_type
}, {
_ => {}, *k = Text::Other { tag: cst.into() };
} }
} }
} else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") { } else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") {
@ -136,9 +136,7 @@ impl AttachmentBuilder {
tag.extend(ct); tag.extend(ct);
tag.push(b'/'); tag.push(b'/');
tag.extend(cst); tag.extend(cst);
self.content_type = ContentType::Unsupported { self.content_type = ContentType::Unsupported { tag };
tag
};
}, },
Err(v) => { Err(v) => {
eprintln!("parsing error in content_type: {:?} {:?}", value, v); eprintln!("parsing error in content_type: {:?} {:?}", value, v);
@ -197,8 +195,7 @@ impl AttachmentBuilder {
} }
pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> { pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
match parser::attachments(raw, boundary).to_full_result() match parser::attachments(raw, boundary).to_full_result() {
{
Ok(attachments) => { Ok(attachments) => {
let mut vec = Vec::with_capacity(attachments.len()); let mut vec = Vec::with_capacity(attachments.len());
for a in attachments { for a in attachments {
@ -246,37 +243,29 @@ impl AttachmentBuilder {
impl fmt::Display for Attachment { impl fmt::Display for Attachment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.content_type { match self.content_type {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => match EnvelopeWrapper::new(self.bytes().to_vec()) {
match EnvelopeWrapper::new(self.bytes().to_vec()) { Ok(wrapper) => write!(
Ok(wrapper) => write!(f, "message/rfc822: {} - {} - {}", wrapper.date(), wrapper.from_to_string(), wrapper.subject()), f,
Err(e) => write!(f, "{}", e), "message/rfc822: {} - {} - {}",
} wrapper.date(),
wrapper.field_from_to_string(),
wrapper.subject()
),
Err(e) => write!(f, "{}", e),
}, },
ContentType::Unsupported { .. } => { ContentType::Unsupported { .. } => {
write!(f, "Data attachment of type {}", self.mime_type()) write!(f, "Data attachment of type {}", self.mime_type())
} }
ContentType::Text { .. } => { ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
write!(f, "Text attachment of type {}", self.mime_type())
}
ContentType::Multipart { ContentType::Multipart {
kind: ref multipart_type,
subattachments: ref sub_att_vec, subattachments: ref sub_att_vec,
.. ..
} => if *multipart_type == MultipartType::Alternative { } => write!(
write!( f,
f, "{} attachment with {} subs",
"{} attachment with {} subs", self.mime_type(),
self.mime_type(), sub_att_vec.len()
sub_att_vec.len() ),
)
} else {
write!(
f,
"{} attachment with {} subs",
self.mime_type(),
sub_att_vec.len()
)
},
} }
} }
} }
@ -297,20 +286,23 @@ impl Attachment {
} => match *multipart_type { } => match *multipart_type {
MultipartType::Alternative => { MultipartType::Alternative => {
for a in sub_att_vec { for a in sub_att_vec {
if let ContentType::Text { kind: Text::Plain, .. } = a.content_type { if let ContentType::Text {
kind: Text::Plain, ..
} = a.content_type
{
a.get_text_recursive(text); a.get_text_recursive(text);
break; break;
} }
} }
}, }
MultipartType::Mixed | MultipartType::Digest => { MultipartType::Mixed | MultipartType::Digest => {
for a in sub_att_vec { for a in sub_att_vec {
a.get_text_recursive(text); a.get_text_recursive(text);
text.extend_from_slice(b"\n\n"); text.extend_from_slice(b"\n\n");
} }
}, }
} },
_ => {}, _ => {}
} }
} }
pub fn text(&self) -> String { pub fn text(&self) -> String {
@ -356,7 +348,9 @@ impl Attachment {
} }
pub fn is_html(&self) -> bool { pub fn is_html(&self) -> bool {
match self.content_type { match self.content_type {
ContentType::Text { kind: Text::Html, .. } => true, ContentType::Text {
kind: Text::Html, ..
} => true,
_ => false, _ => false,
} }
} }
@ -369,13 +363,13 @@ pub fn interpret_format_flowed(_t: &str) -> String {
fn decode_rfc822(_raw: &[u8]) -> Attachment { fn decode_rfc822(_raw: &[u8]) -> Attachment {
let builder = AttachmentBuilder::new(b""); let builder = AttachmentBuilder::new(b"");
return builder.build(); builder.build()
/* /*
eprintln!("raw is\n{:?}", str::from_utf8(raw).unwrap()); eprintln!("raw is\n{:?}", str::from_utf8(raw).unwrap());
let e = match Envelope::from_bytes(raw) { let e = match Envelope::from_bytes(raw) {
Some(e) => e, Some(e) => e,
None => { None => {
eprintln!("error in parsing mail"); eprintln!("error in parsing mail");
let error_msg = b"Mail cannot be shown because of errors."; let error_msg = b"Mail cannot be shown because of errors.";
let mut builder = AttachmentBuilder::new(error_msg); let mut builder = AttachmentBuilder::new(error_msg);
@ -384,10 +378,11 @@ fn decode_rfc822(_raw: &[u8]) -> Attachment {
}; };
e.body(None) e.body(None)
*/ */
} }
fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> { type Filter = Box<Fn(&Attachment, &mut Vec<u8>) -> ()>;
fn decode_rec_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
let mut ret = match a.content_type { let mut ret = match a.content_type {
ContentType::Unsupported { .. } => Vec::new(), ContentType::Unsupported { .. } => Vec::new(),
ContentType::Text { .. } => decode_helper(a, filter), ContentType::Text { .. } => decode_helper(a, filter),
@ -398,7 +393,10 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Ve
.. ..
} => if *multipart_type == MultipartType::Alternative { } => if *multipart_type == MultipartType::Alternative {
for a in sub_att_vec { for a in sub_att_vec {
if let ContentType::Text { kind: Text::Plain, .. } = a.content_type { if let ContentType::Text {
kind: Text::Plain, ..
} = a.content_type
{
return decode_helper(a, filter); return decode_helper(a, filter);
} }
} }
@ -417,10 +415,10 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Ve
} }
ret ret
} }
pub fn decode_rec(a: &Attachment, filter: Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> { pub fn decode_rec(a: &Attachment, filter: Option<Filter>) -> Vec<u8> {
decode_rec_helper(a, &filter) decode_rec_helper(a, &filter)
} }
fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> { fn decode_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
let charset = match a.content_type { let charset = match a.content_type {
ContentType::Text { charset: c, .. } => c, ContentType::Text { charset: c, .. } => c,
_ => Default::default(), _ => Default::default(),
@ -449,12 +447,11 @@ fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8
bytes.to_vec() bytes.to_vec()
}; };
if let Some(filter) = filter { if let Some(filter) = filter {
filter(a, &mut ret); filter(a, &mut ret);
} }
ret ret
} }
pub fn decode(a: &Attachment, filter: Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> { pub fn decode(a: &Attachment, filter: Option<Filter>) -> Vec<u8> {
decode_helper(a, &filter) decode_helper(a, &filter)
} }

View File

@ -129,10 +129,10 @@ impl StrBuilder {
let offset = self.offset; let offset = self.offset;
let length = self.length; let length = self.length;
String::from_utf8(s[offset..offset + length].to_vec()).unwrap() String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
} }
#[cfg(test)] #[cfg(test)]
fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] { fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] {
&b[self.offset..(self.offset+self.length)] &b[self.offset..(self.offset + self.length)]
} }
} }
@ -146,7 +146,7 @@ impl StrBuild for MessageID {
MessageID( MessageID(
string.to_owned(), string.to_owned(),
StrBuilder { StrBuilder {
offset: offset, offset,
length: slice.len() + 1, length: slice.len() + 1,
}, },
) )
@ -197,12 +197,12 @@ struct References {
bitflags! { bitflags! {
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Flag: u8 { pub struct Flag: u8 {
const PASSED = 0b00000001; const PASSED = 0b0000_0001;
const REPLIED = 0b00000010; const REPLIED = 0b0000_0010;
const SEEN = 0b00000100; const SEEN = 0b0000_0100;
const TRASHED = 0b00001000; const TRASHED = 0b0000_1000;
const DRAFT = 0b00010000; const DRAFT = 0b0001_0000;
const FLAGGED = 0b00100000; const FLAGGED = 0b0010_0000;
} }
} }
@ -212,7 +212,6 @@ pub struct EnvelopeWrapper {
buffer: Vec<u8>, buffer: Vec<u8>,
} }
use std::ops::Deref; use std::ops::Deref;
impl Deref for EnvelopeWrapper { impl Deref for EnvelopeWrapper {
@ -317,7 +316,6 @@ impl Envelope {
self.hash self.hash
} }
pub fn populate_headers(&mut self, bytes: &[u8]) -> Result<()> { pub fn populate_headers(&mut self, bytes: &[u8]) -> Result<()> {
let (headers, _) = match parser::mail(bytes).to_full_result() { let (headers, _) = match parser::mail(bytes).to_full_result() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
@ -372,13 +370,13 @@ impl Envelope {
self.set_in_reply_to(value); self.set_in_reply_to(value);
in_reply_to = Some(value); in_reply_to = Some(value);
} else if name.eq_ignore_ascii_case(b"date") { } else if name.eq_ignore_ascii_case(b"date") {
let parse_result = parser::phrase(value); let parse_result = parser::phrase(value);
if parse_result.is_done() { if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.to_full_result().unwrap();
self.set_date(value.as_slice()); self.set_date(value.as_slice());
} else { } else {
self.set_date(value); self.set_date(value);
} }
} }
} }
/* /*
@ -400,24 +398,21 @@ impl Envelope {
Ok(()) Ok(())
} }
pub fn populate_headers_from_token(&mut self, mut operation: Box<BackendOp>) -> Result<()> { pub fn populate_headers_from_token(&mut self, mut operation: Box<BackendOp>) -> Result<()> {
{ let headers = operation.fetch_headers()?;
let headers = operation.fetch_headers()?; self.populate_headers(headers)
return self.populate_headers(headers);
}
} }
pub fn date(&self) -> u64 { pub fn date(&self) -> u64 {
self.timestamp self.timestamp
} }
pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> { pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
if let Some(d) = parser::date(&self.date.as_bytes()) { if let Some(d) = parser::date(&self.date.as_bytes()) {
return d; return d;
} }
chrono::FixedOffset::west(0) chrono::FixedOffset::west(0)
.ymd(1970, 1, 1) .ymd(1970, 1, 1)
.and_hms(0, 0, 0) .and_hms(0, 0, 0)
} }
pub fn date_as_str(&self) -> &str { pub fn date_as_str(&self) -> &str {
&self.date &self.date
@ -426,14 +421,14 @@ impl Envelope {
&self.from &self.from
} }
pub fn from_to_string(&self) -> String { pub fn field_from_to_string(&self) -> String {
let _strings: Vec<String> = self.from.iter().map(|a| format!("{}", a)).collect(); let _strings: Vec<String> = self.from.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ") _strings.join(", ")
} }
pub fn to(&self) -> &Vec<Address> { pub fn to(&self) -> &Vec<Address> {
&self.to &self.to
} }
pub fn to_to_string(&self) -> String { pub fn field_to_to_string(&self) -> String {
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect(); let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ") _strings.join(", ")
} }
@ -504,7 +499,7 @@ impl Envelope {
} }
pub fn in_reply_to_raw(&self) -> Cow<str> { pub fn in_reply_to_raw(&self) -> Cow<str> {
match self.in_reply_to { match self.in_reply_to {
Some(ref s) => String::from_utf8_lossy(s.raw()).into(), Some(ref s) => String::from_utf8_lossy(s.raw()),
_ => Cow::from(String::new()), _ => Cow::from(String::new()),
} }
} }

View File

@ -114,12 +114,11 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> { fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
let input_len = input.len(); let input_len = input.len();
for (i, x) in input.iter().enumerate() { for (i, x) in input.iter().enumerate() {
if *x == b'\n' { if *x == b'\n'
if ((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t') && (((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t')
|| i + 1 == input_len || i + 1 == input_len)
{ {
return IResult::Done(&input[(i + 1)..], &input[0..i]); return IResult::Done(&input[(i + 1)..], &input[0..i]);
}
} }
} }
IResult::Incomplete(Needed::Unknown) IResult::Incomplete(Needed::Unknown)
@ -146,7 +145,7 @@ pub fn headers_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
return IResult::Done(&input[(i + 1)..], &input[0..i + 1]); return IResult::Done(&input[(i + 1)..], &input[0..i + 1]);
} }
} }
return IResult::Error(error_code!(ErrorKind::Custom(43))); IResult::Error(error_code!(ErrorKind::Custom(43)))
} }
named!(pub body_raw<&[u8]>, named!(pub body_raw<&[u8]>,
@ -337,9 +336,9 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Done( IResult::Done(
rest, rest,
Address::Mailbox(MailboxAddress { Address::Mailbox(MailboxAddress {
raw: raw, raw,
display_name: display_name, display_name,
address_spec: address_spec, address_spec,
}), }),
) )
} }
@ -414,12 +413,11 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
} }
match mailbox_list(&input[dlength..]) { match mailbox_list(&input[dlength..]) {
IResult::Error(e) => { IResult::Error(e) => IResult::Error(e),
return IResult::Error(e);
}
IResult::Done(rest, vec) => { IResult::Done(rest, vec) => {
let size: usize = (rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize); let size: usize =
return IResult::Done( (rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize);
IResult::Done(
rest, rest,
Address::Group(GroupAddress { Address::Group(GroupAddress {
raw: input[0..size].into(), raw: input[0..size].into(),
@ -429,11 +427,9 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
}, },
mailbox_list: vec, mailbox_list: vec,
}), }),
); )
}
IResult::Incomplete(i) => {
return IResult::Incomplete(i);
} }
IResult::Incomplete(i) => IResult::Incomplete(i),
} }
} }
@ -559,7 +555,7 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<
return IResult::Error(error_code!(ErrorKind::Custom(43))); return IResult::Error(error_code!(ErrorKind::Custom(43)));
} }
} }
return IResult::Done(input, ret); IResult::Done(input, ret)
} }
named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >, named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
@ -603,13 +599,11 @@ named!(
acc += x.len(); acc += x.len();
acc acc
}); });
let bytes = list list.iter()
.iter()
.fold(Vec::with_capacity(list_len), |mut acc, x| { .fold(Vec::with_capacity(list_len), |mut acc, x| {
acc.append(&mut x.clone()); acc.append(&mut x.clone());
acc acc
}); })
bytes
}) })
)) ))
); );
@ -685,7 +679,7 @@ pub fn phrase(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
acc.push(b' '); acc.push(b' ');
} }
} }
return IResult::Done(&[], acc); IResult::Done(&[], acc)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -32,7 +32,7 @@ pub mod backends;
use error::Result; use error::Result;
use mailbox::backends::{folder_default, Folder}; use mailbox::backends::{folder_default, Folder};
pub mod thread; pub mod thread;
pub use mailbox::thread::{build_threads, Container, Threads, SortOrder, SortField}; pub use mailbox::thread::{build_threads, Container, SortField, SortOrder, Threads};
use std::option::Option; use std::option::Option;
@ -74,10 +74,13 @@ impl Mailbox {
let threads = build_threads(&mut collection, sent_folder); let threads = build_threads(&mut collection, sent_folder);
Ok(Mailbox { Ok(Mailbox {
folder: (*folder).clone(), folder: (*folder).clone(),
collection: collection, collection,
threads: threads, threads,
}) })
} }
pub fn is_empty(&self) -> bool {
self.collection.is_empty()
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.collection.len() self.collection.len()
} }

View File

@ -30,11 +30,11 @@ use mailbox::Mailbox;
extern crate fnv; extern crate fnv;
use self::fnv::FnvHashMap; use self::fnv::FnvHashMap;
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::{Index, };
use std::str::FromStr;
use std::result::Result as StdResult;
use std::cell::{Ref, RefCell}; use std::cell::{Ref, RefCell};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ops::Index;
use std::result::Result as StdResult;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Copy)] #[derive(Debug, Clone, PartialEq, Copy)]
pub enum SortOrder { pub enum SortOrder {
@ -122,7 +122,6 @@ impl ContainerTree {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Threads { pub struct Threads {
containers: Vec<Container>, containers: Vec<Container>,
@ -136,10 +135,10 @@ pub struct Threads {
pub struct ThreadIterator<'a> { pub struct ThreadIterator<'a> {
pos: usize, pos: usize,
stack: Vec<usize>, stack: Vec<usize>,
tree: Ref<'a ,Vec<ContainerTree>>, tree: Ref<'a, Vec<ContainerTree>>,
} }
impl<'a> Iterator for ThreadIterator<'a> { impl<'a> Iterator for ThreadIterator<'a> {
type Item = usize; type Item = usize;
fn next(&mut self) -> Option<usize> { fn next(&mut self) -> Option<usize> {
{ {
let mut tree = &(*self.tree); let mut tree = &(*self.tree);
@ -163,7 +162,7 @@ impl<'a> Iterator for ThreadIterator<'a> {
return Some(ret); return Some(ret);
} }
} }
return self.next(); self.next()
} }
} }
@ -172,25 +171,28 @@ impl<'a> IntoIterator for &'a Threads {
type IntoIter = ThreadIterator<'a>; type IntoIter = ThreadIterator<'a>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
ThreadIterator { pos: 0, stack: Vec::new(), tree: self.tree.borrow() } ThreadIterator {
pos: 0,
stack: Vec::new(),
tree: self.tree.borrow(),
}
} }
} }
pub struct RootIterator<'a> { pub struct RootIterator<'a> {
pos: usize, pos: usize,
tree: Ref<'a ,Vec<ContainerTree>>, tree: Ref<'a, Vec<ContainerTree>>,
} }
impl<'a> Iterator for RootIterator<'a> { impl<'a> Iterator for RootIterator<'a> {
type Item = (usize, usize, bool); type Item = (usize, usize, bool);
fn next(&mut self) -> Option<(usize, usize, bool)> { fn next(&mut self) -> Option<(usize, usize, bool)> {
if self.pos == self.tree.len() { if self.pos == self.tree.len() {
return None; return None;
} }
let node = &self.tree[self.pos]; let node = &self.tree[self.pos];
self.pos += 1; self.pos += 1;
return Some((node.id, node.len, node.has_unseen)); Some((node.id, node.len, node.has_unseen))
} }
} }
@ -201,8 +203,11 @@ impl Threads {
pub fn root_set(&self) -> &Vec<usize> { pub fn root_set(&self) -> &Vec<usize> {
&self.root_set &self.root_set
} }
pub fn root_set_iter(&self) -> RootIterator { pub fn root_set_iter(&self) -> RootIterator {
RootIterator { pos: 0, tree: self.tree.borrow() } RootIterator {
pos: 0,
tree: self.tree.borrow(),
}
} }
pub fn thread_to_mail(&self, i: usize) -> usize { pub fn thread_to_mail(&self, i: usize) -> usize {
let thread = self.containers[self.threaded_collection[i]]; let thread = self.containers[self.threaded_collection[i]];
@ -219,8 +224,8 @@ impl Threads {
let tree = &mut self.tree.borrow_mut(); let tree = &mut self.tree.borrow_mut();
let containers = &self.containers; let containers = &self.containers;
for mut t in tree.iter_mut() { for mut t in tree.iter_mut() {
if let Some(ref mut c ) = t.children { if let Some(ref mut c) = t.children {
c.sort_by(|a, b| { match subsort { c.sort_by(|a, b| match subsort {
(SortField::Date, SortOrder::Desc) => { (SortField::Date, SortOrder::Desc) => {
let a = containers[a.id]; let a = containers[a.id];
let b = containers[b.id]; let b = containers[b.id];
@ -253,7 +258,6 @@ impl Threads {
let mb = &collection[b.unwrap()]; let mb = &collection[b.unwrap()];
mb.subject().cmp(&ma.subject()) mb.subject().cmp(&ma.subject())
} }
}
}); });
} }
} }
@ -262,12 +266,11 @@ impl Threads {
fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) { fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) {
let tree = &mut self.tree.borrow_mut(); let tree = &mut self.tree.borrow_mut();
let containers = &self.containers; let containers = &self.containers;
tree.sort_by(|a, b| { match sort { tree.sort_by(|a, b| match sort {
(SortField::Date, SortOrder::Desc) => { (SortField::Date, SortOrder::Desc) => {
let a = containers[a.id]; let a = containers[a.id];
let b = containers[b.id]; let b = containers[b.id];
b.date.cmp(&a.date) b.date.cmp(&a.date)
} }
(SortField::Date, SortOrder::Asc) => { (SortField::Date, SortOrder::Asc) => {
let a = containers[a.id]; let a = containers[a.id];
@ -296,10 +299,14 @@ impl Threads {
let mb = &collection[b.unwrap()]; let mb = &collection[b.unwrap()];
mb.subject().cmp(&ma.subject()) mb.subject().cmp(&ma.subject())
} }
}
}); });
} }
pub fn sort_by(&self, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), collection: &[Envelope]) { pub fn sort_by(
&self,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
collection: &[Envelope],
) {
if *self.sort.borrow() != sort { if *self.sort.borrow() != sort {
self.inner_sort_by(sort, collection); self.inner_sort_by(sort, collection);
*self.sort.borrow_mut() = sort; *self.sort.borrow_mut() = sort;
@ -308,7 +315,6 @@ impl Threads {
self.inner_subsort_by(subsort, collection); self.inner_subsort_by(subsort, collection);
*self.subsort.borrow_mut() = subsort; *self.subsort.borrow_mut() = subsort;
} }
} }
pub fn build_collection(&mut self, collection: &[Envelope]) { pub fn build_collection(&mut self, collection: &[Envelope]) {
@ -320,7 +326,7 @@ impl Threads {
i: usize, i: usize,
root_subject_idx: usize, root_subject_idx: usize,
collection: &[Envelope], collection: &[Envelope],
) { ) {
let thread = containers[i]; let thread = containers[i];
if let Some(msg_idx) = containers[root_subject_idx].message() { if let Some(msg_idx) = containers[root_subject_idx].message() {
let root_subject = collection[msg_idx].subject(); let root_subject = collection[msg_idx].subject();
@ -333,9 +339,9 @@ impl Threads {
if subject == root_subject if subject == root_subject
|| subject.starts_with("Re: ") || subject.starts_with("Re: ")
&& subject.as_ref().ends_with(root_subject.as_ref()) && subject.as_ref().ends_with(root_subject.as_ref())
{ {
containers[i].set_show_subject(false); containers[i].set_show_subject(false);
} }
} }
} }
if thread.has_parent() && !containers[thread.parent().unwrap()].has_message() { if thread.has_parent() && !containers[thread.parent().unwrap()].has_message() {
@ -360,7 +366,15 @@ impl Threads {
loop { loop {
let mut new_child_tree = ContainerTree::new(fc); let mut new_child_tree = ContainerTree::new(fc);
build_threaded(&mut new_child_tree, containers, indentation, threaded, fc, i, collection); build_threaded(
&mut new_child_tree,
containers,
indentation,
threaded,
fc,
i,
collection,
);
tree.has_unseen |= new_child_tree.has_unseen; tree.has_unseen |= new_child_tree.has_unseen;
child_vec.push(new_child_tree); child_vec.push(new_child_tree);
let thread_ = containers[fc]; let thread_ = containers[fc];
@ -384,7 +398,7 @@ impl Threads {
*i, *i,
*i, *i,
collection, collection,
); );
tree.push(tree_node); tree.push(tree_node);
} }
self.tree.replace(tree); self.tree.replace(tree);
@ -397,11 +411,12 @@ impl Index<usize> for Threads {
type Output = Container; type Output = Container;
fn index(&self, index: usize) -> &Container { fn index(&self, index: usize) -> &Container {
self.containers.get(index).expect("thread index out of bounds") self.containers
.get(index)
.expect("thread index out of bounds")
} }
} }
impl Container { impl Container {
pub fn date(&self) -> UnixTimestamp { pub fn date(&self) -> UnixTimestamp {
self.date self.date
@ -474,7 +489,7 @@ fn build_collection(
threads: &mut Vec<Container>, threads: &mut Vec<Container>,
id_table: &mut FnvHashMap<Cow<str>, usize>, id_table: &mut FnvHashMap<Cow<str>, usize>,
collection: &mut [Envelope], collection: &mut [Envelope],
) -> () { ) -> () {
for (i, x) in collection.iter_mut().enumerate() { for (i, x) in collection.iter_mut().enumerate() {
let x_index; /* x's index in threads */ let x_index; /* x's index in threads */
let m_id = x.message_id_raw().into_owned(); let m_id = x.message_id_raw().into_owned();
@ -529,7 +544,7 @@ fn build_collection(
let parent_id = if id_table.contains_key(&r) { let parent_id = if id_table.contains_key(&r) {
let p = id_table[r.as_ref()]; let p = id_table[r.as_ref()];
if !(threads[p].is_descendant(threads, &threads[curr_ref]) if !(threads[p].is_descendant(threads, &threads[curr_ref])
|| threads[curr_ref].is_descendant(threads, &threads[p])) || threads[curr_ref].is_descendant(threads, &threads[p]))
{ {
threads[curr_ref].parent = Some(p); threads[curr_ref].parent = Some(p);
if threads[p].first_child.is_none() { if threads[p].first_child.is_none() {
@ -589,7 +604,7 @@ fn build_collection(
pub fn build_threads( pub fn build_threads(
collection: &mut Vec<Envelope>, collection: &mut Vec<Envelope>,
sent_folder: &Option<Result<Mailbox>>, sent_folder: &Option<Result<Mailbox>>,
) -> Threads { ) -> Threads {
/* To reconstruct thread information from the mails we need: */ /* To reconstruct thread information from the mails we need: */
/* a vector to hold thread members */ /* a vector to hold thread members */
@ -623,77 +638,77 @@ pub fn build_threads(
if id_table.contains_key(&m_id) if id_table.contains_key(&m_id)
|| (!x.in_reply_to_raw().is_empty() || (!x.in_reply_to_raw().is_empty()
&& id_table.contains_key(&x.in_reply_to_raw())) && id_table.contains_key(&x.in_reply_to_raw()))
{
let mut x: Envelope = (*x).clone();
if id_table.contains_key(&m_id) {
let c = id_table[&m_id];
if threads[c].message.is_some() {
/* skip duplicate message-id, but this should be handled instead */
continue;
}
threads[c].message = Some(idx);
assert!(threads[c].has_children());
threads[c].date = x.date();
x.set_thread(c);
} else if !x.in_reply_to_raw().is_empty()
&& id_table.contains_key(&x.in_reply_to_raw())
{ {
let mut x: Envelope = (*x).clone(); let p = id_table[&x_r_id];
if id_table.contains_key(&m_id) { let c = if id_table.contains_key(&m_id) {
let c = id_table[&m_id]; id_table[&m_id]
if threads[c].message.is_some() { } else {
/* skip duplicate message-id, but this should be handled instead */ threads.push(Container {
continue; message: Some(idx),
id: tidx,
parent: Some(p),
first_child: None,
next_sibling: None,
date: x.date(),
indentation: 0,
show_subject: true,
});
id_table.insert(Cow::from(m_id.into_owned()), tidx);
x.set_thread(tidx);
tidx += 1;
tidx - 1
};
threads[c].parent = Some(p);
if threads[p].is_descendant(&threads, &threads[c])
|| threads[c].is_descendant(&threads, &threads[p])
{
continue;
}
if threads[p].first_child.is_none() {
threads[p].first_child = Some(c);
} else {
let mut fc = threads[p].first_child.unwrap();
while threads[fc].next_sibling.is_some() {
threads[fc].parent = Some(p);
fc = threads[fc].next_sibling.unwrap();
} }
threads[c].message = Some(idx); threads[fc].next_sibling = Some(c);
assert!(threads[c].has_children()); threads[fc].parent = Some(p);
threads[c].date = x.date(); }
x.set_thread(c); /* update thread date */
} else if !x.in_reply_to_raw().is_empty() let mut parent_iter = p;
&& id_table.contains_key(&x.in_reply_to_raw()) 'date: loop {
{ let p = &mut threads[parent_iter];
let p = id_table[&x_r_id]; if p.date < x.date() {
let c = if id_table.contains_key(&m_id) { p.date = x.date();
id_table[&m_id] }
} else { match p.parent {
threads.push(Container { Some(p) => {
message: Some(idx), parent_iter = p;
id: tidx,
parent: Some(p),
first_child: None,
next_sibling: None,
date: x.date(),
indentation: 0,
show_subject: true,
});
id_table.insert(Cow::from(m_id.into_owned()), tidx);
x.set_thread(tidx);
tidx += 1;
tidx - 1
};
threads[c].parent = Some(p);
if threads[p].is_descendant(&threads, &threads[c])
|| threads[c].is_descendant(&threads, &threads[p])
{
continue;
}
if threads[p].first_child.is_none() {
threads[p].first_child = Some(c);
} else {
let mut fc = threads[p].first_child.unwrap();
while threads[fc].next_sibling.is_some() {
threads[fc].parent = Some(p);
fc = threads[fc].next_sibling.unwrap();
}
threads[fc].next_sibling = Some(c);
threads[fc].parent = Some(p);
} }
/* update thread date */ None => {
let mut parent_iter = p; break 'date;
'date: loop {
let p = &mut threads[parent_iter];
if p.date < x.date() {
p.date = x.date();
}
match p.parent {
Some(p) => {
parent_iter = p;
}
None => {
break 'date;
}
}
} }
} }
collection.push(x); }
idx += 1;
} }
collection.push(x);
idx += 1;
}
} }
} }
} }
@ -704,14 +719,14 @@ pub fn build_threads(
if threads[*v].parent.is_none() { if threads[*v].parent.is_none() {
if !threads[*v].has_message() if !threads[*v].has_message()
&& threads[*v].has_children() && threads[*v].has_children()
&& !threads[threads[*v].first_child.unwrap()].has_sibling() && !threads[threads[*v].first_child.unwrap()].has_sibling()
{ {
/* Do not promote the children if doing so would promote them to the root set /* Do not promote the children if doing so would promote them to the root set
* -- unless there is only one child, in which case, do. */ * -- unless there is only one child, in which case, do. */
root_set.push(threads[*v].first_child.unwrap()); root_set.push(threads[*v].first_child.unwrap());
continue 'root_set; continue 'root_set;
} }
root_set.push(*v); root_set.push(*v);
} }
} }

3
scripts/pre-commit 100644 → 100755
View File

@ -1,7 +1,8 @@
#!/bin/zsh #!/bin/zsh
# #
exec find . -name "*rs" -exec rustfmt {} \; exec git diff --name-only HEAD | grep ".*\.rs" | xargs rustfmt
#exec find . -name "*rs" -exec rustfmt {} \;
exec cargo +nightly clippy exec cargo +nightly clippy
# If there are whitespace errors, print the offending file names and fail. # If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against -- exec git diff-index --check --cached $against --

View File

@ -35,7 +35,6 @@ extern crate ui;
pub use melib::*; pub use melib::*;
pub use ui::*; pub use ui::*;
#[macro_use] #[macro_use]
extern crate chan; extern crate chan;
extern crate chan_signal; extern crate chan_signal;
@ -56,7 +55,6 @@ fn main() {
/* Catch SIGWINCH to handle terminal resizing */ /* Catch SIGWINCH to handle terminal resizing */
let signal = chan_signal::notify(&[Signal::WINCH]); let signal = chan_signal::notify(&[Signal::WINCH]);
/* Create the application State. This is the 'System' part of an ECS architecture */ /* Create the application State. This is the 'System' part of an ECS architecture */
let mut state = State::new(); let mut state = State::new();

View File

@ -65,7 +65,7 @@ impl Component for Composer {
let width = width!(area); let width = width!(area);
let mid = if width > 80 { let mid = if width > 80 {
let width = width - 80; let width = width - 80;
let mid = width / 2;; let mid = width / 2;
if self.dirty { if self.dirty {
for i in get_y(upper_left)..=get_y(bottom_right) { for i in get_y(upper_left)..=get_y(bottom_right) {
@ -78,17 +78,22 @@ impl Component for Composer {
} }
} }
mid mid
} else { 0 }; } else {
0
};
if self.dirty { if self.dirty {
for i in get_x(upper_left)+ mid + 1..=get_x(upper_left) + mid + 79 { for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + 79 {
grid[(i, header_height)].set_ch(HORZ_BOUNDARY); grid[(i, header_height)].set_ch(HORZ_BOUNDARY);
grid[(i, header_height)].set_fg(Color::Default); grid[(i, header_height)].set_fg(Color::Default);
grid[(i, header_height)].set_bg(Color::Default); grid[(i, header_height)].set_bg(Color::Default);
} }
} }
let body_area = ((mid + 1, header_height+2), (mid + 78, get_y(bottom_right))); let body_area = (
(mid + 1, header_height + 2),
(mid + 78, get_y(bottom_right)),
);
if self.dirty { if self.dirty {
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
@ -97,8 +102,7 @@ impl Component for Composer {
match self.mode { match self.mode {
ViewMode::Overview => { ViewMode::Overview => {
self.pager.draw(grid, body_area, context); self.pager.draw(grid, body_area, context);
}
},
} }
} }
@ -134,12 +138,12 @@ impl Component for Composer {
.expect("failed to execute process"); .expect("failed to execute process");
let result = f.read_to_string(); let result = f.read_to_string();
self.buffer = result.clone(); self.buffer = result.clone();
self.pager.update_from_string(result); self.pager.update_from_str(result.as_str());
context.restore_input(); context.restore_input();
self.dirty = true; self.dirty = true;
return true; return true;
}, }
_ => {}, _ => {}
} }
false false
} }

View File

@ -67,14 +67,14 @@ impl CompactListing {
&CompactListing::format_date(e), &CompactListing::format_date(e),
e.subject(), e.subject(),
len len
) )
} else { } else {
format!( format!(
"{} {} {:.85}", "{} {} {:.85}",
idx, idx,
&CompactListing::format_date(e), &CompactListing::format_date(e),
e.subject(), e.subject(),
) )
} }
} }
@ -86,7 +86,7 @@ impl CompactListing {
length: 0, length: 0,
sort: (Default::default(), Default::default()), sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()), subsort: (Default::default(), Default::default()),
content: content, content,
dirty: true, dirty: true,
unfocused: false, unfocused: false,
view: None, view: None,
@ -110,7 +110,10 @@ impl CompactListing {
// //
loop { loop {
// TODO: Show progress visually // TODO: Show progress visually
if let Ok(_) = context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { if context.accounts[self.cursor_pos.0]
.status(self.cursor_pos.1)
.is_ok()
{
break; break;
} }
} }
@ -118,7 +121,6 @@ impl CompactListing {
.as_ref() .as_ref()
.unwrap(); .unwrap();
self.length = mailbox.threads.root_len(); self.length = mailbox.threads.root_len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
if self.length == 0 { if self.length == 0 {
@ -139,9 +141,9 @@ impl CompactListing {
let i = if let Some(i) = container.message() { let i = if let Some(i) = container.message() {
i i
} else { } else {
threads.containers()[ threads.containers()[container.first_child().unwrap()]
container.first_child().unwrap() .message()
].message().unwrap() .unwrap()
}; };
let root_envelope: &Envelope = &mailbox.collection[i]; let root_envelope: &Envelope = &mailbox.collection[i];
let fg_color = if has_unseen { let fg_color = if has_unseen {
@ -163,16 +165,13 @@ impl CompactListing {
bg_color, bg_color,
((0, idx), (MAX_COLS - 1, idx)), ((0, idx), (MAX_COLS - 1, idx)),
false, false,
); );
for x in x..MAX_COLS { for x in x..MAX_COLS {
self.content[(x, idx)].set_ch(' '); self.content[(x, idx)].set_ch(' ');
self.content[(x, idx)].set_bg(bg_color); self.content[(x, idx)].set_bg(bg_color);
} }
} }
} }
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) { fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
@ -185,9 +184,9 @@ impl CompactListing {
let i = if let Some(i) = container.message() { let i = if let Some(i) = container.message() {
i i
} else { } else {
threads.containers()[ threads.containers()[container.first_child().unwrap()]
container.first_child().unwrap() .message()
].message().unwrap() .unwrap()
}; };
let root_envelope: &Envelope = &mailbox.collection[i]; let root_envelope: &Envelope = &mailbox.collection[i];
let fg_color = if !root_envelope.is_seen() { let fg_color = if !root_envelope.is_seen() {
@ -303,11 +302,7 @@ impl Component for CompactListing {
return; return;
} }
self.view = Some(ThreadView::new(self.cursor_pos, context)); self.view = Some(ThreadView::new(self.cursor_pos, context));
self.view.as_mut().unwrap().draw( self.view.as_mut().unwrap().draw(grid, area, context);
grid,
area,
context,
);
self.dirty = false; self.dirty = false;
} }
} }
@ -440,8 +435,7 @@ impl Component for CompactListing {
self.dirty = true; self.dirty = true;
self.refresh_mailbox(context); self.refresh_mailbox(context);
return true; return true;
} } // _ => {}
// _ => {}
}, },
_ => {} _ => {}
} }
@ -451,7 +445,9 @@ impl Component for CompactListing {
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false) self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
} }
fn set_dirty(&mut self) { fn set_dirty(&mut self) {
self.view.as_mut().map(|p| p.set_dirty()); if let Some(p) = self.view.as_mut() {
p.set_dirty();
}
self.dirty = true; self.dirty = true;
} }
} }

View File

@ -79,7 +79,7 @@ impl MailListing {
local_collection: Vec::new(), local_collection: Vec::new(),
sort: (Default::default(), Default::default()), sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()), subsort: (Default::default(), Default::default()),
content: content, content,
dirty: true, dirty: true,
unfocused: false, unfocused: false,
view: None, view: None,
@ -107,7 +107,10 @@ impl MailListing {
// //
loop { loop {
// TODO: Show progress visually // TODO: Show progress visually
if let Ok(_) = context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { if context.accounts[self.cursor_pos.0]
.status(self.cursor_pos.1)
.is_ok()
{
break; break;
} }
} }
@ -162,7 +165,6 @@ impl MailListing {
continue; continue;
} }
match iter.peek() { match iter.peek() {
Some(&x) if threads[x].indentation() == indentation => { Some(&x) if threads[x].indentation() == indentation => {
indentations.pop(); indentations.pop();
@ -198,7 +200,7 @@ impl MailListing {
container, container,
&indentations, &indentations,
len, len,
// context.accounts[self.cursor_pos.0].backend.operation(envelope.hash()) // context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
), ),
&mut self.content, &mut self.content,
fg_color, fg_color,
@ -522,9 +524,7 @@ impl Component for MailListing {
.threaded(); .threaded();
let account = &mut context.accounts[self.cursor_pos.0]; let account = &mut context.accounts[self.cursor_pos.0];
let (hash, is_seen) = { let (hash, is_seen) = {
let mailbox = &mut account[self.cursor_pos.1] let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
.as_mut()
.unwrap();
let envelope: &mut Envelope = if threaded { let envelope: &mut Envelope = if threaded {
let i = mailbox.threaded_mail(idx); let i = mailbox.threaded_mail(idx);
&mut mailbox.collection[i] &mut mailbox.collection[i]
@ -538,9 +538,7 @@ impl Component for MailListing {
let backend = &account.backend; let backend = &account.backend;
backend.operation(hash) backend.operation(hash)
}; };
let mailbox = &mut account[self.cursor_pos.1] let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
.as_mut()
.unwrap();
let envelope: &mut Envelope = if threaded { let envelope: &mut Envelope = if threaded {
let i = mailbox.threaded_mail(idx); let i = mailbox.threaded_mail(idx);
&mut mailbox.collection[i] &mut mailbox.collection[i]
@ -598,9 +596,7 @@ impl Component for MailListing {
.conf() .conf()
.threaded(); .threaded();
let account = &context.accounts[self.cursor_pos.0]; let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1] let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
.as_ref()
.unwrap();
let mut coordinates = self.cursor_pos; let mut coordinates = self.cursor_pos;
coordinates.2 = if threaded { coordinates.2 = if threaded {
mailbox.threaded_mail(self.cursor_pos.2) mailbox.threaded_mail(self.cursor_pos.2)
@ -792,8 +788,7 @@ impl Component for MailListing {
self.dirty = true; self.dirty = true;
self.refresh_mailbox(context); self.refresh_mailbox(context);
return true; return true;
} } // _ => {}
// _ => {}
}, },
_ => {} _ => {}
} }
@ -803,7 +798,9 @@ impl Component for MailListing {
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false) self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
} }
fn set_dirty(&mut self) { fn set_dirty(&mut self) {
self.view.as_mut().map(|p| p.set_dirty()); if let Some(p) = self.view.as_mut() {
p.set_dirty();
};
self.dirty = true; self.dirty = true;
} }
} }

View File

@ -65,10 +65,8 @@ impl AccountMenu {
index: i, index: i,
entries: { entries: {
let mut entries = Vec::with_capacity(a.len()); let mut entries = Vec::with_capacity(a.len());
let mut idx = 0; for (idx, acc) in a.list_folders().into_iter().enumerate() {
for acc in a.list_folders() {
entries.push((idx, acc)); entries.push((idx, acc));
idx += 1;
} }
entries entries
}, },

View File

@ -68,7 +68,7 @@ impl EnvelopeView {
wrapper: EnvelopeWrapper, wrapper: EnvelopeWrapper,
pager: Option<Pager>, pager: Option<Pager>,
subview: Option<Box<Component>>, subview: Option<Box<Component>>,
) -> Self { ) -> Self {
EnvelopeView { EnvelopeView {
pager, pager,
subview, subview,
@ -81,33 +81,34 @@ impl EnvelopeView {
} }
/// Returns the string to be displayed in the Viewer /// Returns the string to be displayed in the Viewer
fn attachment_to_text(&self, body: Attachment) -> String { fn attachment_to_text(&self, body: &Attachment) -> String {
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let body_text = String::from_utf8_lossy(&decode_rec( let body_text = String::from_utf8_lossy(&decode_rec(
&body, &body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| { Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() { if a.content_type().is_text_html() {
use std::io::Write; use std::io::Write;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let mut html_filter = Command::new("w3m") let mut html_filter = Command::new("w3m")
.args(&["-I", "utf-8", "-T", "text/html"]) .args(&["-I", "utf-8", "-T", "text/html"])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.expect("Failed to start html filter process"); .expect("Failed to start html filter process");
html_filter html_filter
.stdin .stdin
.as_mut() .as_mut()
.unwrap() .unwrap()
.write_all(&v) .write_all(&v)
.expect("Failed to write to w3m stdin"); .expect("Failed to write to w3m stdin");
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n".to_vec(); *v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n"
v.extend(html_filter.wait_with_output().unwrap().stdout); .to_vec();
} v.extend(html_filter.wait_with_output().unwrap().stdout);
})), }
)).into_owned(); })),
)).into_owned();
match self.mode { match self.mode {
ViewMode::Normal | ViewMode::Subview => { ViewMode::Normal | ViewMode::Subview => {
let mut t = body_text.to_string(); let mut t = body_text.to_string();
@ -158,7 +159,7 @@ impl EnvelopeView {
} }
} }
} }
pub fn plain_text_to_buf(s: &String, highlight_urls: bool) -> CellBuffer { pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
let mut buf = CellBuffer::from(s); let mut buf = CellBuffer::from(s);
if highlight_urls { if highlight_urls {
@ -197,7 +198,7 @@ impl Component for EnvelopeView {
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area); let bottom_right = bottom_right!(area);
let y :usize = { let y: usize = {
let envelope: &Envelope = &self.wrapper; let envelope: &Envelope = &self.wrapper;
if self.mode == ViewMode::Raw { if self.mode == ViewMode::Raw {
@ -219,7 +220,7 @@ impl Component for EnvelopeView {
grid[(x, y)].set_fg(Color::Default); grid[(x, y)].set_fg(Color::Default);
} }
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&format!("From: {}", envelope.from_to_string()), &format!("From: {}", envelope.field_from_to_string()),
grid, grid,
Color::Byte(33), Color::Byte(33),
Color::Default, Color::Default,
@ -232,7 +233,7 @@ impl Component for EnvelopeView {
grid[(x, y)].set_fg(Color::Default); grid[(x, y)].set_fg(Color::Default);
} }
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&format!("To: {}", envelope.to_to_string()), &format!("To: {}", envelope.field_to_to_string()),
grid, grid,
Color::Byte(33), Color::Byte(33),
Color::Default, Color::Default,
@ -293,7 +294,7 @@ impl Component for EnvelopeView {
} }
_ => { _ => {
let buf = { let buf = {
let text = self.attachment_to_text(body); let text = self.attachment_to_text(&body);
// URL indexes must be colored (ugh..) // URL indexes must be colored (ugh..)
EnvelopeView::plain_text_to_buf(&text, self.mode == ViewMode::Url) EnvelopeView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
}; };
@ -344,7 +345,9 @@ impl Component for EnvelopeView {
self.dirty = true; self.dirty = true;
return true; return true;
} }
UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() || self.mode == ViewMode::Subview => { UIEventType::Input(Key::Char('r'))
if self.mode.is_attachment() || self.mode == ViewMode::Subview =>
{
self.mode = ViewMode::Normal; self.mode = ViewMode::Normal;
self.subview.take(); self.subview.take();
self.dirty = true; self.dirty = true;
@ -358,12 +361,19 @@ impl Component for EnvelopeView {
{ {
let envelope: &Envelope = self.wrapper.envelope(); let envelope: &Envelope = self.wrapper.envelope();
if let Some(u) = envelope.body_bytes(self.wrapper.buffer()).attachments().get(lidx) { if let Some(u) = envelope
.body_bytes(self.wrapper.buffer())
.attachments()
.get(lidx)
{
match u.content_type() { match u.content_type() {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
self.subview = Some(Box::new(Pager::from_str(&String::from_utf8_lossy(&decode_rec(u, None)).to_string(), None))); self.subview = Some(Box::new(Pager::from_str(
}, &String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
None,
)));
}
ContentType::Text { .. } => { ContentType::Text { .. } => {
self.mode = ViewMode::Attachment(lidx); self.mode = ViewMode::Attachment(lidx);
@ -416,7 +426,7 @@ impl Component for EnvelopeView {
} }
}; };
return true; return true;
}, }
UIEventType::Input(Key::Char('g')) UIEventType::Input(Key::Char('g'))
if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url => if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url =>
{ {
@ -425,7 +435,10 @@ impl Component for EnvelopeView {
let url = { let url = {
let envelope: &Envelope = self.wrapper.envelope(); let envelope: &Envelope = self.wrapper.envelope();
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let mut t = envelope.body_bytes(self.wrapper.buffer()).text().to_string(); let mut t = envelope
.body_bytes(self.wrapper.buffer())
.text()
.to_string();
let links: Vec<Link> = finder.links(&t).collect(); let links: Vec<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) { if let Some(u) = links.get(lidx) {
u.as_str().to_string() u.as_str().to_string()

View File

@ -70,31 +70,28 @@ impl Component for HtmlView {
if self.pager.process_event(event, context) { if self.pager.process_event(event, context) {
return true; return true;
} }
match event.event_type { if let UIEventType::Input(Key::Char('v')) = event.event_type {
UIEventType::Input(Key::Char('v')) => { // TODO: Optional filter that removes outgoing resource requests (images and
// TODO: Optional filter that removes outgoing resource requests (images and // scripts)
// scripts) let binary = query_default_app("text/html");
let binary = query_default_app("text/html"); if let Ok(binary) = binary {
if let Ok(binary) = binary { let mut p = create_temp_file(&self.bytes, None);
let mut p = create_temp_file(&self.bytes, None); Command::new(&binary)
Command::new(&binary) .arg(p.path())
.arg(p.path()) .stdin(Stdio::piped())
.stdin(Stdio::piped()) .stdout(Stdio::piped())
.stdout(Stdio::piped()) .spawn()
.spawn() .unwrap_or_else(|_| panic!("Failed to start {}", binary.display()));
.unwrap_or_else(|_| panic!("Failed to start {}", binary.display())); context.temp_files.push(p);
context.temp_files.push(p); } else {
} else { context.replies.push_back(UIEvent {
context.replies.push_back(UIEvent { id: 0,
id: 0, event_type: UIEventType::StatusNotification(
event_type: UIEventType::StatusNotification(format!( "Couldn't find a default application for html files.".to_string(),
"Couldn't find a default application for html files." ),
)), });
});
}
return true;
} }
_ => {} return true;
} }
false false
} }

View File

@ -82,7 +82,7 @@ impl MailView {
coordinates: (usize, usize, usize), coordinates: (usize, usize, usize),
pager: Option<Pager>, pager: Option<Pager>,
subview: Option<Box<Component>>, subview: Option<Box<Component>>,
) -> Self { ) -> Self {
MailView { MailView {
coordinates, coordinates,
pager, pager,
@ -95,33 +95,34 @@ impl MailView {
} }
/// Returns the string to be displayed in the Viewer /// Returns the string to be displayed in the Viewer
fn attachment_to_text(&self, body: Attachment) -> String { fn attachment_to_text(&self, body: &Attachment) -> String {
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let body_text = String::from_utf8_lossy(&decode_rec( let body_text = String::from_utf8_lossy(&decode_rec(
&body, &body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| { Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() { if a.content_type().is_text_html() {
use std::io::Write; use std::io::Write;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let mut html_filter = Command::new("w3m") let mut html_filter = Command::new("w3m")
.args(&["-I", "utf-8", "-T", "text/html"]) .args(&["-I", "utf-8", "-T", "text/html"])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.expect("Failed to start html filter process"); .expect("Failed to start html filter process");
html_filter html_filter
.stdin .stdin
.as_mut() .as_mut()
.unwrap() .unwrap()
.write_all(&v) .write_all(&v)
.expect("Failed to write to w3m stdin"); .expect("Failed to write to w3m stdin");
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n".to_vec(); *v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n"
v.extend(html_filter.wait_with_output().unwrap().stdout); .to_vec();
} v.extend(html_filter.wait_with_output().unwrap().stdout);
})), }
)).into_owned(); })),
)).into_owned();
match self.mode { match self.mode {
ViewMode::Normal | ViewMode::Subview => { ViewMode::Normal | ViewMode::Subview => {
let mut t = body_text.to_string(); let mut t = body_text.to_string();
@ -172,7 +173,7 @@ impl MailView {
} }
} }
} }
pub fn plain_text_to_buf(s: &String, highlight_urls: bool) -> CellBuffer { pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
let mut buf = CellBuffer::from(s); let mut buf = CellBuffer::from(s);
if highlight_urls { if highlight_urls {
@ -237,7 +238,7 @@ impl Component for MailView {
grid[(x, y)].set_fg(Color::Default); grid[(x, y)].set_fg(Color::Default);
} }
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&format!("From: {}", envelope.from_to_string()), &format!("From: {}", envelope.field_from_to_string()),
grid, grid,
Color::Byte(33), Color::Byte(33),
Color::Default, Color::Default,
@ -250,7 +251,7 @@ impl Component for MailView {
grid[(x, y)].set_fg(Color::Default); grid[(x, y)].set_fg(Color::Default);
} }
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&format!("To: {}", envelope.to_to_string()), &format!("To: {}", envelope.field_to_to_string()),
grid, grid,
Color::Byte(33), Color::Byte(33),
Color::Default, Color::Default,
@ -302,7 +303,9 @@ impl Component for MailView {
.as_ref() .as_ref()
.unwrap(); .unwrap();
let envelope: &Envelope = &mailbox.collection[mailbox_idx.2]; let envelope: &Envelope = &mailbox.collection[mailbox_idx.2];
let op = context.accounts[mailbox_idx.0].backend.operation(envelope.hash()); let op = context.accounts[mailbox_idx.0]
.backend
.operation(envelope.hash());
let body = envelope.body(op); let body = envelope.body(op);
match self.mode { match self.mode {
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
@ -317,7 +320,7 @@ impl Component for MailView {
} }
_ => { _ => {
let buf = { let buf = {
let text = self.attachment_to_text(body); let text = self.attachment_to_text(&body);
// URL indexes must be colored (ugh..) // URL indexes must be colored (ugh..)
MailView::plain_text_to_buf(&text, self.mode == ViewMode::Url) MailView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
}; };
@ -365,7 +368,9 @@ impl Component for MailView {
}; };
self.dirty = true; self.dirty = true;
} }
UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() || self.mode == ViewMode::Subview => { UIEventType::Input(Key::Char('r'))
if self.mode.is_attachment() || self.mode == ViewMode::Subview =>
{
self.mode = ViewMode::Normal; self.mode = ViewMode::Normal;
self.subview.take(); self.subview.take();
self.dirty = true; self.dirty = true;
@ -383,26 +388,30 @@ impl Component for MailView {
.unwrap(); .unwrap();
let envelope: &Envelope = &mailbox.collection[self.coordinates.2]; let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash()); let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash());
if let Some(u) = envelope.body(op).attachments().get(lidx) { if let Some(u) = envelope.body(op).attachments().get(lidx) {
match u.content_type() { match u.content_type() {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
match EnvelopeWrapper::new(u.bytes().to_vec()) { match EnvelopeWrapper::new(u.bytes().to_vec()) {
Ok(wrapper) => { Ok(wrapper) => {
self.subview = Some(Box::new(EnvelopeView::new(wrapper, None, None))); self.subview =
}, Some(Box::new(EnvelopeView::new(wrapper, None, None)));
}
Err(e) => { Err(e) => {
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::StatusNotification( event_type: UIEventType::StatusNotification(format!(
format!("{}", e) "{}",
), e
)),
}); });
} }
} }
return true; return true;
}, }
ContentType::Text { .. } => { ContentType::Text { .. } => {
self.mode = ViewMode::Attachment(lidx); self.mode = ViewMode::Attachment(lidx);
@ -468,7 +477,9 @@ impl Component for MailView {
let envelope: &Envelope = &mailbox.collection[self.coordinates.2]; let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash()); let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash());
let mut t = envelope.body(op).text().to_string(); let mut t = envelope.body(op).text().to_string();
let links: Vec<Link> = finder.links(&t).collect(); let links: Vec<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) { if let Some(u) = links.get(lidx) {
@ -500,7 +511,9 @@ impl Component for MailView {
} }
self.dirty = true; self.dirty = true;
} }
_ => { return false; } _ => {
return false;
}
} }
true true
} }

View File

@ -31,7 +31,6 @@ struct ThreadEntry {
msg_idx: usize, msg_idx: usize,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ThreadView { pub struct ThreadView {
new_cursor_pos: usize, new_cursor_pos: usize,
@ -49,7 +48,9 @@ pub struct ThreadView {
impl ThreadView { impl ThreadView {
pub fn new(coordinates: (usize, usize, usize), context: &Context) -> Self { pub fn new(coordinates: (usize, usize, usize), context: &Context) -> Self {
let mut stack: Vec<(usize, usize)> = Vec::with_capacity(32); let mut stack: Vec<(usize, usize)> = Vec::with_capacity(32);
let mailbox = &context.accounts[coordinates.0][coordinates.1].as_ref().unwrap(); let mailbox = &context.accounts[coordinates.0][coordinates.1]
.as_ref()
.unwrap();
let threads = &mailbox.threads; let threads = &mailbox.threads;
let container = &threads.containers()[threads.root_set()[coordinates.2]]; let container = &threads.containers()[threads.root_set()[coordinates.2]];
@ -66,7 +67,7 @@ impl ThreadView {
entries: Vec::new(), entries: Vec::new(),
cursor_pos: 1, cursor_pos: 1,
new_cursor_pos: 0, new_cursor_pos: 0,
.. Default::default() ..Default::default()
}; };
let mut line = 0; let mut line = 0;
let mut max_ind = 0; let mut max_ind = 0;
@ -87,22 +88,28 @@ impl ThreadView {
view.new_expanded_pos = view.entries.len() - 1; view.new_expanded_pos = view.entries.len() - 1;
view.expanded_pos = view.new_expanded_pos + 1; view.expanded_pos = view.new_expanded_pos + 1;
let height = 2*view.entries.len(); let height = 2 * view.entries.len();
let mut width = 0; let mut width = 0;
let mut strings: Vec<String> = Vec::with_capacity(view.entries.len()); let mut strings: Vec<String> = Vec::with_capacity(view.entries.len());
for e in &view.entries { for e in &view.entries {
let envelope: &Envelope = &mailbox.collection[e.msg_idx]; let envelope: &Envelope = &mailbox.collection[e.msg_idx];
strings.push(format!(" {}{} - {}", " ".repeat(e.index.0 * 4), envelope.date_as_str(), envelope.from_to_string())); strings.push(format!(
width = cmp::max(width, e.index.0 + strings.last().as_ref().unwrap().len() + 1); " {}{} - {}",
" ".repeat(e.index.0 * 4),
envelope.date_as_str(),
envelope.field_from_to_string()
));
width = cmp::max(
width,
e.index.0 + strings.last().as_ref().unwrap().len() + 1,
);
} }
let mut content = CellBuffer::new(width, height, Cell::default()); let mut content = CellBuffer::new(width, height, Cell::default());
for (y, e) in view.entries.iter().enumerate() { for (y, e) in view.entries.iter().enumerate() {
if y > 0 && content.get_mut(e.index.0 * 4, 2*y - 1).is_some() { if y > 0 && content.get_mut(e.index.0 * 4, 2 * y - 1).is_some() {
let index = (e.index.0 * 4, 2*y - 1); let index = (e.index.0 * 4, 2 * y - 1);
if content[index].ch() == ' ' { if content[index].ch() == ' ' {
let mut ctr = 1; let mut ctr = 1;
while content[(e.index.0 * 4 + ctr, 2 * y - 1)].ch() == ' ' { while content[(e.index.0 * 4 + ctr, 2 * y - 1)].ch() == ' ' {
@ -120,14 +127,14 @@ impl ThreadView {
&mut content, &mut content,
Color::Default, Color::Default,
Color::Default, Color::Default,
((e.index.0 + 1, 2*y), (width - 1, height - 1)), ((e.index.0 + 1, 2 * y), (width - 1, height - 1)),
true, true,
); );
content[(e.index.0 * 4, 2*y)].set_ch(VERT_BOUNDARY); content[(e.index.0 * 4, 2 * y)].set_ch(VERT_BOUNDARY);
for i in (e.index.0 * 4)..width { for i in (e.index.0 * 4)..width {
content[(i, 2*y + 1)].set_ch(HORZ_BOUNDARY); content[(i, 2 * y + 1)].set_ch(HORZ_BOUNDARY);
} }
content[(e.index.0 *4, 2*y + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL); content[(e.index.0 * 4, 2 * y + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
} }
//view.mailview = MailView::new((view.coordinates.0, view.coordinates.1, view.entries[view.expanded_pos].msg_idx), None, None); //view.mailview = MailView::new((view.coordinates.0, view.coordinates.1, view.entries[view.expanded_pos].msg_idx), None, None);
view.content = content; view.content = content;
@ -144,23 +151,28 @@ impl ThreadView {
let msg_idx = if let Some(i) = container.message() { let msg_idx = if let Some(i) = container.message() {
i i
} else { } else {
mailbox.threads.containers()[ mailbox.threads.containers()[container.first_child().unwrap()]
container.first_child().unwrap() .message()
].message().unwrap() .unwrap()
}; };
let envelope: &Envelope = &mailbox.collection[msg_idx]; let envelope: &Envelope = &mailbox.collection[msg_idx];
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash()); let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash());
let body = envelope.body(op); let body = envelope.body(op);
let mut body_text: String = " \n".repeat(6); let mut body_text: String = " \n".repeat(6);
body_text.push_str(&String::from_utf8_lossy(&decode_rec(&body, None))); body_text.push_str(&String::from_utf8_lossy(&decode_rec(&body, None)));
let mut buf = CellBuffer::from(&body_text).split_newlines(); let mut buf = CellBuffer::from(body_text.as_str()).split_newlines();
let date = format!("Date: {}\n", envelope.date_as_str()); let date = format!("Date: {}\n", envelope.date_as_str());
let from = format!("From: {}\n", envelope.from_to_string()); let from = format!("From: {}\n", envelope.field_from_to_string());
let message_id = &format!("Message-ID: <{}>\n\n", envelope.message_id_raw()); let message_id = &format!("Message-ID: <{}>\n\n", envelope.message_id_raw());
let mut width = [date.len(), from.len(), message_id.len(), buf.size().0].iter().map(|&v| v).max().unwrap_or(1); let mut width = [date.len(), from.len(), message_id.len(), buf.size().0]
.iter()
.cloned()
.max()
.unwrap_or(1);
let height = buf.size().1; let height = buf.size().1;
if width > buf.size().0 { if width > buf.size().0 {
buf.resize(width, height, Cell::default()); buf.resize(width, height, Cell::default());
@ -176,7 +188,7 @@ impl ThreadView {
Color::Default, Color::Default,
((ind, 0), (width, height)), ((ind, 0), (width, height)),
true, true,
); );
write_string_to_grid( write_string_to_grid(
&from, &from,
&mut buf, &mut buf,
@ -184,7 +196,7 @@ impl ThreadView {
Color::Default, Color::Default,
((ind, 1), (width, height)), ((ind, 1), (width, height)),
true, true,
); );
write_string_to_grid( write_string_to_grid(
&message_id, &message_id,
&mut buf, &mut buf,
@ -192,7 +204,7 @@ impl ThreadView {
Color::Default, Color::Default,
((ind, 2), (width, height)), ((ind, 2), (width, height)),
true, true,
); );
ThreadEntry { ThreadEntry {
index: (ind, idx, order), index: (ind, idx, order),
@ -237,8 +249,8 @@ impl ThreadView {
self.cursor_pos = self.new_cursor_pos; self.cursor_pos = self.new_cursor_pos;
for &idx in &[old_cursor_pos, self.new_cursor_pos] { for &idx in &[old_cursor_pos, self.new_cursor_pos] {
let new_area = ( let new_area = (
set_y(upper_left, get_y(upper_left) + 2*(idx % rows)), set_y(upper_left, get_y(upper_left) + 2 * (idx % rows)),
set_y(bottom_right, get_y(upper_left) + 2*(idx % rows)), set_y(bottom_right, get_y(upper_left) + 2 * (idx % rows)),
); );
self.highlight_line(grid, new_area, idx); self.highlight_line(grid, new_area, idx);
context.dirty_areas.push_back(new_area); context.dirty_areas.push_back(new_area);
@ -253,13 +265,16 @@ impl ThreadView {
grid, grid,
&self.content, &self.content,
area, area,
((0, 2*top_idx), (width - 1, height - 1)), ((0, 2 * top_idx), (width - 1, height - 1)),
); );
self.highlight_line( self.highlight_line(
grid, grid,
( (
set_y(upper_left, get_y(upper_left) + 2*(self.cursor_pos % rows)), set_y(upper_left, get_y(upper_left) + 2 * (self.cursor_pos % rows)),
set_y(bottom_right, get_y(upper_left) + 2*(self.cursor_pos % rows)), set_y(
bottom_right,
get_y(upper_left) + 2 * (self.cursor_pos % rows),
),
), ),
self.cursor_pos, self.cursor_pos,
); );
@ -292,22 +307,25 @@ impl Component for ThreadView {
let mid = get_y(upper_left) + total_rows - bottom_entity_rows; let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
if !self.dirty { if !self.dirty {
self.mailview.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context); self.mailview
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
return; return;
} }
self.dirty = false; self.dirty = false;
let y = { let y = {
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1].as_ref().unwrap(); let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
.as_ref()
.unwrap();
let threads = &mailbox.threads; let threads = &mailbox.threads;
let container = &threads.containers()[threads.root_set()[self.coordinates.2]]; let container = &threads.containers()[threads.root_set()[self.coordinates.2]];
let i = if let Some(i) = container.message() { let i = if let Some(i) = container.message() {
i i
} else { } else {
threads.containers()[ threads.containers()[container.first_child().unwrap()]
container.first_child().unwrap() .message()
].message().unwrap() .unwrap()
}; };
let envelope: &Envelope = &mailbox.collection[i]; let envelope: &Envelope = &mailbox.collection[i];
@ -318,7 +336,7 @@ impl Component for ThreadView {
Color::Default, Color::Default,
area, area,
true, true,
); );
for x in x..=get_x(bottom_right) { for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' '); grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_bg(Color::Default);
@ -341,7 +359,7 @@ impl Component for ThreadView {
&self.content, &self.content,
(set_y(upper_left, y), set_y(bottom_right, mid - 1)), (set_y(upper_left, y), set_y(bottom_right, mid - 1)),
((0, 0), (width - 1, height - 1)), ((0, 0), (width - 1, height - 1)),
); );
for x in get_x(upper_left)..=get_x(bottom_right) { for x in get_x(upper_left)..=get_x(bottom_right) {
grid[(x, mid)].set_ch(HORZ_BOUNDARY); grid[(x, mid)].set_ch(HORZ_BOUNDARY);
} }
@ -356,10 +374,23 @@ impl Component for ThreadView {
if self.new_expanded_pos != self.expanded_pos { if self.new_expanded_pos != self.expanded_pos {
self.expanded_pos = self.new_expanded_pos; self.expanded_pos = self.new_expanded_pos;
self.mailview = MailView::new((self.coordinates.0, self.coordinates.1, self.entries[self.expanded_pos].msg_idx), None, None); self.mailview = MailView::new(
(
self.coordinates.0,
self.coordinates.1,
self.entries[self.expanded_pos].msg_idx,
),
None,
None,
);
} }
self.draw_list(grid, (set_y(upper_left, y), set_y(bottom_right, mid - 1)), context); self.draw_list(
self.mailview.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context); grid,
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
context,
);
self.mailview
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
} }
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
if self.mailview.process_event(event, context) { if self.mailview.process_event(event, context) {
@ -372,7 +403,7 @@ impl Component for ThreadView {
self.dirty = true; self.dirty = true;
} }
return true; return true;
}, }
UIEventType::Input(Key::Down) => { UIEventType::Input(Key::Down) => {
let height = self.entries.len(); let height = self.entries.len();
if height > 0 && self.cursor_pos + 1 < height { if height > 0 && self.cursor_pos + 1 < height {
@ -385,10 +416,10 @@ impl Component for ThreadView {
self.new_expanded_pos = self.cursor_pos; self.new_expanded_pos = self.cursor_pos;
self.dirty = true; self.dirty = true;
return true; return true;
}, }
UIEventType::Resize => { UIEventType::Resize => {
self.dirty = true; self.dirty = true;
}, }
_ => {} _ => {}
} }
false false

View File

@ -36,7 +36,7 @@ pub mod utilities;
pub use self::utilities::*; pub use self::utilities::*;
use std::fmt; use std::fmt;
use std::fmt::{Display, Debug}; use std::fmt::{Debug, Display};
use std::ops::Deref; use std::ops::Deref;
use super::{Key, UIEvent, UIEventType}; use super::{Key, UIEvent, UIEventType};
@ -76,7 +76,6 @@ impl Display for Entity {
} }
} }
impl Deref for Entity { impl Deref for Entity {
type Target = Box<Component>; type Target = Box<Component>;
@ -227,22 +226,22 @@ fn write_string_to_grid(
eprintln!(" Invalid area with string {} and area {:?}", s, area); eprintln!(" Invalid area with string {} and area {:?}", s, area);
return (x, y); return (x, y);
} }
'char: for c in s.chars() { 'char: for c in s.chars() {
grid[(x, y)].set_ch(c); grid[(x, y)].set_ch(c);
grid[(x, y)].set_fg(fg_color); grid[(x, y)].set_fg(fg_color);
grid[(x, y)].set_bg(bg_color); grid[(x, y)].set_bg(bg_color);
x += 1; x += 1;
if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) { if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) {
x = get_x(upper_left); x = get_x(upper_left);
y += 1; y += 1;
if y > (get_y(bottom_right)) || y > get_y(bounds) { if y > (get_y(bottom_right)) || y > get_y(bounds) {
return (x, y - 1); return (x, y - 1);
}
if !line_break {
break 'char;
}
} }
if !line_break {
break 'char;
}
}
} }
(x, y) (x, y)
} }

View File

@ -65,7 +65,9 @@ impl Component for HSplit {
for i in get_x(upper_left)..=get_x(bottom_right) { for i in get_x(upper_left)..=get_x(bottom_right) {
grid[(i, mid)].set_ch('─'); grid[(i, mid)].set_ch('─');
} }
context.dirty_areas.push_back(((get_x(upper_left), mid), (get_x(bottom_right), mid))); context
.dirty_areas
.push_back(((get_x(upper_left), mid), (get_x(bottom_right), mid)));
} }
self.top.component.draw( self.top.component.draw(
@ -154,14 +156,13 @@ impl Component for VSplit {
.get(mid, get_y(bottom_right) - 1) .get(mid, get_y(bottom_right) - 1)
.map(|a| a.ch()) .map(|a| a.ch())
.unwrap_or_else(|| ' '); .unwrap_or_else(|| ' ');
match c { if let HORZ_BOUNDARY = c {
HORZ_BOUNDARY => { grid[(mid, get_y(bottom_right) + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
grid[(mid, get_y(bottom_right) + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
}
_ => {}
} }
} }
context.dirty_areas.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right)))); context
.dirty_areas
.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
} }
self.left self.left
.component .component
@ -209,13 +210,13 @@ impl fmt::Display for Pager {
} }
impl Pager { impl Pager {
pub fn update_from_string(&mut self, text: String) -> () { pub fn update_from_str(&mut self, text: &str) -> () {
let lines: Vec<&str> = text.trim().split('\n').collect(); let lines: Vec<&str> = text.trim().split('\n').collect();
let height = lines.len() + 1; let height = lines.len() + 1;
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
//interpret_format_flowed(&text); //interpret_format_flowed(&text);
Pager::print_string(&mut content, &text); Pager::print_string(&mut content, text);
self.content = content; self.content = content;
self.height = height; self.height = height;
self.width = width; self.width = width;
@ -256,11 +257,11 @@ impl Pager {
Pager::print_string(&mut content, &text); Pager::print_string(&mut content, &text);
Pager { Pager {
cursor_pos: cursor_pos.unwrap_or(0), cursor_pos: cursor_pos.unwrap_or(0),
height: height, height,
width: width, width,
dirty: true, dirty: true,
content: content, content,
.. Default::default() ..Default::default()
} }
} }
pub fn from_str(s: &str, cursor_pos: Option<usize>) -> Self { pub fn from_str(s: &str, cursor_pos: Option<usize>) -> Self {
@ -275,7 +276,7 @@ impl Pager {
width, width,
dirty: true, dirty: true,
content, content,
.. Default::default() ..Default::default()
} }
} }
pub fn from_buf(content: CellBuffer, cursor_pos: Option<usize>) -> Self { pub fn from_buf(content: CellBuffer, cursor_pos: Option<usize>) -> Self {
@ -286,7 +287,7 @@ impl Pager {
width, width,
dirty: true, dirty: true,
content, content,
.. Default::default() ..Default::default()
} }
} }
pub fn print_string(content: &mut CellBuffer, s: &str) { pub fn print_string(content: &mut CellBuffer, s: &str) {
@ -327,14 +328,14 @@ impl Component for Pager {
match mvm { match mvm {
PagerMovement::PageUp => { PagerMovement::PageUp => {
self.cursor_pos = self.cursor_pos.saturating_sub(height); self.cursor_pos = self.cursor_pos.saturating_sub(height);
}, }
PagerMovement::PageDown => { PagerMovement::PageDown => {
if self.cursor_pos + 2*height + 1 < self.height { if self.cursor_pos + 2 * height + 1 < self.height {
self.cursor_pos += height; self.cursor_pos += height;
} else { } else {
self.cursor_pos = self.height.saturating_sub(height).saturating_sub(1); self.cursor_pos = self.height.saturating_sub(height).saturating_sub(1);
} }
}, }
} }
} }
@ -394,7 +395,9 @@ impl Component for Pager {
self.dirty = true; self.dirty = true;
return false; return false;
} }
_ => { return false; } _ => {
return false;
}
} }
true true
} }
@ -586,7 +589,7 @@ impl Component for StatusBar {
} }
_ => {} _ => {}
} }
return false; false
} }
fn is_dirty(&self) -> bool { fn is_dirty(&self) -> bool {
self.dirty || self.container.component.is_dirty() self.dirty || self.container.component.is_dirty()
@ -617,7 +620,9 @@ impl fmt::Display for TextBox {
impl Component for TextBox { impl Component for TextBox {
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {} fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool { false } fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
false
}
fn set_dirty(&mut self) {} fn set_dirty(&mut self) {}
} }
@ -669,7 +674,7 @@ impl Component for Progress {
unimplemented!() unimplemented!()
} }
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
return false; false
} }
fn set_dirty(&mut self) {} fn set_dirty(&mut self) {}
} }
@ -751,13 +756,10 @@ impl Component for Tabbed {
} }
} }
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
match &event.event_type { if let UIEventType::Input(Key::Char('T')) = event.event_type {
UIEventType::Input(Key::Char('T')) => { self.cursor_pos = (self.cursor_pos + 1) % self.children.len();
self.cursor_pos = (self.cursor_pos + 1) % self.children.len(); self.children[self.cursor_pos].set_dirty();
self.children[self.cursor_pos].set_dirty(); return true;
return true;
}
_ => {}
} }
self.children[self.cursor_pos].process_event(event, context) self.children[self.cursor_pos].process_event(event, context)
} }

View File

@ -117,10 +117,13 @@ impl Settings {
sent_folder, sent_folder,
}; };
s.insert(id, AccountConf { s.insert(
account: acc, id,
conf: x AccountConf {
}); account: acc,
conf: x,
},
);
} }
Settings { Settings {

View File

@ -23,14 +23,13 @@
* User actions that need to be handled by the UI * User actions that need to be handled by the UI
*/ */
pub use melib::mailbox::{SortOrder, SortField}; pub use melib::mailbox::{SortField, SortOrder};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum MailListingAction { pub enum MailListingAction {
ToggleThreaded, ToggleThreaded,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Action { pub enum Action {
MailListing(MailListingAction), MailListing(MailListingAction),

View File

@ -32,8 +32,8 @@ use super::*;
use chan::{Receiver, Sender}; use chan::{Receiver, Sender};
use fnv::FnvHashMap; use fnv::FnvHashMap;
use std::io::Write; use std::io::Write;
use std::thread;
use std::result; use std::result;
use std::thread;
use std::time; use std::time;
use termion::raw::IntoRawMode; use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen; use termion::screen::AlternateScreen;
@ -60,9 +60,9 @@ impl InputHandler {
tx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(UIMode::Fork))); tx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(UIMode::Fork)));
}, },
&rx, &rx,
) )
}) })
.unwrap(); .unwrap();
} }
fn kill(&self) { fn kill(&self) {
self.tx.send(false); self.tx.send(false);
@ -100,23 +100,33 @@ impl Context {
} }
pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<bool, usize> { pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<bool, usize> {
let s = self.accounts[idx_a].status(idx_m)?; let s = self.accounts[idx_a].status(idx_m)?;
if let Some(Some(event)) = s { match s {
eprintln!("setting up notification"); LoadMailboxResult::New(event) => {
let (idx_a, idx_m) = self.mailbox_hashes[&event.folder]; eprintln!("setting up notification");
let subjects = { let (idx_a, idx_m) = self.mailbox_hashes[&event.folder];
let mut ret = Vec::with_capacity(event.index.len()); let subjects = {
eprintln!("index is {:?}", &event.index); let mut ret = Vec::with_capacity(event.index.len());
for &i in &event.index { eprintln!("index is {:?}", &event.index);
ret.push(self.accounts[idx_a][idx_m].as_ref().unwrap().collection[i].subject()); for &i in &event.index {
} ret.push(
ret self.accounts[idx_a][idx_m].as_ref().unwrap().collection[i].subject(),
}; );
self.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Notification(format!("Update in {}/{}, indexes {:?}", self.accounts[idx_a].name(), self.accounts[idx_a][idx_m].as_ref().unwrap().folder.name(), subjects)) }); }
Ok(true) ret
} else if let Some(None) = s { };
Ok(true) self.replies.push_back(UIEvent {
} else { id: 0,
Ok(false) event_type: UIEventType::Notification(format!(
"Update in {}/{}, indexes {:?}",
self.accounts[idx_a].name(),
self.accounts[idx_a][idx_m].as_ref().unwrap().folder.name(),
subjects
)),
});
Ok(true)
}
LoadMailboxResult::Loaded => Ok(true),
LoadMailboxResult::Refresh => Ok(false),
} }
} }
} }
@ -154,6 +164,12 @@ impl<W: Write> Drop for State<W> {
} }
} }
impl Default for State<std::io::Stdout> {
fn default() -> Self {
Self::new()
}
}
impl State<std::io::Stdout> { impl State<std::io::Stdout> {
pub fn new() -> Self { pub fn new() -> Self {
/* Create a channel to communicate with other threads. The main process is the sole receiver. /* Create a channel to communicate with other threads. The main process is the sole receiver.
@ -236,8 +252,10 @@ impl State<std::io::Stdout> {
startup_thread: Some(startup_tx.clone()), startup_thread: Some(startup_tx.clone()),
threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()), threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
}; };
s.threads s.threads.insert(
.insert(startup_thread.thread().id(), (startup_tx.clone(), startup_thread)); startup_thread.thread().id(),
(startup_tx.clone(), startup_thread),
);
write!( write!(
s.stdout(), s.stdout(),
"{}{}{}", "{}{}{}",
@ -290,13 +308,16 @@ impl State<std::io::Stdout> {
thread::sleep(dur); thread::sleep(dur);
} }
}) })
.expect("Failed to spawn startup-thread in hash_to_folder()") .expect("Failed to spawn startup-thread in hash_to_folder()")
}; };
self.startup_thread = Some(startup_tx.clone()); self.startup_thread = Some(startup_tx.clone());
self.threads self.threads
.insert(startup_thread.thread().id(), (startup_tx, startup_thread)); .insert(startup_thread.thread().id(), (startup_tx, startup_thread));
} else { } else {
eprintln!("BUG: mailbox with hash {} not found in mailbox_hashes.", hash); eprintln!(
"BUG: mailbox with hash {} not found in mailbox_hashes.",
hash
);
} }
} }

View File

@ -25,12 +25,18 @@
use async::*; use async::*;
use conf::AccountConf; use conf::AccountConf;
use melib::error::Result; use mailbox::backends::{Backends, Folder, MailBackend, RefreshEventConsumer};
use mailbox::backends::{MailBackend, Folder, Backends, RefreshEventConsumer};
use mailbox::*; use mailbox::*;
use melib::error::Result;
use std::mem;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::result; use std::result;
use std::mem;
pub enum LoadMailboxResult {
New(NewMailEvent),
Refresh,
Loaded,
}
pub struct NewMailEvent { pub struct NewMailEvent {
pub folder: u64, pub folder: u64,
@ -68,15 +74,13 @@ impl Account {
workers.push(Some(handle)); workers.push(Some(handle));
} }
Account { Account {
name: name, name,
folders: folders, folders,
workers: workers, workers,
sent_folder,
sent_folder: sent_folder,
settings: settings.clone(), settings: settings.clone(),
runtime_settings: settings, runtime_settings: settings,
backend: backend, backend,
} }
} }
pub fn reload(&mut self, idx: usize) { pub fn reload(&mut self, idx: usize) {
@ -91,6 +95,9 @@ impl Account {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.folders.len() self.folders.len()
} }
pub fn is_empty(&self) -> bool {
self.folders.is_empty()
}
pub fn list_folders(&self) -> Vec<Folder> { pub fn list_folders(&self) -> Vec<Folder> {
let mut folders = self.backend.folders(); let mut folders = self.backend.folders();
if let Some(folder_renames) = self.settings.conf().folders() { if let Some(folder_renames) = self.settings.conf().folders() {
@ -109,8 +116,13 @@ impl Account {
pub fn workers(&mut self) -> &mut Vec<Worker> { pub fn workers(&mut self) -> &mut Vec<Worker> {
&mut self.workers &mut self.workers
} }
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> Option<Option<NewMailEvent>> {
let mut ret: Option<Option<NewMailEvent>> = None; fn load_mailbox(
&mut self,
index: usize,
envelopes: Result<Vec<Envelope>>,
) -> LoadMailboxResult {
let mut ret: LoadMailboxResult = LoadMailboxResult::Refresh;
// TODO: Cleanup this function // TODO: Cleanup this function
let folders = self.backend.folders(); let folders = self.backend.folders();
@ -118,22 +130,50 @@ impl Account {
if self.sent_folder.is_some() { if self.sent_folder.is_some() {
let id = self.sent_folder.unwrap(); let id = self.sent_folder.unwrap();
if id == index { if id == index {
/* ======================== */ /* ======================== */
let old_m = mem::replace(&mut self.folders[index], Some(Mailbox::new(folder, &None, envelopes))); let old_m = mem::replace(
&mut self.folders[index],
Some(Mailbox::new(folder, &None, envelopes)),
);
if let Some(old_m) = old_m { if let Some(old_m) = old_m {
if self.folders[index].is_some() && old_m.is_ok() { if self.folders[index].is_some() && old_m.is_ok() {
let diff = self.folders[index].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0)); let diff = self.folders[index]
.as_ref()
.map(|v| v.as_ref().unwrap().collection.len())
.unwrap_or(0)
.saturating_sub(
old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0),
);
if diff > 0 { if diff > 0 {
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&self.folders[index].as_ref().unwrap().as_ref().unwrap().collection).count(); let mut index = old_m
ret = Some(Some(NewMailEvent { .as_ref()
.unwrap()
.collection
.iter()
.zip(
&self.folders[index]
.as_ref()
.unwrap()
.as_ref()
.unwrap()
.collection,
)
.count();
ret = LoadMailboxResult::New(NewMailEvent {
folder: folder.hash(), folder: folder.hash(),
index: (index.saturating_sub(1)..(self.folders[index].as_ref().unwrap().as_ref().unwrap().collection.len().saturating_sub(1))).collect(), index: (index.saturating_sub(1)
})); ..(self.folders[index]
.as_ref()
.unwrap()
.as_ref()
.unwrap()
.collection
.len()
.saturating_sub(1)))
.collect(),
});
} }
} }
} else {
ret = Some(None);
} }
/* ======================== */ /* ======================== */
} else { } else {
@ -151,54 +191,98 @@ impl Account {
if sent[0].is_none() { if sent[0].is_none() {
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone())); sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
} }
/* ======================== */ /* ======================== */
let old_m = mem::replace(&mut cur[0], Some(Mailbox::new(folder, &sent[0], envelopes))); let old_m =
mem::replace(&mut cur[0], Some(Mailbox::new(folder, &sent[0], envelopes)));
if let Some(old_m) = old_m { if let Some(old_m) = old_m {
if cur[0].is_some() && old_m.is_ok() { if cur[0].is_some() && old_m.is_ok() {
let diff = cur[0].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0)); let diff = cur[0]
.as_ref()
.map(|v| v.as_ref().unwrap().collection.len())
.unwrap_or(0)
.saturating_sub(
old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0),
);
if diff > 0 { if diff > 0 {
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&cur[0].as_ref().unwrap().as_ref().unwrap().collection).count(); let mut index = old_m
ret = Some(Some(NewMailEvent { .as_ref()
.unwrap()
.collection
.iter()
.zip(&cur[0].as_ref().unwrap().as_ref().unwrap().collection)
.count();
ret = LoadMailboxResult::New(NewMailEvent {
folder: folder.hash(), folder: folder.hash(),
index: (index.saturating_sub(1)..(cur[0].as_ref().unwrap().as_ref().unwrap().collection.len()).saturating_sub(1)).collect(), index: (index.saturating_sub(1)
})); ..(cur[0]
.as_ref()
.unwrap()
.as_ref()
.unwrap()
.collection
.len())
.saturating_sub(1))
.collect(),
});
} }
} }
} else {
ret = Some(None);
} }
/* ======================== */ /* ======================== */
} }
} else { } else {
/* ======================== */ /* ======================== */
let old_m = mem::replace(&mut self.folders[index], Some(Mailbox::new(folder, &None, envelopes))); let old_m = mem::replace(
if let Some(old_m) = old_m { &mut self.folders[index],
if self.folders[index].is_some() && old_m.is_ok() { Some(Mailbox::new(folder, &None, envelopes)),
let diff = self.folders[index].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0)); );
if diff > 0 { if let Some(old_m) = old_m {
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&self.folders[index].as_ref().unwrap().as_ref().unwrap().collection).count(); if self.folders[index].is_some() && old_m.is_ok() {
ret = Some(Some(NewMailEvent { let diff = self.folders[index]
folder: folder.hash(), .as_ref()
index: (index.saturating_sub(1)..(self.folders[index].as_ref().unwrap().as_ref().unwrap().collection.len().saturating_sub(1))).collect(), .map(|v| v.as_ref().unwrap().collection.len())
})); .unwrap_or(0)
.saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0));
} if diff > 0 {
let mut index = old_m
.as_ref()
.unwrap()
.collection
.iter()
.zip(
&self.folders[index]
.as_ref()
.unwrap()
.as_ref()
.unwrap()
.collection,
)
.count();
ret = LoadMailboxResult::New(NewMailEvent {
folder: folder.hash(),
index: (index.saturating_sub(1)
..(self.folders[index]
.as_ref()
.unwrap()
.as_ref()
.unwrap()
.collection
.len()
.saturating_sub(1)))
.collect(),
});
} }
} else {
ret = Some(None);
} }
}
/* ======================== */ /* ======================== */
}; };
ret ret
} }
pub fn status(&mut self, index: usize) -> result::Result<Option<Option<NewMailEvent>>, usize> { pub fn status(&mut self, index: usize) -> result::Result<LoadMailboxResult, usize> {
match self.workers[index].as_mut() { match self.workers[index].as_mut() {
None => { None => {
return Ok(None); return Ok(LoadMailboxResult::Loaded);
} }
Some(ref mut w) => match w.poll() { Some(ref mut w) => match w.poll() {
Ok(AsyncStatus::NoUpdate) => { Ok(AsyncStatus::NoUpdate) => {

View File

@ -106,13 +106,22 @@ pub struct CellBuffer {
impl fmt::Debug for CellBuffer { impl fmt::Debug for CellBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CellBuffer {{ cols: {}, rows: {}, buf: {} cells", self.cols, self.rows, self.buf.len()) write!(
f,
"CellBuffer {{ cols: {}, rows: {}, buf: {} cells",
self.cols,
self.rows,
self.buf.len()
)
} }
} }
impl CellBuffer { impl CellBuffer {
pub fn area(&self) -> Area { pub fn area(&self) -> Area {
((0, 0), (self.cols.saturating_sub(1), self.rows.saturating_sub(1))) (
(0, 0),
(self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
)
} }
pub fn set_cols(&mut self, new_cols: usize) { pub fn set_cols(&mut self, new_cols: usize) {
self.cols = new_cols; self.cols = new_cols;
@ -216,13 +225,13 @@ impl Default for CellBuffer {
} }
} }
impl<'a> From<&'a String> for CellBuffer { impl<'a> From<&'a str> for CellBuffer {
fn from(s: &'a String) -> Self { fn from(s: &'a str) -> Self {
let lines: Vec<&str> = s.lines().map(|l| l.trim_right()).collect(); let lines: Vec<&str> = s.lines().map(|l| l.trim_right()).collect();
let len = s.len() + lines.len(); let len = s.len() + lines.len();
let mut buf = CellBuffer::new(len, 1, Cell::default()); let mut buf = CellBuffer::new(len, 1, Cell::default());
let mut x = 0; let mut x = 0;
for l in lines.iter() { for l in &lines {
for (idx, c) in l.chars().enumerate() { for (idx, c) in l.chars().enumerate() {
buf[(x + idx, 0)].set_ch(c); buf[(x + idx, 0)].set_ch(c);
} }

View File

@ -22,7 +22,7 @@
use std; use std;
use std::fs; use std::fs;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{Write, Read}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
use uuid::Uuid; use uuid::Uuid;
@ -52,10 +52,11 @@ impl File {
&self.path &self.path
} }
pub fn read_to_string(&self) -> String { pub fn read_to_string(&self) -> String {
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut f = fs::File::open(&self.path).expect(&format!("Can't open {}", &self.path.display())); let mut f = fs::File::open(&self.path)
f.read_to_end(&mut buf).expect(&format!("Can't read {}", &self.path.display())); .unwrap_or_else(|_| panic!("Can't open {}", &self.path.display()));
f.read_to_end(&mut buf)
.unwrap_or_else(|_| panic!("Can't read {}", &self.path.display()));
String::from_utf8(buf).unwrap() String::from_utf8(buf).unwrap()
} }
} }

View File

@ -20,7 +20,7 @@
*/ */
pub mod accounts; pub mod accounts;
pub use self::accounts::Account; pub use self::accounts::{Account, LoadMailboxResult};
#[macro_use] #[macro_use]
mod position; mod position;
#[macro_use] #[macro_use]

View File

@ -87,7 +87,7 @@ macro_rules! height {
/// # } /// # }
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! width{ macro_rules! width {
($a:expr) => { ($a:expr) => {
(get_x(bottom_right!($a))).saturating_sub(get_x(upper_left!($a))) (get_x(bottom_right!($a))).saturating_sub(get_x(upper_left!($a)))
}; };