Browse Source

Run clippy lints

tags/pre-alpha-0.0
parent
commit
2b6d1e0dbf
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS. GPG Key ID: 73627C2F690DF710
29 changed files with 860 additions and 655 deletions
  1. +7
    -1
      melib/src/async.rs
  2. +95
    -82
      melib/src/mailbox/backends/maildir.rs
  3. +6
    -0
      melib/src/mailbox/backends/mod.rs
  4. +19
    -9
      melib/src/mailbox/email/attachment_types.rs
  5. +62
    -65
      melib/src/mailbox/email/attachments.rs
  6. +27
    -32
      melib/src/mailbox/email/mod.rs
  7. +19
    -25
      melib/src/mailbox/email/parser.rs
  8. +6
    -3
      melib/src/mailbox/mod.rs
  9. +120
    -105
      melib/src/mailbox/thread.rs
  10. +2
    -1
      scripts/pre-commit
  11. +0
    -2
      src/bin.rs
  12. +13
    -9
      ui/src/components/mail/compose.rs
  13. +19
    -23
      ui/src/components/mail/listing/compact.rs
  14. +13
    -16
      ui/src/components/mail/listing/mod.rs
  15. +1
    -3
      ui/src/components/mail/mod.rs
  16. +48
    -35
      ui/src/components/mail/view/envelope.rs
  17. +21
    -24
      ui/src/components/mail/view/html.rs
  18. +52
    -39
      ui/src/components/mail/view/mod.rs
  19. +76
    -45
      ui/src/components/mail/view/thread.rs
  20. +15
    -16
      ui/src/components/mod.rs
  21. +31
    -29
      ui/src/components/utilities.rs
  22. +7
    -4
      ui/src/conf/mod.rs
  23. +1
    -2
      ui/src/execute/actions.rs
  24. +45
    -24
      ui/src/state.rs
  25. +134
    -50
      ui/src/types/accounts.rs
  26. +14
    -5
      ui/src/types/cells.rs
  27. +5
    -4
      ui/src/types/helpers.rs
  28. +1
    -1
      ui/src/types/mod.rs
  29. +1
    -1
      ui/src/types/position.rs

+ 7
- 1
melib/src/async.rs View File

@@ -58,6 +58,12 @@ pub struct Async<T> {
rx: chan::Receiver<AsyncStatus>,
}

impl Default for AsyncBuilder {
fn default() -> Self {
AsyncBuilder::new()
}
}

impl AsyncBuilder {
pub fn new() -> Self {
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();
self.value = Some(v);
return Ok(AsyncStatus::Finished);
Ok(AsyncStatus::Finished)
}
}

+ 95
- 82
melib/src/mailbox/backends/maildir.rs View File

@@ -19,15 +19,14 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/

extern crate xdg;
extern crate bincode;
extern crate xdg;

use async::*;
use conf::AccountSettings;
use error::{MeliError, Result};
use mailbox::backends::{
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent,
RefreshEventConsumer,
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer,
};
use mailbox::email::parser;
use mailbox::email::{Envelope, Flag};
@@ -46,11 +45,11 @@ extern crate crossbeam;
use memmap::{Mmap, Protection};
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::Read;
use std::sync::{Mutex, Arc};
use std::hash::{Hasher, Hash};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
extern crate fnv;
use self::fnv::FnvHashMap;

@@ -66,7 +65,7 @@ impl Clone for MaildirOp {
fn clone(&self) -> Self {
MaildirOp {
hash_index: self.hash_index.clone(),
hash: self.hash.clone(),
hash: self.hash,
slice: None,
}
}
@@ -89,7 +88,7 @@ impl MaildirOp {

impl<'a> BackendOp for MaildirOp {
fn description(&self) -> String {
format!("Path of file:")// self.0ipath)
format!("Path of file: {}", self.path())
}
fn as_bytes(&mut self) -> Result<&[u8]> {
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<()> {
let path = self.path();
let idx: usize = path.rfind(":2,").ok_or(MeliError::new(format!(
"Invalid email filename: {:?}",
self
)))? + 3;
let idx: usize = path
.rfind(":2,")
.ok_or_else(|| MeliError::new(format!("Invalid email filename: {:?}", self)))?
+ 3;
let mut new_name: String = path[..idx].to_string();
let mut flags = self.fetch_flags();
if !(flags & *f).is_empty() {
@@ -212,24 +211,24 @@ impl MailBackend for MaildirType {
match rx.recv() {
Ok(event) => match event {
DebouncedEvent::Create(mut pathbuf)
| DebouncedEvent::Remove(mut pathbuf) => {
if pathbuf.is_dir() {
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
pathbuf.pop();
}
} else {
pathbuf.pop();
| DebouncedEvent::Remove(mut pathbuf) => {
if pathbuf.is_dir() {
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
pathbuf.pop();
};
eprintln!(" got event in {}", pathbuf.display());
}
} else {
pathbuf.pop();
pathbuf.pop();
};
eprintln!(" got event in {}", pathbuf.display());

let mut hasher = DefaultHasher::new();
pathbuf.hash(&mut hasher);
sender.send(RefreshEvent {
folder: format!("{}", pathbuf.display()),
hash: hasher.finish(),
});
}
let mut hasher = DefaultHasher::new();
pathbuf.hash(&mut hasher);
sender.send(RefreshEvent {
folder: format!("{}", pathbuf.display()),
hash: hasher.finish(),
});
}
_ => {}
},
Err(e) => eprintln!("watch error: {:?}", e),
@@ -261,7 +260,7 @@ impl MaildirType {
path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(),
path_children,
) {
) {
folders.push(f);
children.push(folders.len() - 1);
}
@@ -277,7 +276,7 @@ impl MaildirType {
path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(),
Vec::with_capacity(0),
) {
) {
folders.push(f);
}
}
@@ -285,7 +284,10 @@ impl MaildirType {
MaildirType {
name: f.name().to_string(),
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(),
}
}
@@ -342,28 +344,33 @@ impl MaildirType {
let s = scope.builder().name(name.clone()).spawn(move || {
let len = chunk.len();
let size = if len <= 100 { 100 } else { (len / 100) * 100 };
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
let mut local_r: Vec<
Envelope,
> = Vec::with_capacity(chunk.len());
for c in chunk.chunks(size) {
let map = map.clone();
let len = c.len();
for file in c {
let ri = file.rfind("/").unwrap() + 1;
let ri = file.rfind('/').unwrap() + 1;
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
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 mut map = map.lock().unwrap();
let hash = env.hash();
if (*map).contains_key(&hash) {
continue;
}
(*map).insert(hash, (0, file.to_string()));
local_r.push(env);
{
let mut map = map.lock().unwrap();
let hash = env.hash();
if (*map).contains_key(&hash) {
continue;
}

(*map).insert(hash, (0, file.to_string()));
local_r.push(env);
continue;
}
}
let e_copy = file.to_string();
/*
@@ -376,41 +383,48 @@ impl MaildirType {
*/
{
let mut hasher = DefaultHasher::new();
let hash = {
let mut buf = Vec::new();
let mut f = fs::File::open(&e_copy).expect(&format!("Can't open {}", e_copy));
f.read_to_end(&mut buf).expect(&format!("Can't read {}", e_copy));
/* Unwrap is safe since we use ? above. */
hasher.write(&buf);
hasher.finish()
};
{
let mut map = map.lock().unwrap();
if (*map).contains_key(&hash) {
continue;
}
(*map).insert(hash, (0, e_copy));
}
// TODO: Check cache
let op = Box::new(MaildirOp::new(hash, map.clone()));
if let Some(mut e) = Envelope::from_token(op, hash) {
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 hash = {
let mut buf = Vec::new();
let mut f = fs::File::open(&e_copy)
.unwrap_or_else(|_| {
panic!("Can't open {}", e_copy)
});
f.read_to_end(&mut buf).unwrap_or_else(|_| {
panic!("Can't read {}", e_copy)
});
/* Unwrap is safe since we use ? above. */
hasher.write(&buf);
hasher.finish()
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &e).unwrap();
}
local_r.push(e);


} else {
{
let mut map = map.lock().unwrap();
if (*map).contains_key(&hash) {
continue;
}

(*map).insert(hash, (0, e_copy));
}
// TODO: Check cache
let op =
Box::new(MaildirOp::new(hash, map.clone()));
if let Some(mut e) = Envelope::from_token(op, hash)
{
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));
@@ -429,16 +443,15 @@ impl MaildirType {
for (idx, e) in r.iter().enumerate() {
let mut y = (*map)[&e.hash()].clone();
y.0 = idx;
(*map).insert(e.hash(),y);
(*map).insert(e.hash(), y);
}
tx.send(AsyncStatus::Finished);
Ok(r)
})
.unwrap()
.unwrap()
};
w.build(handle)
}

}

#[derive(Debug, Default)]
@@ -459,7 +472,7 @@ impl MaildirFolder {
hash: h.finish(),
name: file_name,
path: pathbuf,
children: children,
children,
};
ret.is_valid()?;
Ok(ret)
@@ -474,9 +487,9 @@ impl MaildirFolder {
p.push(d);
if !p.is_dir() {
return Err(MeliError::new(format!(
"{} is not a valid maildir folder",
path.display()
)));
"{} is not a valid maildir folder",
path.display()
)));
}
p.pop();
}


+ 6
- 0
melib/src/mailbox/backends/mod.rs View File

@@ -44,6 +44,12 @@ pub struct Backends {
map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>,
}

impl Default for Backends {
fn default() -> Self {
Backends::new()
}
}

impl Backends {
pub fn new() -> Self {
let mut b = Backends {


+ 19
- 9
melib/src/mailbox/email/attachment_types.rs View File

@@ -1,5 +1,5 @@
use mailbox::email::parser::BytesExt;
use mailbox::email::attachments::Attachment;
use mailbox::email::parser::BytesExt;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str;

@@ -20,13 +20,11 @@ impl SliceBuild {
//fn length(&self) -> usize {
// 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]
}
}



#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Charset {
Ascii,
@@ -72,7 +70,7 @@ impl<'a> From<&'a [u8]> for Charset {
_ => {
eprintln!("unknown tag is {:?}", str::from_utf8(b));
Charset::Ascii
},
}
}
}
}
@@ -102,10 +100,19 @@ impl Display for MultipartType {

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ContentType {
Text { kind: Text, charset: Charset },
Multipart { boundary: SliceBuild, kind: MultipartType, subattachments: Vec<Attachment>},
Text {
kind: Text,
charset: Charset,
},
Multipart {
boundary: SliceBuild,
kind: MultipartType,
subattachments: Vec<Attachment>,
},
MessageRfc822,
Unsupported { tag: Vec<u8> },
Unsupported {
tag: Vec<u8>,
},
}

impl Default for ContentType {
@@ -137,7 +144,10 @@ impl ContentType {
}
}
pub fn is_text_html(&self) -> bool {
if let ContentType::Text { kind: Text::Html, .. } = self {
if let ContentType::Text {
kind: Text::Html, ..
} = self
{
true
} else {
false


+ 62
- 65
melib/src/mailbox/email/attachments.rs View File

@@ -19,9 +19,9 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use data_encoding::BASE64_MIME;
use mailbox::email::EnvelopeWrapper;
use mailbox::email::parser;
use mailbox::email::parser::BytesExt;
use mailbox::email::EnvelopeWrapper;
use std::fmt;
use std::str;

@@ -84,7 +84,7 @@ impl AttachmentBuilder {
let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize);
let boundary = SliceBuild::new(offset, _boundary.len());
let subattachments = Self::subattachments(&self.raw, boundary.get(&value));
assert!(subattachments.len() > 0);
assert!(!subattachments.is_empty());
self.content_type = ContentType::Multipart {
boundary,
kind: if cst.eq_ignore_ascii_case(b"mixed") {
@@ -96,7 +96,7 @@ impl AttachmentBuilder {
} else {
Default::default()
},
subattachments
subattachments,
};
} else if ct.eq_ignore_ascii_case(b"text") {
self.content_type = ContentType::Text {
@@ -105,28 +105,28 @@ impl AttachmentBuilder {
};
for (n, v) in params {
if n.eq_ignore_ascii_case(b"charset") {
match self.content_type {
ContentType::Text { charset: ref mut c, .. } => {
*c = Charset::from(v);
},
_ => {},
if let ContentType::Text {
charset: ref mut c, ..
} = self.content_type
{
*c = Charset::from(v);
}
break;
break;
}
}
if cst.eq_ignore_ascii_case(b"html") {
match self.content_type {
ContentType::Text { kind: ref mut k, .. } => {
*k = Text::Html;
},
_ => {},
if let ContentType::Text {
kind: ref mut k, ..
} = self.content_type
{
*k = Text::Html;
}
} else if !cst.eq_ignore_ascii_case(b"plain") {
match self.content_type {
ContentType::Text { kind: ref mut k, .. } => {
*k = Text::Other { tag: cst.into() };
},
_ => {},
if let ContentType::Text {
kind: ref mut k, ..
} = 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") {
@@ -136,9 +136,7 @@ impl AttachmentBuilder {
tag.extend(ct);
tag.push(b'/');
tag.extend(cst);
self.content_type = ContentType::Unsupported {
tag
};
self.content_type = ContentType::Unsupported { tag };
},
Err(v) => {
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
@@ -197,8 +195,7 @@ impl AttachmentBuilder {
}

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) => {
let mut vec = Vec::with_capacity(attachments.len());
for a in attachments {
@@ -246,37 +243,29 @@ impl AttachmentBuilder {
impl fmt::Display for Attachment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.content_type {
ContentType::MessageRfc822 => {
match EnvelopeWrapper::new(self.bytes().to_vec()) {
Ok(wrapper) => write!(f, "message/rfc822: {} - {} - {}", wrapper.date(), wrapper.from_to_string(), wrapper.subject()),
Err(e) => write!(f, "{}", e),
}
ContentType::MessageRfc822 => match EnvelopeWrapper::new(self.bytes().to_vec()) {
Ok(wrapper) => write!(
f,
"message/rfc822: {} - {} - {}",
wrapper.date(),
wrapper.field_from_to_string(),
wrapper.subject()
),
Err(e) => write!(f, "{}", e),
},
ContentType::Unsupported { .. } => {
write!(f, "Data attachment of type {}", self.mime_type())
}
ContentType::Text { .. } => {
write!(f, "Text attachment of type {}", self.mime_type())
}
ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
ContentType::Multipart {
kind: ref multipart_type,
subattachments: ref sub_att_vec,
..
} => if *multipart_type == MultipartType::Alternative {
write!(
f,
"{} attachment with {} subs",
self.mime_type(),
sub_att_vec.len()
)
} else {
write!(
f,
"{} attachment with {} subs",
self.mime_type(),
sub_att_vec.len()
)
},
} => write!(
f,
"{} attachment with {} subs",
self.mime_type(),
sub_att_vec.len()
),
}
}
}
@@ -297,20 +286,23 @@ impl Attachment {
} => match *multipart_type {
MultipartType::Alternative => {
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);
break;
}
}
},
}
MultipartType::Mixed | MultipartType::Digest => {
for a in sub_att_vec {
a.get_text_recursive(text);
text.extend_from_slice(b"\n\n");
}
},
}
_ => {},
}
},
_ => {}
}
}
pub fn text(&self) -> String {
@@ -356,7 +348,9 @@ impl Attachment {
}
pub fn is_html(&self) -> bool {
match self.content_type {
ContentType::Text { kind: Text::Html, .. } => true,
ContentType::Text {
kind: Text::Html, ..
} => true,
_ => false,
}
}
@@ -369,13 +363,13 @@ pub fn interpret_format_flowed(_t: &str) -> String {

fn decode_rfc822(_raw: &[u8]) -> Attachment {
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) {
Some(e) => e,
None => {
None => {
eprintln!("error in parsing mail");
let error_msg = b"Mail cannot be shown because of errors.";
let mut builder = AttachmentBuilder::new(error_msg);
@@ -384,10 +378,11 @@ fn decode_rfc822(_raw: &[u8]) -> Attachment {
};
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 {
ContentType::Unsupported { .. } => Vec::new(),
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 {
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);
}
}
@@ -417,10 +415,10 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Ve
}
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)
}
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 {
ContentType::Text { charset: c, .. } => c,
_ => Default::default(),
@@ -449,12 +447,11 @@ fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8
bytes.to_vec()
};
if let Some(filter) = filter {
filter(a, &mut ret);
filter(a, &mut 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)
}

+ 27
- 32
melib/src/mailbox/email/mod.rs View File

@@ -129,10 +129,10 @@ impl StrBuilder {
let offset = self.offset;
let length = self.length;
String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
}
}
#[cfg(test)]
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(
string.to_owned(),
StrBuilder {
offset: offset,
offset,
length: slice.len() + 1,
},
)
@@ -197,12 +197,12 @@ struct References {
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct Flag: u8 {
const PASSED = 0b00000001;
const REPLIED = 0b00000010;
const SEEN = 0b00000100;
const TRASHED = 0b00001000;
const DRAFT = 0b00010000;
const FLAGGED = 0b00100000;
const PASSED = 0b0000_0001;
const REPLIED = 0b0000_0010;
const SEEN = 0b0000_0100;
const TRASHED = 0b0000_1000;
const DRAFT = 0b0001_0000;
const FLAGGED = 0b0010_0000;
}
}

@@ -212,7 +212,6 @@ pub struct EnvelopeWrapper {
buffer: Vec<u8>,
}


use std::ops::Deref;

impl Deref for EnvelopeWrapper {
@@ -317,7 +316,6 @@ impl Envelope {
self.hash
}
pub fn populate_headers(&mut self, bytes: &[u8]) -> Result<()> {

let (headers, _) = match parser::mail(bytes).to_full_result() {
Ok(v) => v,
Err(e) => {
@@ -372,13 +370,13 @@ impl Envelope {
self.set_in_reply_to(value);
in_reply_to = Some(value);
} else if name.eq_ignore_ascii_case(b"date") {
let parse_result = parser::phrase(value);
if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap();
self.set_date(value.as_slice());
} else {
self.set_date(value);
}
let parse_result = parser::phrase(value);
if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap();
self.set_date(value.as_slice());
} else {
self.set_date(value);
}
}
}
/*
@@ -400,24 +398,21 @@ impl Envelope {
Ok(())
}


pub fn populate_headers_from_token(&mut self, mut operation: Box<BackendOp>) -> Result<()> {
{
let headers = operation.fetch_headers()?;
return self.populate_headers(headers);
}
let headers = operation.fetch_headers()?;
self.populate_headers(headers)
}
pub fn date(&self) -> u64 {
self.timestamp
}

pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
if let Some(d) = parser::date(&self.date.as_bytes()) {
return d;
}
chrono::FixedOffset::west(0)
.ymd(1970, 1, 1)
.and_hms(0, 0, 0)
if let Some(d) = parser::date(&self.date.as_bytes()) {
return d;
}
chrono::FixedOffset::west(0)
.ymd(1970, 1, 1)
.and_hms(0, 0, 0)
}
pub fn date_as_str(&self) -> &str {
&self.date
@@ -426,14 +421,14 @@ impl Envelope {
&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();
_strings.join(", ")
}
pub fn to(&self) -> &Vec<Address> {
&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();
_strings.join(", ")
}
@@ -504,7 +499,7 @@ impl Envelope {
}
pub fn in_reply_to_raw(&self) -> Cow<str> {
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()),
}
}


+ 19
- 25
melib/src/mailbox/email/parser.rs View File

@@ -114,12 +114,11 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
let input_len = input.len();
for (i, x) in input.iter().enumerate() {
if *x == b'\n' {
if ((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t')
|| i + 1 == input_len
{
return IResult::Done(&input[(i + 1)..], &input[0..i]);
}
if *x == b'\n'
&& (((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t')
|| i + 1 == input_len)
{
return IResult::Done(&input[(i + 1)..], &input[0..i]);
}
}
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::Error(error_code!(ErrorKind::Custom(43)));
IResult::Error(error_code!(ErrorKind::Custom(43)))
}

named!(pub body_raw<&[u8]>,
@@ -337,9 +336,9 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Done(
rest,
Address::Mailbox(MailboxAddress {
raw: raw,
display_name: display_name,
address_spec: address_spec,
raw,
display_name,
address_spec,
}),
)
}
@@ -414,12 +413,11 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
}

match mailbox_list(&input[dlength..]) {
IResult::Error(e) => {
return IResult::Error(e);
}
IResult::Error(e) => IResult::Error(e),
IResult::Done(rest, vec) => {
let size: usize = (rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize);
return IResult::Done(
let size: usize =
(rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize);
IResult::Done(
rest,
Address::Group(GroupAddress {
raw: input[0..size].into(),
@@ -429,11 +427,9 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
},
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::Done(input, ret);
IResult::Done(input, ret)
}

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
});
let bytes = list
.iter()
list.iter()
.fold(Vec::with_capacity(list_len), |mut acc, x| {
acc.append(&mut x.clone());
acc
});
bytes
})
})
))
);
@@ -685,7 +679,7 @@ pub fn phrase(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
acc.push(b' ');
}
}
return IResult::Done(&[], acc);
IResult::Done(&[], acc)
}

#[cfg(test)]


+ 6
- 3
melib/src/mailbox/mod.rs View File

@@ -32,7 +32,7 @@ pub mod backends;
use error::Result;
use mailbox::backends::{folder_default, Folder};
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;

@@ -74,10 +74,13 @@ impl Mailbox {
let threads = build_threads(&mut collection, sent_folder);
Ok(Mailbox {
folder: (*folder).clone(),
collection: collection,
threads: threads,
collection,
threads,
})
}
pub fn is_empty(&self) -> bool {
self.collection.is_empty()
}
pub fn len(&self) -> usize {
self.collection.len()
}


+ 120
- 105
melib/src/mailbox/thread.rs View File

@@ -30,11 +30,11 @@ use mailbox::Mailbox;
extern crate fnv;
use self::fnv::FnvHashMap;
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::cmp::Ordering;
use std::ops::Index;
use std::result::Result as StdResult;
use std::str::FromStr;

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum SortOrder {
@@ -122,7 +122,6 @@ impl ContainerTree {
}
}


#[derive(Clone, Debug, Default)]
pub struct Threads {
containers: Vec<Container>,
@@ -136,10 +135,10 @@ pub struct Threads {
pub struct ThreadIterator<'a> {
pos: usize,
stack: Vec<usize>,
tree: Ref<'a ,Vec<ContainerTree>>,
tree: Ref<'a, Vec<ContainerTree>>,
}
impl<'a> Iterator for ThreadIterator<'a> {
type Item = usize;
type Item = usize;
fn next(&mut self) -> Option<usize> {
{
let mut tree = &(*self.tree);
@@ -163,7 +162,7 @@ impl<'a> Iterator for ThreadIterator<'a> {
return Some(ret);
}
}
return self.next();
self.next()
}
}

@@ -172,25 +171,28 @@ impl<'a> IntoIterator for &'a Threads {
type IntoIter = ThreadIterator<'a>;

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> {
pos: usize,
tree: Ref<'a ,Vec<ContainerTree>>,
tree: Ref<'a, Vec<ContainerTree>>,
}

impl<'a> Iterator for RootIterator<'a> {
type Item = (usize, usize, bool);
type Item = (usize, usize, bool);
fn next(&mut self) -> Option<(usize, usize, bool)> {
if self.pos == self.tree.len() {
return None;
}
let node = &self.tree[self.pos];
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> {
&self.root_set
}
pub fn root_set_iter(&self) -> RootIterator {
RootIterator { pos: 0, tree: self.tree.borrow() }
pub fn root_set_iter(&self) -> RootIterator {
RootIterator {
pos: 0,
tree: self.tree.borrow(),
}
}
pub fn thread_to_mail(&self, i: usize) -> usize {
let thread = self.containers[self.threaded_collection[i]];
@@ -219,8 +224,8 @@ impl Threads {
let tree = &mut self.tree.borrow_mut();
let containers = &self.containers;
for mut t in tree.iter_mut() {
if let Some(ref mut c ) = t.children {
c.sort_by(|a, b| { match subsort {
if let Some(ref mut c) = t.children {
c.sort_by(|a, b| match subsort {
(SortField::Date, SortOrder::Desc) => {
let a = containers[a.id];
let b = containers[b.id];
@@ -253,7 +258,6 @@ impl Threads {
let mb = &collection[b.unwrap()];
mb.subject().cmp(&ma.subject())
}
}
});
}
}
@@ -262,12 +266,11 @@ impl Threads {
fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) {
let tree = &mut self.tree.borrow_mut();
let containers = &self.containers;
tree.sort_by(|a, b| { match sort {
tree.sort_by(|a, b| match sort {
(SortField::Date, SortOrder::Desc) => {
let a = containers[a.id];
let b = containers[b.id];
b.date.cmp(&a.date)

}
(SortField::Date, SortOrder::Asc) => {
let a = containers[a.id];
@@ -296,10 +299,14 @@ impl Threads {
let mb = &collection[b.unwrap()];
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 {
self.inner_sort_by(sort, collection);
*self.sort.borrow_mut() = sort;
@@ -308,7 +315,6 @@ impl Threads {
self.inner_subsort_by(subsort, collection);
*self.subsort.borrow_mut() = subsort;
}

}

pub fn build_collection(&mut self, collection: &[Envelope]) {
@@ -320,7 +326,7 @@ impl Threads {
i: usize,
root_subject_idx: usize,
collection: &[Envelope],
) {
) {
let thread = containers[i];
if let Some(msg_idx) = containers[root_subject_idx].message() {
let root_subject = collection[msg_idx].subject();
@@ -333,9 +339,9 @@ impl Threads {
if subject == root_subject
|| subject.starts_with("Re: ")
&& 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() {
@@ -360,7 +366,15 @@ impl Threads {

loop {
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;
child_vec.push(new_child_tree);
let thread_ = containers[fc];
@@ -384,7 +398,7 @@ impl Threads {
*i,
*i,
collection,
);
);
tree.push(tree_node);
}
self.tree.replace(tree);
@@ -397,11 +411,12 @@ impl Index<usize> for Threads {
type Output = 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 {
pub fn date(&self) -> UnixTimestamp {
self.date
@@ -474,7 +489,7 @@ fn build_collection(
threads: &mut Vec<Container>,
id_table: &mut FnvHashMap<Cow<str>, usize>,
collection: &mut [Envelope],
) -> () {
) -> () {
for (i, x) in collection.iter_mut().enumerate() {
let x_index; /* x's index in threads */
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 p = id_table[r.as_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);
if threads[p].first_child.is_none() {
@@ -589,7 +604,7 @@ fn build_collection(
pub fn build_threads(
collection: &mut Vec<Envelope>,
sent_folder: &Option<Result<Mailbox>>,
) -> Threads {
) -> Threads {
/* To reconstruct thread information from the mails we need: */

/* a vector to hold thread members */
@@ -623,77 +638,77 @@ pub fn build_threads(
if id_table.contains_key(&m_id)
|| (!x.in_reply_to_raw().is_empty()
&& 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();
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;
let p = id_table[&x_r_id];
let c = if id_table.contains_key(&m_id) {
id_table[&m_id]
} else {
threads.push(Container {
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);
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 p = id_table[&x_r_id];
let c = if id_table.contains_key(&m_id) {
id_table[&m_id]
} else {
threads.push(Container {
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[fc].next_sibling = Some(c);
threads[fc].parent = Some(p);
threads[fc].next_sibling = Some(c);
threads[fc].parent = Some(p);
}
/* update thread date */
let mut parent_iter = p;
'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;
}
/* update thread date */
let mut parent_iter = p;
'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;
}
}
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].has_message()
&& threads[*v].has_children()
&& !threads[threads[*v].first_child.unwrap()].has_sibling()
{
/* 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. */
root_set.push(threads[*v].first_child.unwrap());
&& !threads[threads[*v].first_child.unwrap()].has_sibling()
{
/* 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. */
root_set.push(threads[*v].first_child.unwrap());

continue 'root_set;
}
continue 'root_set;
}
root_set.push(*v);
}
}


+ 2
- 1
scripts/pre-commit View File

@@ -1,7 +1,8 @@
#!/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
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

+ 0
- 2
src/bin.rs View File

@@ -35,7 +35,6 @@ extern crate ui;
pub use melib::*;
pub use ui::*;


#[macro_use]
extern crate chan;
extern crate chan_signal;
@@ -56,7 +55,6 @@ fn main() {
/* Catch SIGWINCH to handle terminal resizing */
let signal = chan_signal::notify(&[Signal::WINCH]);


/* Create the application State. This is the 'System' part of an ECS architecture */
let mut state = State::new();



+ 13
- 9
ui/src/components/mail/compose.rs View File

@@ -65,7 +65,7 @@ impl Component for Composer {
let width = width!(area);
let mid = if width > 80 {
let width = width - 80;
let mid = width / 2;;
let mid = width / 2;

if self.dirty {
for i in get_y(upper_left)..=get_y(bottom_right) {
@@ -78,17 +78,22 @@ impl Component for Composer {
}
}
mid
} else { 0 };
} else {
0
};

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_fg(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 {
context.dirty_areas.push_back(area);
@@ -97,8 +102,7 @@ impl Component for Composer {
match self.mode {
ViewMode::Overview => {
self.pager.draw(grid, body_area, context);

},
}
}
}

@@ -134,12 +138,12 @@ impl Component for Composer {
.expect("failed to execute process");
let result = f.read_to_string();
self.buffer = result.clone();
self.pager.update_from_string(result);
self.pager.update_from_str(result.as_str());
context.restore_input();
self.dirty = true;
return true;
},
_ => {},
}
_ => {}
}
false
}


+ 19
- 23
ui/src/components/mail/listing/compact.rs View File

@@ -67,14 +67,14 @@ impl CompactListing {
&CompactListing::format_date(e),
e.subject(),
len
)
)
} else {
format!(
"{} {} {:.85}",
idx,
&CompactListing::format_date(e),
e.subject(),
)
)
}
}

@@ -86,7 +86,7 @@ impl CompactListing {
length: 0,
sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()),
content: content,
content,
dirty: true,
unfocused: false,
view: None,
@@ -110,7 +110,10 @@ impl CompactListing {
//
loop {
// 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;
}
}
@@ -118,7 +121,6 @@ impl CompactListing {
.as_ref()
.unwrap();


self.length = mailbox.threads.root_len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
if self.length == 0 {
@@ -139,9 +141,9 @@ impl CompactListing {
let i = if let Some(i) = container.message() {
i
} else {
threads.containers()[
container.first_child().unwrap()
].message().unwrap()
threads.containers()[container.first_child().unwrap()]
.message()
.unwrap()
};
let root_envelope: &Envelope = &mailbox.collection[i];
let fg_color = if has_unseen {
@@ -163,16 +165,13 @@ impl CompactListing {
bg_color,
((0, idx), (MAX_COLS - 1, idx)),
false,
);
);

for x in x..MAX_COLS {
self.content[(x, idx)].set_ch(' ');
self.content[(x, idx)].set_bg(bg_color);
}


}

}

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() {
i
} else {
threads.containers()[
container.first_child().unwrap()
].message().unwrap()
threads.containers()[container.first_child().unwrap()]
.message()
.unwrap()
};
let root_envelope: &Envelope = &mailbox.collection[i];
let fg_color = if !root_envelope.is_seen() {
@@ -303,11 +302,7 @@ impl Component for CompactListing {
return;
}
self.view = Some(ThreadView::new(self.cursor_pos, context));
self.view.as_mut().unwrap().draw(
grid,
area,
context,
);
self.view.as_mut().unwrap().draw(grid, area, context);
self.dirty = false;
}
}
@@ -440,8 +435,7 @@ impl Component for CompactListing {
self.dirty = true;
self.refresh_mailbox(context);
return true;
}
// _ => {}
} // _ => {}
},
_ => {}
}
@@ -451,7 +445,9 @@ impl Component for CompactListing {
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
}
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;
}
}

+ 13
- 16
ui/src/components/mail/listing/mod.rs View File

@@ -79,7 +79,7 @@ impl MailListing {
local_collection: Vec::new(),
sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()),
content: content,
content,
dirty: true,
unfocused: false,
view: None,
@@ -107,7 +107,10 @@ impl MailListing {
//
loop {
// 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;
}
}
@@ -162,7 +165,6 @@ impl MailListing {
continue;
}


match iter.peek() {
Some(&x) if threads[x].indentation() == indentation => {
indentations.pop();
@@ -198,7 +200,7 @@ impl MailListing {
container,
&indentations,
len,
// context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
// context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
),
&mut self.content,
fg_color,
@@ -522,9 +524,7 @@ impl Component for MailListing {
.threaded();
let account = &mut context.accounts[self.cursor_pos.0];
let (hash, is_seen) = {
let mailbox = &mut account[self.cursor_pos.1]
.as_mut()
.unwrap();
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = if threaded {
let i = mailbox.threaded_mail(idx);
&mut mailbox.collection[i]
@@ -538,9 +538,7 @@ impl Component for MailListing {
let backend = &account.backend;
backend.operation(hash)
};
let mailbox = &mut account[self.cursor_pos.1]
.as_mut()
.unwrap();
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = if threaded {
let i = mailbox.threaded_mail(idx);
&mut mailbox.collection[i]
@@ -598,9 +596,7 @@ impl Component for MailListing {
.conf()
.threaded();
let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1]
.as_ref()
.unwrap();
let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
let mut coordinates = self.cursor_pos;
coordinates.2 = if threaded {
mailbox.threaded_mail(self.cursor_pos.2)
@@ -792,8 +788,7 @@ impl Component for MailListing {
self.dirty = true;
self.refresh_mailbox(context);
return true;
}
// _ => {}
} // _ => {}
},
_ => {}
}
@@ -803,7 +798,9 @@ impl Component for MailListing {
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
}
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;
}
}

+ 1
- 3
ui/src/components/mail/mod.rs View File

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


+ 48
- 35
ui/src/components/mail/view/envelope.rs View File

@@ -68,7 +68,7 @@ impl EnvelopeView {
wrapper: EnvelopeWrapper,
pager: Option<Pager>,
subview: Option<Box<Component>>,
) -> Self {
) -> Self {
EnvelopeView {
pager,
subview,
@@ -81,33 +81,34 @@ impl EnvelopeView {
}

/// 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 body_text = String::from_utf8_lossy(&decode_rec(
&body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() {
use std::io::Write;
use std::process::{Command, Stdio};
&body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() {
use std::io::Write;
use std::process::{Command, Stdio};

let mut html_filter = Command::new("w3m")
.args(&["-I", "utf-8", "-T", "text/html"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start html filter process");
let mut html_filter = Command::new("w3m")
.args(&["-I", "utf-8", "-T", "text/html"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start html filter process");

html_filter
.stdin
.as_mut()
.unwrap()
.write_all(&v)
.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.extend(html_filter.wait_with_output().unwrap().stdout);
}
})),
)).into_owned();
html_filter
.stdin
.as_mut()
.unwrap()
.write_all(&v)
.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.extend(html_filter.wait_with_output().unwrap().stdout);
}
})),
)).into_owned();
match self.mode {
ViewMode::Normal | ViewMode::Subview => {
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);

if highlight_urls {
@@ -197,7 +198,7 @@ impl Component for EnvelopeView {
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);

let y :usize = {
let y: usize = {
let envelope: &Envelope = &self.wrapper;

if self.mode == ViewMode::Raw {
@@ -219,7 +220,7 @@ impl Component for EnvelopeView {
grid[(x, y)].set_fg(Color::Default);
}
let (x, y) = write_string_to_grid(
&format!("From: {}", envelope.from_to_string()),
&format!("From: {}", envelope.field_from_to_string()),
grid,
Color::Byte(33),
Color::Default,
@@ -232,7 +233,7 @@ impl Component for EnvelopeView {
grid[(x, y)].set_fg(Color::Default);
}
let (x, y) = write_string_to_grid(
&format!("To: {}", envelope.to_to_string()),
&format!("To: {}", envelope.field_to_to_string()),
grid,
Color::Byte(33),
Color::Default,
@@ -293,7 +294,7 @@ impl Component for EnvelopeView {
}
_ => {
let buf = {
let text = self.attachment_to_text(body);
let text = self.attachment_to_text(&body);
// URL indexes must be colored (ugh..)
EnvelopeView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
};
@@ -344,7 +345,9 @@ impl Component for EnvelopeView {
self.dirty = 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.subview.take();
self.dirty = true;
@@ -358,12 +361,19 @@ impl Component for EnvelopeView {

{
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() {
ContentType::MessageRfc822 => {
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 { .. } => {
self.mode = ViewMode::Attachment(lidx);
@@ -416,7 +426,7 @@ impl Component for EnvelopeView {
}
};
return true;
},
}
UIEventType::Input(Key::Char('g'))
if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url =>
{
@@ -425,7 +435,10 @@ impl Component for EnvelopeView {
let url = {
let envelope: &Envelope = self.wrapper.envelope();
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();
if let Some(u) = links.get(lidx) {
u.as_str().to_string()


+ 21
- 24
ui/src/components/mail/view/html.rs View File

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