From 2f91d2932610947c166120aa5608641ec82886e6 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 27 Jul 2018 21:37:56 +0300 Subject: [PATCH] rustfmt everything --- benches/parse.rs | 4 +- melib/src/conf/mod.rs | 38 +-- melib/src/conf/pager.rs | 12 +- melib/src/error.rs | 2 +- melib/src/lib.rs | 13 +- melib/src/mailbox/accounts.rs | 12 +- melib/src/mailbox/backends/imap.rs | 23 +- melib/src/mailbox/backends/maildir.rs | 96 ++++---- melib/src/mailbox/backends/mbox.rs | 25 +- melib/src/mailbox/backends/mod.rs | 19 +- melib/src/mailbox/email/attachments.rs | 125 ++++------ melib/src/mailbox/email/mod.rs | 84 +++---- melib/src/mailbox/email/parser.rs | 221 ++++++++--------- melib/src/mailbox/mod.rs | 10 +- melib/src/mailbox/thread.rs | 34 ++- src/bin.rs | 69 ++++-- ui/src/cells.rs | 25 +- ui/src/components/mail/listing.rs | 313 +++++++++++++++---------- ui/src/components/mail/mod.rs | 72 +++--- ui/src/components/mod.rs | 75 +++--- ui/src/components/notifications.rs | 18 +- ui/src/components/utilities.rs | 235 +++++++++++-------- ui/src/execute/mod.rs | 12 +- ui/src/helpers.rs | 14 +- ui/src/lib.rs | 228 +++++++++++------- ui/src/position.rs | 32 ++- 26 files changed, 1002 insertions(+), 809 deletions(-) diff --git a/benches/parse.rs b/benches/parse.rs index 12a93961d..d149d35c6 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -1,9 +1,9 @@ #![feature(test)] extern crate melib; -use melib::mailbox::email::Envelope; -use melib::mailbox::backends::BackendOpGenerator; use melib::mailbox::backends::maildir::MaildirOp; +use melib::mailbox::backends::BackendOpGenerator; +use melib::mailbox::email::Envelope; extern crate test; use self::test::Bencher; diff --git a/melib/src/conf/mod.rs b/melib/src/conf/mod.rs index c955ab4c5..8d7b94426 100644 --- a/melib/src/conf/mod.rs +++ b/melib/src/conf/mod.rs @@ -20,18 +20,16 @@ */ extern crate config; -extern crate xdg; extern crate serde; +extern crate xdg; pub mod pager; - use pager::PagerSettings; - -use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; -use std::hash::Hasher; +use std::collections::HashMap; use std::fs; +use std::hash::Hasher; use std::path::{Path, PathBuf}; #[derive(Debug, Default, Clone)] @@ -64,7 +62,6 @@ impl Folder { } } - #[derive(Debug, Deserialize)] struct FileAccount { folders: String, @@ -73,7 +70,6 @@ struct FileAccount { threaded: bool, } - #[derive(Debug, Deserialize)] struct FileSettings { accounts: HashMap, @@ -104,7 +100,6 @@ pub struct Settings { pub pager: PagerSettings, } - use self::config::{Config, File, FileFormat}; impl FileSettings { pub fn new() -> FileSettings { @@ -117,7 +112,7 @@ impl FileSettings { let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml)); // TODO: Return result - s.unwrap().deserialize().unwrap() + s.unwrap().deserialize().unwrap() } } @@ -134,16 +129,20 @@ impl Settings { for f in f.iter_mut() { { let path = f.path(); - if path.ends_with("cur") || path.ends_with("new") || - path.ends_with("tmp") + if path.ends_with("cur") + || path.ends_with("new") + || path.ends_with("tmp") { continue; } if path.is_dir() { let path_children = recurse_folders(folders, &path); - folders.push(Folder::new(path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), path_children)); - children.push(folders.len()-1); - + folders.push(Folder::new( + path.to_str().unwrap().to_string(), + path.file_name().unwrap().to_str().unwrap().to_string(), + path_children, + )); + children.push(folders.len() - 1); } } } @@ -153,7 +152,11 @@ impl Settings { let path = PathBuf::from(&x.folders); let path_children = recurse_folders(&mut folders, &path); if path.is_dir() { - folders.push(Folder::new(path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), path_children)); + folders.push(Folder::new( + path.to_str().unwrap().to_string(), + path.file_name().unwrap().to_str().unwrap().to_string(), + path_children, + )); } //folders.sort_by(|a, b| b.name.cmp(&a.name)); s.insert( @@ -168,6 +171,9 @@ impl Settings { ); } - Settings { accounts: s, pager: fs.pager } + Settings { + accounts: s, + pager: fs.pager, + } } } diff --git a/melib/src/conf/pager.rs b/melib/src/conf/pager.rs index edb65c95a..f30fdbeae 100644 --- a/melib/src/conf/pager.rs +++ b/melib/src/conf/pager.rs @@ -1,15 +1,15 @@ -fn false_val () -> bool { +fn false_val() -> bool { true } -fn true_val () -> bool { +fn true_val() -> bool { true } -fn zero_val () -> usize { +fn zero_val() -> usize { 0 } -fn eighty_percent () -> usize { +fn eighty_percent() -> usize { 80 } @@ -39,12 +39,12 @@ pub struct PagerSettings { /// Default: 80 #[serde(default = "eighty_percent")] pub pager_ratio: usize, - + /// A command to pipe mail output through for viewing in pager. /// Default: None #[serde(default = "none")] pub filter: Option, - + /// Respect "format=flowed" /// Default: true #[serde(default = "true_val")] diff --git a/melib/src/error.rs b/melib/src/error.rs index ee272fbee..3391875c7 100644 --- a/melib/src/error.rs +++ b/melib/src/error.rs @@ -20,8 +20,8 @@ */ use std::error::Error; use std::fmt; -use std::result; use std::io; +use std::result; use nom; diff --git a/melib/src/lib.rs b/melib/src/lib.rs index cda927dcf..41954a68a 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -18,10 +18,9 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ -pub mod mailbox; pub mod conf; pub mod error; - +pub mod mailbox; #[macro_use] extern crate serde_derive; @@ -29,16 +28,16 @@ extern crate serde_derive; #[macro_use] extern crate nom; extern crate chrono; -extern crate memmap; -extern crate encoding; extern crate data_encoding; +extern crate encoding; +extern crate memmap; #[macro_use] extern crate bitflags; -pub use mailbox::*; pub use conf::*; +pub use mailbox::*; -pub use mailbox::backends::{RefreshEventConsumer, RefreshEvent, Backends}; +pub use error::{MeliError, Result}; +pub use mailbox::backends::{Backends, RefreshEvent, RefreshEventConsumer}; pub use mailbox::email::{Envelope, Flag}; -pub use error::{Result, MeliError}; diff --git a/melib/src/mailbox/accounts.rs b/melib/src/mailbox/accounts.rs index da642c4a4..9dc5812f3 100644 --- a/melib/src/mailbox/accounts.rs +++ b/melib/src/mailbox/accounts.rs @@ -19,9 +19,9 @@ * along with meli. If not, see . */ -use mailbox::*; -use mailbox::backends::{RefreshEventConsumer, Backends}; use conf::{AccountSettings, Folder}; +use mailbox::backends::{Backends, RefreshEventConsumer}; +use mailbox::*; use std::ops::{Index, IndexMut}; #[derive(Debug)] @@ -36,7 +36,6 @@ pub struct Account { pub backend: Box, } - impl Account { pub fn new(name: String, settings: AccountSettings, backends: &Backends) -> Self { let sent_folder = settings @@ -88,7 +87,8 @@ impl IndexMut for Account { if self.sent_folder.is_some() { let id = self.sent_folder.unwrap(); if id == index { - self.folders[index] = Some(Mailbox::new(folder, &None, self.backend.get(&folder))); + self.folders[index] = + Some(Mailbox::new(folder, &None, self.backend.get(&folder))); } else { let (sent, cur) = { let ptr = self.folders.as_mut_ptr(); @@ -104,10 +104,10 @@ impl IndexMut for Account { if sent[0].is_none() { sent[0] = Some(Mailbox::new(sent_path, &None, self.backend.get(&folder))); } - cur[0] = Some(Mailbox::new(folder, &sent[0],self.backend.get(folder))); + cur[0] = Some(Mailbox::new(folder, &sent[0], self.backend.get(folder))); } } else { - self.folders[index] = Some(Mailbox::new(folder, &None,self.backend.get(&folder))); + self.folders[index] = Some(Mailbox::new(folder, &None, self.backend.get(&folder))); }; } &mut self.folders[index] diff --git a/melib/src/mailbox/backends/imap.rs b/melib/src/mailbox/backends/imap.rs index b693bc835..ac733865d 100644 --- a/melib/src/mailbox/backends/imap.rs +++ b/melib/src/mailbox/backends/imap.rs @@ -19,21 +19,18 @@ * along with meli. If not, see . */ -use mailbox::email::{Envelope, Flag}; -use error::{Result}; -use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; use conf::Folder; - +use error::Result; +use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; +use mailbox::email::{Envelope, Flag}; /// `BackendOp` implementor for Imap #[derive(Debug, Default, Clone)] -pub struct ImapOp { -} +pub struct ImapOp {} impl ImapOp { pub fn new(_path: String) -> Self { - ImapOp { - } + ImapOp {} } } @@ -55,12 +52,9 @@ impl BackendOp for ImapOp { } } - -/// Imap backend +/// Imap backend #[derive(Debug)] -pub struct ImapType { -} - +pub struct ImapType {} impl MailBackend for ImapType { fn get(&self, _folder: &Folder) -> Result> { @@ -73,7 +67,6 @@ impl MailBackend for ImapType { impl ImapType { pub fn new(_path: &str) -> Self { - ImapType { - } + ImapType {} } } diff --git a/melib/src/mailbox/backends/maildir.rs b/melib/src/mailbox/backends/maildir.rs index 08a877502..afef36c90 100644 --- a/melib/src/mailbox/backends/maildir.rs +++ b/melib/src/mailbox/backends/maildir.rs @@ -19,15 +19,17 @@ * along with meli. If not, see . */ -use mailbox::email::{Envelope, Flag}; -use error::{MeliError, Result}; -use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer}; -use mailbox::email::parser; use conf::Folder; +use error::{MeliError, Result}; +use mailbox::backends::{ + BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer, +}; +use mailbox::email::parser; +use mailbox::email::{Envelope, Flag}; extern crate notify; -use self::notify::{Watcher, RecursiveMode, watcher, DebouncedEvent}; +use self::notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use std::time::Duration; use std::sync::mpsc::channel; @@ -36,8 +38,8 @@ use std::sync::mpsc::channel; //use std::time::Duration; use std::thread; extern crate crossbeam; -use std::path::PathBuf; use memmap::{Mmap, Protection}; +use std::path::PathBuf; /// `BackendOp` implementor for Maildir #[derive(Debug, Default)] @@ -70,9 +72,7 @@ impl BackendOp for MaildirOp { } fn as_bytes(&mut self) -> Result<&[u8]> { if self.slice.is_none() { - self.slice = Some( - Mmap::open_path(self.path.to_string(), Protection::Read)?, - ); + self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read)?); } /* Unwrap is safe since we use ? above. */ Ok(unsafe { self.slice.as_ref().unwrap().as_slice() }) @@ -104,24 +104,20 @@ impl BackendOp for MaildirOp { 'T' => flag |= Flag::TRASHED, 'D' => flag |= Flag::DRAFT, 'F' => flag |= Flag::FLAGGED, - _ => panic!(), + _ => panic!(), } - } - flag } } - /// Maildir backend https://cr.yp.to/proto/maildir.html #[derive(Debug)] pub struct MaildirType { path: String, } - impl MailBackend for MaildirType { fn get(&self, folder: &Folder) -> Result> { self.multicore(4, folder) @@ -129,34 +125,40 @@ impl MailBackend for MaildirType { fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () { let folders = folders.to_vec(); - thread::Builder::new().name("folder watch".to_string()).spawn(move || { - let (tx, rx) = channel(); - let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); - for f in folders { - if MaildirType::is_valid(&f).is_err() { - continue; - } - let mut p = PathBuf::from(&f.path()); - p.push("cur"); - watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); - p.pop(); - p.push("new"); - watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); - } - loop { - match rx.recv() { - Ok(event) => { - match event { - DebouncedEvent::Create(pathbuf) => { - sender.send(RefreshEvent { folder: format!("{}", pathbuf.parent().unwrap().to_str().unwrap()) }); - }, - _ => {}, - } + thread::Builder::new() + .name("folder watch".to_string()) + .spawn(move || { + let (tx, rx) = channel(); + let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); + for f in folders { + if MaildirType::is_valid(&f).is_err() { + continue; } - Err(e) => eprintln!("watch error: {:?}", e), + let mut p = PathBuf::from(&f.path()); + p.push("cur"); + watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); + p.pop(); + p.push("new"); + watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); } - } - }).unwrap(); + loop { + match rx.recv() { + Ok(event) => match event { + DebouncedEvent::Create(pathbuf) => { + sender.send(RefreshEvent { + folder: format!( + "{}", + pathbuf.parent().unwrap().to_str().unwrap() + ), + }); + } + _ => {} + }, + Err(e) => eprintln!("watch error: {:?}", e), + } + } + }) + .unwrap(); } } @@ -172,9 +174,10 @@ impl MaildirType { for d in &["cur", "new", "tmp"] { p.push(d); if !p.is_dir() { - return Err(MeliError::new( - format!("{} is not a valid maildir folder", path), - )); + return Err(MeliError::new(format!( + "{} is not a valid maildir folder", + path + ))); } p.pop(); } @@ -209,9 +212,10 @@ impl MaildirType { let mut local_r: Vec = Vec::with_capacity(chunk.len()); for e in chunk { let e_copy = e.to_string(); - if let Some(mut e) = Envelope::from_token(Box::new(BackendOpGenerator::new( - Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))), - ))) { + if let Some(mut e) = + Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new( + move || Box::new(MaildirOp::new(e_copy.clone())), + )))) { e.populate_headers(); local_r.push(e); } diff --git a/melib/src/mailbox/backends/mbox.rs b/melib/src/mailbox/backends/mbox.rs index 4719ab6e3..d140a0c74 100644 --- a/melib/src/mailbox/backends/mbox.rs +++ b/melib/src/mailbox/backends/mbox.rs @@ -19,23 +19,18 @@ * along with meli. If not, see . */ -use mailbox::email::{Envelope, Flag}; -use error::{Result}; -use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; use conf::Folder; - - - +use error::Result; +use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer}; +use mailbox::email::{Envelope, Flag}; /// `BackendOp` implementor for Mbox #[derive(Debug, Default, Clone)] -pub struct MboxOp { -} +pub struct MboxOp {} impl MboxOp { pub fn new(_path: String) -> Self { - MboxOp { - } + MboxOp {} } } @@ -57,12 +52,9 @@ impl BackendOp for MboxOp { } } - -/// Mbox backend +/// Mbox backend #[derive(Debug)] -pub struct MboxType { -} - +pub struct MboxType {} impl MailBackend for MboxType { fn get(&self, _folder: &Folder) -> Result> { @@ -75,7 +67,6 @@ impl MailBackend for MboxType { impl MboxType { pub fn new(_path: &str) -> Self { - MboxType { - } + MboxType {} } } diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index 7cc2a963b..744f23123 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -18,19 +18,18 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +pub mod imap; pub mod maildir; pub mod mbox; -pub mod imap; use conf::Folder; -use mailbox::email::{Envelope, Flag}; +use error::Result; +use mailbox::backends::imap::ImapType; use mailbox::backends::maildir::MaildirType; use mailbox::backends::mbox::MboxType; -use mailbox::backends::imap::ImapType; -use error::Result; +use mailbox::email::{Envelope, Flag}; use std::fmt; - extern crate fnv; use self::fnv::FnvHashMap; use std; @@ -44,9 +43,12 @@ pub struct Backends { impl Backends { pub fn new() -> Self { let mut b = Backends { - map: FnvHashMap::with_capacity_and_hasher(1, Default::default()) + map: FnvHashMap::with_capacity_and_hasher(1, Default::default()), }; - b.register("maildir".to_string(), Box::new(|| Box::new(MaildirType::new("")))); + b.register( + "maildir".to_string(), + Box::new(|| Box::new(MaildirType::new(""))), + ); b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new("")))); b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new("")))); b @@ -66,7 +68,6 @@ impl Backends { } } - pub struct RefreshEvent { pub folder: String, } @@ -87,7 +88,7 @@ impl RefreshEventConsumer { } pub trait MailBackend: ::std::fmt::Debug { fn get(&self, folder: &Folder) -> Result>; - fn watch(&self, sender:RefreshEventConsumer, folders: &[Folder]) -> (); + fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> (); //fn new(folders: &Vec) -> Box; //login function } diff --git a/melib/src/mailbox/email/attachments.rs b/melib/src/mailbox/email/attachments.rs index b890ee792..66a2d6aa2 100644 --- a/melib/src/mailbox/email/attachments.rs +++ b/melib/src/mailbox/email/attachments.rs @@ -40,60 +40,39 @@ pub enum MultipartType { Unsupported { tag: String }, } - impl Display for MultipartType { fn fmt(&self, f: &mut Formatter) -> FmtResult { match self { - MultipartType::Mixed => { - write!(f, "multipart/mixed") - }, - MultipartType::Alternative => { - write!(f, "multipart/alternative") - }, - MultipartType::Digest => { - write!(f, "multipart/digest") - }, - MultipartType::Unsupported { tag: ref t } => { - write!(f, "multipart/{}", t) - }, + MultipartType::Mixed => write!(f, "multipart/mixed"), + MultipartType::Alternative => write!(f, "multipart/alternative"), + MultipartType::Digest => write!(f, "multipart/digest"), + MultipartType::Unsupported { tag: ref t } => write!(f, "multipart/{}", t), } } } - #[derive(Clone, Debug)] pub enum AttachmentType { - Data { tag: String }, - Text { content: String }, + Data { + tag: String, + }, + Text { + content: String, + }, Multipart { of_type: MultipartType, subattachments: Vec, }, } - impl Display for AttachmentType { fn fmt(&self, f: &mut Formatter) -> FmtResult { match self { - AttachmentType::Data { tag: ref t } => { - write!(f, "{}", t) - }, - AttachmentType::Text { content: ref c } => { - write!(f, "{}", c) - }, - AttachmentType::Multipart { of_type: ref t, .. } => { - write!(f, "{}", t) - - }, - - + AttachmentType::Data { tag: ref t } => write!(f, "{}", t), + AttachmentType::Text { content: ref c } => write!(f, "{}", c), + AttachmentType::Multipart { of_type: ref t, .. } => write!(f, "{}", t), } - - } - - - } #[derive(Clone, Debug)] pub enum ContentType { @@ -207,32 +186,33 @@ impl AttachmentBuilder { fn decode(&self) -> String { // TODO: Use charset for decoding match self.content_transfer_encoding { - ContentTransferEncoding::Base64 => { - match BASE64_MIME.decode(str::from_utf8(&self.raw) + ContentTransferEncoding::Base64 => match BASE64_MIME.decode( + str::from_utf8(&self.raw) .unwrap() .trim() .lines() .fold(String::with_capacity(self.raw.len()), |mut acc, x| { acc.push_str(x); acc - }).as_bytes()) { - Ok(ref v) => { - let s = String::from_utf8_lossy(v); - if s.find("\r\n").is_some() { - s.replace("\r\n", "\n") - } else { - s.into_owned() - } + }) + .as_bytes(), + ) { + Ok(ref v) => { + let s = String::from_utf8_lossy(v); + if s.find("\r\n").is_some() { + s.replace("\r\n", "\n") + } else { + s.into_owned() } - _ => String::from_utf8_lossy(&self.raw).into_owned(), } - } + _ => String::from_utf8_lossy(&self.raw).into_owned(), + }, ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw) - .to_full_result() - .unwrap(), - ContentTransferEncoding::_7Bit | - ContentTransferEncoding::_8Bit | - ContentTransferEncoding::Other { .. } => { + .to_full_result() + .unwrap(), + ContentTransferEncoding::_7Bit + | ContentTransferEncoding::_8Bit + | ContentTransferEncoding::Other { .. } => { String::from_utf8_lossy(&self.raw).into_owned() } } @@ -310,7 +290,6 @@ impl AttachmentBuilder { } } - #[derive(Clone, Debug)] pub struct Attachment { content_type: (ContentType, ContentSubType), @@ -327,19 +306,20 @@ impl Display for Attachment { AttachmentType::Data { .. } => { write!(f, "Data attachment of type {}", self.mime_type()) } - AttachmentType::Text { .. } => { - write!(f, "Text attachment") - } + AttachmentType::Text { .. } => write!(f, "Text attachment"), AttachmentType::Multipart { of_type: ref multipart_type, subattachments: ref sub_att_vec, } => if *multipart_type == MultipartType::Alternative { - write!(f, "Multipart/alternative attachment with {} subs", sub_att_vec.len()) + write!( + f, + "Multipart/alternative attachment with {} subs", + sub_att_vec.len() + ) } else { write!(f, "Multipart attachment with {} subs", sub_att_vec.len()) }, } - } } @@ -388,9 +368,7 @@ impl Attachment { let mut ret = Vec::new(); fn count_recursive(att: &Attachment, ret: &mut Vec) { match att.attachment_type { - AttachmentType::Data { .. } | AttachmentType::Text { .. } => { - ret.push(att.clone()) - } + AttachmentType::Data { .. } | AttachmentType::Text { .. } => ret.push(att.clone()), AttachmentType::Multipart { of_type: ref multipart_type, subattachments: ref sub_att_vec, @@ -405,8 +383,6 @@ impl Attachment { count_recursive(&self, &mut ret); ret - - } pub fn count_attachments(&self) -> usize { self.attachments().len() @@ -422,7 +398,6 @@ impl Attachment { } } - pub fn interpret_format_flowed(_t: &str) -> String { //let mut n = String::with_capacity(t.len()); unimplemented!() @@ -431,19 +406,15 @@ pub fn interpret_format_flowed(_t: &str) -> String { pub fn decode(a: &Attachment) -> Vec { // TODO: Use charset for decoding match a.content_transfer_encoding { - ContentTransferEncoding::Base64 => { - match BASE64_MIME.decode(a.bytes()) { - Ok(v) => { - v - } - _ => a.bytes().to_vec(), - } - } - ContentTransferEncoding::QuotedPrintable => parser::quoted_printed_bytes(&a.bytes()).to_full_result() .unwrap(), - ContentTransferEncoding::_7Bit | - ContentTransferEncoding::_8Bit | - ContentTransferEncoding::Other { .. } => { - a.bytes().to_vec() - } + ContentTransferEncoding::Base64 => match BASE64_MIME.decode(a.bytes()) { + Ok(v) => v, + _ => a.bytes().to_vec(), + }, + ContentTransferEncoding::QuotedPrintable => parser::quoted_printed_bytes(&a.bytes()) + .to_full_result() + .unwrap(), + ContentTransferEncoding::_7Bit + | ContentTransferEncoding::_8Bit + | ContentTransferEncoding::Other { .. } => a.bytes().to_vec(), } } diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index bcc0d8e76..81979a87c 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -19,38 +19,36 @@ * along with meli. If not, see . */ -pub mod parser; pub mod attachments; +pub mod parser; -use mailbox::backends::BackendOpGenerator; pub use self::attachments::*; +use mailbox::backends::BackendOpGenerator; -use std::string::String; -use std::sync::Arc; use std::cmp::Ordering; use std::fmt; use std::option::Option; +use std::string::String; +use std::sync::Arc; use chrono; use chrono::TimeZone; - - -#[derive(Clone, Debug, )] +#[derive(Clone, Debug)] pub struct GroupAddress { raw: String, display_name: StrBuilder, mailbox_list: Vec
, } -#[derive(Clone, Debug, )] +#[derive(Clone, Debug)] pub struct MailboxAddress { raw: String, display_name: StrBuilder, address_spec: StrBuilder, } -#[derive(Clone, Debug, )] +#[derive(Clone, Debug)] pub enum Address { Mailbox(MailboxAddress), Group(GroupAddress), @@ -60,17 +58,19 @@ impl Eq for Address {} impl PartialEq for Address { fn eq(&self, other: &Address) -> bool { match (self, other) { - (Address::Mailbox(_), Address::Group(_)) | - (Address::Group(_), Address::Mailbox(_)) => { + (Address::Mailbox(_), Address::Group(_)) | (Address::Group(_), Address::Mailbox(_)) => { false - }, + } (Address::Mailbox(s), Address::Mailbox(o)) => { s.address_spec.display(&s.raw) == o.address_spec.display(&o.raw) - }, + } (Address::Group(s), Address::Group(o)) => { - s.display_name.display(&s.raw) == o.display_name.display(&o.raw) && - s.mailbox_list.iter().zip(o.mailbox_list.iter()).fold(true, |b, (s, o)| b && (s == o)) - }, + s.display_name.display(&s.raw) == o.display_name.display(&o.raw) + && s.mailbox_list + .iter() + .zip(o.mailbox_list.iter()) + .fold(true, |b, (s, o)| b && (s == o)) + } } } } @@ -78,25 +78,27 @@ impl PartialEq for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Address::Mailbox(m) if m.display_name.length > 0 => { - write!(f, "{} <{}>", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)) - }, - Address::Group(g) => { - let attachment_strings: Vec = g.mailbox_list.iter().map(|a| format!("{}", a)).collect(); - write!(f, "{}: {}", g.display_name.display(&g.raw), attachment_strings.join(", ")) - }, - Address::Mailbox(m) => { - write!(f, "{}", m.address_spec.display(&m.raw)) - }, + Address::Mailbox(m) if m.display_name.length > 0 => write!( + f, + "{} <{}>", + m.display_name.display(&m.raw), + m.address_spec.display(&m.raw) + ), + Address::Group(g) => { + let attachment_strings: Vec = + g.mailbox_list.iter().map(|a| format!("{}", a)).collect(); + write!( + f, + "{}: {}", + g.display_name.display(&g.raw), + attachment_strings.join(", ") + ) + } + Address::Mailbox(m) => write!(f, "{}", m.address_spec.display(&m.raw)), } - } - - } - - /// Helper struct to return slices from a struct field on demand. #[derive(Clone, Debug)] struct StrBuilder { @@ -118,7 +120,7 @@ impl StrBuilder { fn display<'a>(&self, s: &'a str) -> &'a str { let offset = self.offset; let length = self.length; - &s[offset..offset+length] + &s[offset..offset + length] } } @@ -133,14 +135,14 @@ impl StrBuild for MessageID { string.to_string(), StrBuilder { offset: offset, - length: slice.len()+ 1, + length: slice.len() + 1, }, - ) + ) } fn raw(&self) -> &str { let offset = self.1.offset; let length = self.1.length; - &self.0[offset..offset+length-1] + &self.0[offset..offset + length - 1] } fn val(&self) -> &str { &self.0 @@ -159,8 +161,8 @@ fn test_strbuilder() { offset: 1, length: 43, } - ) - ); + ) + ); } impl PartialEq for MessageID { @@ -180,7 +182,6 @@ struct References { refs: Vec, } - bitflags! { #[derive(Default)] pub struct Flag: u8 { @@ -220,7 +221,6 @@ pub struct Envelope { flags: Flag, } - impl Envelope { pub fn new(token: Box) -> Self { Envelope { @@ -349,7 +349,7 @@ impl Envelope { } pub fn to(&self) -> &Vec
{ &self.to - } + } pub fn to_to_string(&self) -> String { let _strings: Vec = self.to.iter().map(|a| format!("{}", a)).collect(); _strings.join(", ") @@ -511,6 +511,10 @@ impl Envelope { self.datetime = Some(new_val); self.timestamp = new_val.timestamp() as u64; } + pub fn set_flag(&mut self, f: Flag) -> () { + self.flags |= f; + + } pub fn flags(&self) -> Flag { self.flags } diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index b8fa3951e..b05e7b321 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -18,23 +18,23 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +use super::*; +use chrono; +use data_encoding::BASE64_MIME; +use encoding::{DecoderTrap, Encoding}; +use nom::{is_hex_digit, le_u8}; +use nom::{Compare, CompareResult}; +use nom::{ErrorKind, IResult, Needed}; use std; use std::str::from_utf8; -use data_encoding::BASE64_MIME; -use chrono; -use nom::{is_hex_digit, le_u8}; -use nom::{ErrorKind, IResult, Needed}; -use nom::{Compare, CompareResult}; -use encoding::{DecoderTrap, Encoding}; -use super::*; macro_rules! is_whitespace { - ($var:ident) => ( + ($var:ident) => { $var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r' - ); - ($var:expr) => ( + }; + ($var:expr) => { $var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r' - ); + }; } fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> { @@ -71,7 +71,6 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> { * Tue, 5 Jan 2016 21:30:44 +0100 (CET) */ - fn header_value(input: &[u8]) -> IResult<&[u8], &str> { if input.is_empty() || input[0] == b'\n' { IResult::Incomplete(Needed::Unknown) @@ -79,8 +78,7 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> { 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' - { + if (i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t' { return match from_utf8(&input[0..i]) { Ok(v) => IResult::Done(&input[(i + 1)..], v), Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))), @@ -97,7 +95,6 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> { } } - /* Parse the name part of the header -> &str */ named!(name<&str>, map_res!(is_not!(":\n"), from_utf8)); @@ -111,15 +108,15 @@ named!(pub headers>, many1!(complete!(header))); //named!(pub headers_raw<&[u8]>, - //take_until1!("\n\n")); +//take_until1!("\n\n")); pub fn headers_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { if input.is_empty() { - return IResult::Incomplete(Needed::Unknown) + return IResult::Incomplete(Needed::Unknown); } for (i, x) in input.iter().enumerate() { - if *x == b'\n' && i + 1 < input.len() && input[i+1] == b'\n' { - return IResult::Done(&input[(i + 1)..], &input[0..i+1]); + if *x == b'\n' && i + 1 < input.len() && input[i + 1] == b'\n' { + return IResult::Done(&input[(i + 1)..], &input[0..i + 1]); } } return IResult::Error(error_code!(ErrorKind::Custom(43))); @@ -131,7 +128,6 @@ named!(pub body_raw<&[u8]>, body: take_while!(call!(|_| true)) >> ( { body } ))); - named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>, separated_pair!(headers, tag!("\n"), take_while!(call!(|_| true)))); named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>, @@ -255,19 +251,32 @@ named!( )) ); -named!(encoded_word_list, ws!(do_parse!( - list: separated_nonempty_list!(complete!(is_a!(" \n\r\t")), encoded_word) >> - ( { - let list_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc }); - let bytes = list.iter().fold(Vec::with_capacity(list_len), |mut acc, x| { acc.append(&mut x.clone()); acc}); +named!( + encoded_word_list, + ws!(do_parse!( + list: separated_nonempty_list!(complete!(is_a!(" \n\r\t")), encoded_word) >> ({ + let list_len = list.iter().fold(0, |mut acc, x| { + acc += x.len(); + acc + }); + let bytes = list.iter() + .fold(Vec::with_capacity(list_len), |mut acc, x| { + acc.append(&mut x.clone()); + acc + }); String::from_utf8_lossy(&bytes).into_owned() - } ) - ))); -named!(ascii_token, do_parse!( - word: alt!(terminated!(take_until1!("=?"), peek!(tag_no_case!("=?UTF-8?"))) | take_while!(call!(|_| { true }))) >> - ( { - String::from_utf8_lossy(word).into_owned() - } ))); + }) + )) +); +named!( + ascii_token, + do_parse!( + word: alt!( + terminated!(take_until1!("=?"), peek!(tag_no_case!("=?UTF-8?"))) + | take_while!(call!(|_| true)) + ) >> ({ String::from_utf8_lossy(word).into_owned() }) + ) +); fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { if input.is_empty() || input.len() < 3 { @@ -280,8 +289,7 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { let mut flag = false; for (i, b) in input[0..].iter().enumerate() { if *b == b'<' { - - display_name.length = if i != 0 { i-1 } else { 0 }; + display_name.length = if i != 0 { i - 1 } else { 0 }; flag = true; break; } @@ -291,7 +299,7 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { } let mut end = input.len(); let mut flag = false; - for (i, b) in input[display_name.length+2..].iter().enumerate() { + for (i, b) in input[display_name.length + 2..].iter().enumerate() { if *b == b'@' { flag = true; } @@ -305,19 +313,22 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { offset: display_name.length + 2, length: end, }; - match phrase(&input[0..end+display_name.length+3]) { + match phrase(&input[0..end + display_name.length + 3]) { IResult::Error(e) => IResult::Error(e), IResult::Incomplete(i) => IResult::Incomplete(i), IResult::Done(rest, raw) => { display_name.length = raw.find('<').unwrap(); address_spec.offset = display_name.length + 1; address_spec.length = raw.len() - display_name.length - 2; - IResult::Done(rest, Address::Mailbox(MailboxAddress { - raw: raw, - display_name: display_name, - address_spec: address_spec, - })) - }, + IResult::Done( + rest, + Address::Mailbox(MailboxAddress { + raw: raw, + display_name: display_name, + address_spec: address_spec, + }), + ) + } } } else { IResult::Error(error_code!(ErrorKind::Custom(43))) @@ -325,11 +336,8 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - } - - fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> { if input.is_empty() || input.len() < 3 { IResult::Incomplete(Needed::Size(1)) @@ -346,17 +354,20 @@ fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> { } } if flag { - IResult::Done(&input[end..], Address::Mailbox(MailboxAddress { - raw: String::from_utf8_lossy(&input[0..end+1]).to_string(), - display_name: StrBuilder { - offset: 0, - length: 0, - }, - address_spec: StrBuilder { - offset: 0, - length: input[0..end+1].len(), - }, - })) + IResult::Done( + &input[end..], + Address::Mailbox(MailboxAddress { + raw: String::from_utf8_lossy(&input[0..end + 1]).to_string(), + display_name: StrBuilder { + offset: 0, + length: 0, + }, + address_spec: StrBuilder { + offset: 0, + length: input[0..end + 1].len(), + }, + }), + ) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } @@ -365,39 +376,43 @@ fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> { } } - -named!(mailbox
, ws!(alt_complete!( - display_addr | - addr_spec - ))); +named!( + mailbox
, + ws!(alt_complete!(display_addr | addr_spec)) +); named!(mailbox_list>, many0!(mailbox)); - #[test] fn test_mailbox() { { - let s = b"epilys@postretch"; - let r = mailbox(s).unwrap().1; - match r { - Address::Mailbox(ref m) => { - println!("----\n`{}`, `{}`\n----", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)); - }, - _ => {}, - } + let s = b"epilys@postretch"; + let r = mailbox(s).unwrap().1; + match r { + Address::Mailbox(ref m) => { + println!( + "----\n`{}`, `{}`\n----", + m.display_name.display(&m.raw), + m.address_spec.display(&m.raw) + ); + } + _ => {} + } } let s = b"Manos "; eprintln!("{:?}", display_addr(s).unwrap()); let r = display_addr(s).unwrap().1; match r { Address::Mailbox(ref m) => { - println!("----\n`{}`, `{}`\n----", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)); - }, - _ => {}, + println!( + "----\n`{}`, `{}`\n----", + m.display_name.display(&m.raw), + m.address_spec.display(&m.raw) + ); + } + _ => {} } - } - //named!(group_t, ws!( do_parse!( // display_name: take_until1!(":") >> // mailbox_list: many0!(mailbox) >> @@ -427,28 +442,26 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> { return IResult::Error(e); } IResult::Done(rest, vec) => { - let size: usize = (rest.as_ptr() as usize) - ((&input[0..] as &[u8]).as_ptr() as usize); - return IResult::Done(rest, Address::Group(GroupAddress { - raw: String::from_utf8(input[0..size].to_vec()).unwrap(), - display_name: StrBuilder { - offset: 0, - length: dlength, - }, - mailbox_list: vec, - })); - }, + let size: usize = (rest.as_ptr() as usize) - ((&input[0..] as &[u8]).as_ptr() as usize); + return IResult::Done( + rest, + Address::Group(GroupAddress { + raw: String::from_utf8(input[0..size].to_vec()).unwrap(), + display_name: StrBuilder { + offset: 0, + length: dlength, + }, + mailbox_list: vec, + }), + ); + } IResult::Incomplete(i) => { return IResult::Incomplete(i); - }, + } } } - - - - -named!(address
, ws!( - alt_complete!(mailbox | group))); +named!(address
, ws!(alt_complete!(mailbox | group))); #[test] fn test_address() { @@ -456,18 +469,14 @@ fn test_address() { qemu-devel , qemu-block , Alberto Garcia , Stefan Hajnoczi "; println!("{:?}", rfc2822address_list(s).unwrap()); - } - - named!(pub rfc2822address_list>, ws!( separated_list!(is_a!(","), address) )); - named!(pub address_list, ws!(do_parse!( list: alt_complete!( encoded_word_list | ascii_token) >> ( { @@ -677,18 +686,17 @@ fn test_attachments() { named!( content_type_parameter<(&str, &str)>, do_parse!( - tag!(";") >> - name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("=")) >> - value: map_res!(ws!( - alt_complete!(delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";"))), - from_utf8) >> - ( { - (name, value) - } ) - ) + tag!(";") >> name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("=")) + >> value: + map_res!( + ws!(alt_complete!( + delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";") + )), + from_utf8 + ) >> ({ (name, value) }) + ) ); - named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >, do_parse!( _type: map_res!(take_until!("/"), from_utf8) >> @@ -700,7 +708,6 @@ named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >, } ) )); - named!(pub quoted_printable_text, do_parse!( bytes: many0!(alt_complete!( diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index c4ae98734..720cbe516 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -23,8 +23,8 @@ pub mod email; pub use self::email::*; /* Mail backends. Currently only maildir is supported */ pub mod backends; -use mailbox::backends::MailBackend; use error::Result; +use mailbox::backends::MailBackend; pub mod accounts; pub use mailbox::accounts::Account; pub mod thread; @@ -34,7 +34,6 @@ use conf::Folder; use std::option::Option; - /// `Mailbox` represents a folder of mail. #[derive(Debug, Clone)] pub struct Mailbox { @@ -44,7 +43,6 @@ pub struct Mailbox { threads: Vec, } - impl Mailbox { pub fn new_dummy() -> Self { Mailbox { @@ -54,7 +52,11 @@ impl Mailbox { threads: Vec::with_capacity(0), } } - pub fn new(folder: &Folder, sent_folder: &Option>, collection: Result>) -> Result { + pub fn new( + folder: &Folder, + sent_folder: &Option>, + collection: Result>, + ) -> Result { let mut collection: Vec = collection?; collection.sort_by(|a, b| a.date().cmp(&b.date())); let (threads, threaded_collection) = build_threads(&mut collection, sent_folder); diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index ea85a7712..f8b0344ed 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -19,9 +19,9 @@ * along with meli. If not, see . */ +use error::Result; use mailbox::email::*; use mailbox::Mailbox; -use error::Result; extern crate fnv; use self::fnv::FnvHashMap; @@ -172,8 +172,8 @@ fn build_collection( iasf += 1; let parent_id = if id_table.contains_key(r.raw()) { let p = id_table[r.raw()]; - if !(threads[p].is_descendant(threads, &threads[curr_ref]) || - threads[curr_ref].is_descendant(threads, &threads[p])) + if !(threads[p].is_descendant(threads, &threads[curr_ref]) + || threads[curr_ref].is_descendant(threads, &threads[p])) { threads[curr_ref].parent = Some(p); if threads[p].first_child.is_none() { @@ -228,7 +228,6 @@ fn build_collection( } } - /// Builds threads from a collection. pub fn build_threads( collection: &mut Vec, @@ -262,9 +261,9 @@ pub fn build_threads( let sent_mailbox = sent_mailbox.unwrap(); for x in &sent_mailbox.collection { - if id_table.contains_key(x.message_id_raw()) || - (!x.in_reply_to_raw().is_empty() && - id_table.contains_key(x.in_reply_to_raw())) + if id_table.contains_key(x.message_id_raw()) + || (!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(x.message_id_raw()) { @@ -277,8 +276,8 @@ pub fn build_threads( 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()) + } else if !x.in_reply_to_raw().is_empty() + && id_table.contains_key(x.in_reply_to_raw()) { let p = id_table[x.in_reply_to_raw()]; let c = if id_table.contains_key(x.message_id_raw()) { @@ -300,8 +299,8 @@ pub fn build_threads( tidx - 1 }; threads[c].parent = Some(p); - if threads[p].is_descendant(&threads, &threads[c]) || - threads[c].is_descendant(&threads, &threads[p]) + if threads[p].is_descendant(&threads, &threads[c]) + || threads[c].is_descendant(&threads, &threads[p]) { continue; } @@ -344,8 +343,9 @@ pub fn build_threads( let mut root_set = Vec::with_capacity(collection.len()); 'root_set: for v in id_table.values() { if threads[*v].parent.is_none() { - if !threads[*v].has_message() && threads[*v].has_children() && - !threads[threads[*v].first_child.unwrap()].has_sibling() + 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. */ @@ -369,15 +369,14 @@ pub fn build_threads( ) { let thread = threads[i]; if threads[root_subject_idx].has_message() { - let root_subject = - collection[threads[root_subject_idx].message().unwrap()].subject(); + let root_subject = collection[threads[root_subject_idx].message().unwrap()].subject(); /* If the Container has no Message, but does have children, remove this container but * promote its children to this level (that is, splice them in to the current child * list.) */ if indentation > 0 && thread.has_message() { let subject = collection[thread.message().unwrap()].subject(); - if subject == root_subject || - subject.starts_with("Re: ") && subject.ends_with(root_subject) + if subject == root_subject + || subject.starts_with("Re: ") && subject.ends_with(root_subject) { threads[i].set_show_subject(false); } @@ -420,6 +419,5 @@ pub fn build_threads( ); } - (threads, threaded_collection) } diff --git a/src/bin.rs b/src/bin.rs index 205ad16b6..3a4603bb2 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -27,8 +27,8 @@ The mail handling stuff is done in the `melib` crate which includes all backend extern crate melib; extern crate ui; -use ui::*; pub use melib::*; +use ui::*; use std::thread; @@ -40,17 +40,28 @@ use chan_signal::Signal; extern crate nix; -fn make_input_thread(sx: chan::Sender, rx: chan::Receiver) -> thread::JoinHandle<()> { - let stdin = std::io::stdin(); - thread::Builder::new().name("input-thread".to_string()).spawn(move || { - - get_events(stdin, - |k| { - sx.send(ThreadEvent::Input(k)); - }, - || { - sx.send(ThreadEvent::UIEventType(UIEventType::ChangeMode(UIMode::Fork))); - }, rx)}).unwrap() +fn make_input_thread( + sx: chan::Sender, + rx: chan::Receiver, +) -> thread::JoinHandle<()> { + let stdin = std::io::stdin(); + thread::Builder::new() + .name("input-thread".to_string()) + .spawn(move || { + get_events( + stdin, + |k| { + sx.send(ThreadEvent::Input(k)); + }, + || { + sx.send(ThreadEvent::UIEventType(UIEventType::ChangeMode( + UIMode::Fork, + ))); + }, + rx, + ) + }) + .unwrap() } fn main() { /* Lock all stdio outs */ @@ -68,7 +79,6 @@ fn main() { * */ let (sender, receiver) = chan::sync(::std::mem::size_of::()); - /* * Create async channel to block the input-thread if we need to fork and stop it from reading * stdin, see get_events() for details @@ -78,18 +88,27 @@ fn main() { let mut _thread_handler = make_input_thread(sender.clone(), rx.clone()); /* Create the application State. This is the 'System' part of an ECS architecture */ - let mut state = State::new(sender.clone(), tx ); + let mut state = State::new(sender.clone(), tx); /* Register some reasonably useful interfaces */ - let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) }; + let menu = Entity { + component: Box::new(AccountMenu::new(&state.context.accounts)), + }; let listing = MailListing::new(); - let b = Entity { component: Box::new(listing) }; - let window = Entity { component: Box::new(VSplit::new(menu, b, 90, true)) }; - let status_bar = Entity { component: Box::new(StatusBar::new(window)) }; + let b = Entity { + component: Box::new(listing), + }; + let window = Entity { + component: Box::new(VSplit::new(menu, b, 90, true)), + }; + let status_bar = Entity { + component: Box::new(StatusBar::new(window)), + }; state.register_entity(status_bar); - - let xdg_notifications = Entity { component: Box::new(ui::components::notifications::XDGNotifications {}) }; + let xdg_notifications = Entity { + component: Box::new(ui::components::notifications::XDGNotifications {}), + }; state.register_entity(xdg_notifications); /* Keep track of the input mode. See ui::UIMode for details */ @@ -191,18 +210,18 @@ fn main() { make_input_thread(sender.clone(), rx.clone()); state.mode = UIMode::Normal; state.render(); - }, + } Some(false) => { use std::{thread, time}; let ten_millis = time::Duration::from_millis(1500); thread::sleep(ten_millis); continue 'reap; - }, - None => {break 'reap;}, + } + None => { + break 'reap; + } } - - } } } diff --git a/ui/src/cells.rs b/ui/src/cells.rs index d17e9ed87..fb0e85d27 100644 --- a/ui/src/cells.rs +++ b/ui/src/cells.rs @@ -2,13 +2,12 @@ Define a (x, y) point in the terminal display as a holder of a character, foreground/background colors and attributes. */ -use std::ops::{Index, IndexMut, Deref, DerefMut}; +use super::position::*; use std::convert::From; use std::fmt; -use super::position::*; +use std::ops::{Deref, DerefMut, Index, IndexMut}; use termion::color::AnsiValue; - /// Types and implementations taken from rustty for convenience. pub trait CellAccessor: HasSize { @@ -180,7 +179,7 @@ impl fmt::Display for CellBuffer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { '_y: for y in 0..self.rows { '_x: for x in 0..self.cols { - let c: &char = &self[(x,y)].ch(); + let c: &char = &self[(x, y)].ch(); write!(f, "{}", *c).unwrap(); if *c == '\n' { continue '_y; @@ -444,15 +443,17 @@ impl Color { pub fn as_termion(&self) -> AnsiValue { match *self { - b @ Color::Black | b @ Color::Red | b @ Color::Green | b @ Color::Yellow | b @ Color::Blue | b @ Color::Magenta | b @ Color::Cyan | b @ Color::White | b @ Color::Default => - { - AnsiValue(b.as_byte()) - }, - Color::Byte(b) => { - AnsiValue(b as u8) - }, + b @ Color::Black + | b @ Color::Red + | b @ Color::Green + | b @ Color::Yellow + | b @ Color::Blue + | b @ Color::Magenta + | b @ Color::Cyan + | b @ Color::White + | b @ Color::Default => AnsiValue(b.as_byte()), + Color::Byte(b) => AnsiValue(b as u8), } - } } diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index b04a51ef3..52f6df9a1 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -27,7 +27,12 @@ impl MailListing { /// Helper function to format entry strings for MailListing */ /* TODO: Make this configurable */ fn make_entry_string(e: &Envelope, idx: usize) -> String { - format!("{} {} {:.85}",idx,&e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.subject()) + format!( + "{} {} {:.85}", + idx, + &e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(), + e.subject() + ) } pub fn new() -> Self { @@ -51,42 +56,52 @@ impl MailListing { self.cursor_pos.1 = self.new_cursor_pos.1; self.cursor_pos.0 = self.new_cursor_pos.0; - let threaded = context.accounts[self.cursor_pos.0].runtime_settings.threaded; + let threaded = context.accounts[self.cursor_pos.0] + .runtime_settings + .threaded; // Get mailbox as a reference. - let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap(); + let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); // Inform State that we changed the current folder view. - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)), + }); self.length = if threaded { mailbox.threaded_collection.len() } else { mailbox.len() }; - let mut content = CellBuffer::new(MAX_COLS, self.length+1, Cell::with_char(' ')); + let mut content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); if self.length == 0 { - write_string_to_grid(&format!("Folder `{}` is empty.", - mailbox.folder.name()), - &mut content, - Color::Default, - Color::Default, - ((0, 0), (MAX_COLS-1, 0)), - true); + write_string_to_grid( + &format!("Folder `{}` is empty.", mailbox.folder.name()), + &mut content, + Color::Default, + Color::Default, + ((0, 0), (MAX_COLS - 1, 0)), + true, + ); self.content = content; return; } - // TODO: Fix the threaded hell and refactor stuff into seperate functions and/or modules. if threaded { let mut indentations: Vec = Vec::with_capacity(6); let mut thread_idx = 0; // needed for alternate thread colors - /* Draw threaded view. */ - let mut iter = mailbox + /* Draw threaded view. */ + let mut iter = mailbox.threaded_collection.iter().enumerate().peekable(); + let len = mailbox .threaded_collection - .iter() - .enumerate() - .peekable(); - let len = mailbox.threaded_collection.len().to_string().chars().count(); + .len() + .to_string() + .chars() + .count(); /* This is just a desugared for loop so that we can use .peek() */ while let Some((idx, i)) = iter.next() { let container = mailbox.thread(*i); @@ -98,12 +113,10 @@ impl MailListing { assert_eq!(container.has_message(), true); match iter.peek() { - Some(&(_, x)) - if mailbox.thread(*x).indentation() == indentation => - { - indentations.pop(); - indentations.push(true); - } + Some(&(_, x)) if mailbox.thread(*x).indentation() == indentation => { + indentations.pop(); + indentations.push(true); + } _ => { indentations.pop(); indentations.push(false); @@ -113,7 +126,7 @@ impl MailListing { indentations.pop(); indentations.push(true); } - let envelope : &Envelope = &mailbox.collection[container.message().unwrap()]; + let envelope: &Envelope = &mailbox.collection[container.message().unwrap()]; let fg_color = if !envelope.is_seen() { Color::Byte(0) } else { @@ -126,43 +139,46 @@ impl MailListing { } else { Color::Default }; - let (x, _) = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations, len), - &mut content, - fg_color, - bg_color, - ((0, idx) , (MAX_COLS-1, idx)), - false); + let (x, _) = write_string_to_grid( + &MailListing::make_thread_entry( + envelope, + idx, + indentation, + container, + &indentations, + len, + ), + &mut content, + fg_color, + bg_color, + ((0, idx), (MAX_COLS - 1, idx)), + false, + ); for x in x..MAX_COLS { - content[(x,idx)].set_ch(' '); - content[(x,idx)].set_bg(bg_color); + content[(x, idx)].set_ch(' '); + content[(x, idx)].set_bg(bg_color); } match iter.peek() { - Some(&(_, x)) - if mailbox.thread(*x).indentation() > indentation => - { - indentations.push(false); - } - Some(&(_, x)) - if mailbox.thread(*x).indentation() < indentation => - { - for _ in 0..(indentation - mailbox.thread(*x).indentation()) { - indentations.pop(); - } + Some(&(_, x)) if mailbox.thread(*x).indentation() > indentation => { + indentations.push(false); + } + Some(&(_, x)) if mailbox.thread(*x).indentation() < indentation => { + for _ in 0..(indentation - mailbox.thread(*x).indentation()) { + indentations.pop(); } + } _ => {} } } } else { - // Populate `CellBuffer` with every entry. // TODO: Lazy load? let mut idx = 0; for y in 0..=self.length { if idx >= self.length { /* No more entries left, so fill the rest of the area with empty space */ - clear_area(&mut content, - ((0, y), (MAX_COLS-1, self.length))); + clear_area(&mut content, ((0, y), (MAX_COLS - 1, self.length))); break; } /* Write an entire line for each envelope entry. */ @@ -180,28 +196,35 @@ impl MailListing { } else { Color::Default }; - let (x, y)= write_string_to_grid(&MailListing::make_entry_string(envelope, idx), - &mut content, - fg_color, - bg_color, - ((0, y) , (MAX_COLS-1, y)), - false); + let (x, y) = write_string_to_grid( + &MailListing::make_entry_string(envelope, idx), + &mut content, + fg_color, + bg_color, + ((0, y), (MAX_COLS - 1, y)), + false, + ); for x in x..MAX_COLS { - content[(x,y)].set_ch(' '); - content[(x,y)].set_bg(bg_color); + content[(x, y)].set_ch(' '); + content[(x, y)].set_bg(bg_color); } - idx+=1; + idx += 1; } - } self.content = content; } fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) { - let threaded = context.accounts[self.cursor_pos.0].runtime_settings.threaded; - let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap(); + let threaded = context.accounts[self.cursor_pos.0] + .runtime_settings + .threaded; + let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); let envelope: &Envelope = if threaded { let i = mailbox.threaded_mail(idx); &mailbox.collection[i] @@ -237,7 +260,7 @@ impl MailListing { let bottom_right = bottom_right!(area); if self.length == 0 { clear_area(grid, area); - copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS-1, 0))); + copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, 0))); context.dirty_areas.push_back(area); return; } @@ -245,8 +268,7 @@ impl MailListing { let prev_page_no = (self.cursor_pos.2).wrapping_div(rows); let page_no = (self.new_cursor_pos.2).wrapping_div(rows); - let top_idx = page_no*rows; - + let top_idx = page_no * rows; /* If cursor position has changed, remove the highlight from the previous position and * apply it in the new one. */ @@ -257,31 +279,57 @@ impl MailListing { if *idx >= self.length { continue; //bounds check } - let new_area = (set_y(upper_left, get_y(upper_left)+(*idx % rows)), set_y(bottom_right, get_y(upper_left) + (*idx % rows))); + let new_area = ( + set_y(upper_left, get_y(upper_left) + (*idx % rows)), + set_y(bottom_right, get_y(upper_left) + (*idx % rows)), + ); self.highlight_line(grid, new_area, *idx, context); context.dirty_areas.push_back(new_area); } return; - } else if self.cursor_pos != self.new_cursor_pos { + } else if self.cursor_pos != self.new_cursor_pos { self.cursor_pos = self.new_cursor_pos; } /* Page_no has changed, so draw new page */ - copy_area(grid, &self.content, area, ((0, top_idx), (MAX_COLS - 1, self.length))); - self.highlight_line(grid, (set_y(upper_left, get_y(upper_left)+(self.cursor_pos.2 % rows)), set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows))), self.cursor_pos.2, context); + copy_area( + grid, + &self.content, + area, + ((0, top_idx), (MAX_COLS - 1, self.length)), + ); + self.highlight_line( + grid, + ( + set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)), + set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)), + ), + self.cursor_pos.2, + context, + ); context.dirty_areas.push_back(area); } - fn make_thread_entry(envelope: &Envelope, idx: usize, indent: usize, - container: &Container, indentations: &Vec, idx_width: usize) -> String { + fn make_thread_entry( + envelope: &Envelope, + idx: usize, + indent: usize, + container: &Container, + indentations: &Vec, + idx_width: usize, + ) -> String { let has_sibling = container.has_sibling(); let has_parent = container.has_parent(); let show_subject = container.show_subject(); - let mut s = format!("{}{}{} ", idx, " ".repeat(idx_width + 2 - (idx.to_string().chars().count())), MailListing::format_date(&envelope)); + let mut s = format!( + "{}{}{} ", + idx, + " ".repeat(idx_width + 2 - (idx.to_string().chars().count())), + MailListing::format_date(&envelope) + ); for i in 0..indent { - if indentations.len() > i && indentations[i] - { + if indentations.len() > i && indentations[i] { s.push('│'); } else { s.push(' '); @@ -298,7 +346,8 @@ impl MailListing { } else { s.push('└'); } - s.push('─'); s.push('>'); + s.push('─'); + s.push('>'); } if show_subject { @@ -306,7 +355,7 @@ impl MailListing { } let attach_count = envelope.body().count_attachments(); if attach_count > 1 { - s.push_str(&format!(" {}∞ ", attach_count-1)); + s.push_str(&format!(" {}∞ ", attach_count - 1)); } s } @@ -314,20 +363,12 @@ impl MailListing { let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date()); let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap(); match now.as_secs() { - n if n < 10*60*60 => { - format!("{} hours ago{}", n / (60*60), " ".repeat(8)) - }, - n if n < 24*60*60 => { - format!("{} hours ago{}", n / (60*60), " ".repeat(7)) - }, - n if n < 4*24*60*60 => { - format!("{} days ago{}", n / (24*60*60), " ".repeat(9)) - }, - _ => { - envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string() - }, - - + n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)), + n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)), + n if n < 4 * 24 * 60 * 60 => { + format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9)) + } + _ => envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string(), } } } @@ -352,7 +393,7 @@ impl Component for MailListing { /* Render the mail body in a pager, basically copy what HSplit does */ let total_rows = get_y(bottom_right) - get_y(upper_left); let pager_ratio = context.runtime_settings.pager.pager_ratio; - let bottom_entity_rows = (pager_ratio*total_rows )/100; + let bottom_entity_rows = (pager_ratio * total_rows) / 100; if bottom_entity_rows > total_rows { clear_area(grid, area); @@ -360,9 +401,14 @@ impl Component for MailListing { return; } let mid = get_y(upper_left) + total_rows - bottom_entity_rows; - self.draw_list(grid, - (upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-1)), - context); + self.draw_list( + grid, + ( + upper_left, + (get_x(bottom_right), get_y(upper_left) + mid - 1), + ), + context, + ); if self.length == 0 { self.dirty = false; return; @@ -378,16 +424,22 @@ impl Component for MailListing { for i in get_x(upper_left)..=get_x(bottom_right) { grid[(i, mid)].set_ch('─'); } - context.dirty_areas.push_back((set_y(upper_left, mid), set_y(bottom_right, mid))); + context + .dirty_areas + .push_back((set_y(upper_left, mid), set_y(bottom_right, mid))); } // TODO: Make headers view configurable if !self.dirty { - self.view.as_mut().map(|v| v.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context)); + self.view + .as_mut() + .map(|v| v.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context)); return; } self.view = Some(MailView::new(self.cursor_pos, None, None)); - self.view.as_mut().map(|v| v.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context)); + self.view + .as_mut() + .map(|v| v.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context)); self.dirty = false; } } @@ -398,17 +450,17 @@ impl Component for MailListing { self.new_cursor_pos.2 -= 1; self.dirty = true; } - }, + } UIEventType::Input(Key::Down) => { if self.length > 0 && self.new_cursor_pos.2 < self.length - 1 { self.new_cursor_pos.2 += 1; self.dirty = true; } - }, + } UIEventType::Input(Key::Char('\n')) if self.unfocused == false => { self.unfocused = true; self.dirty = true; - }, + } UIEventType::Input(Key::Char('m')) if self.unfocused == false => { use std::process::{Command, Stdio}; /* Kill input thread so that spawned command can be sole receiver of stdin */ @@ -446,16 +498,21 @@ impl Component for MailListing { * Main loop will wait on children and when they reap them the loop spawns a new * input-thread */ - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Fork(ForkType::NewDraft(f, output)) }); - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::ChangeMode(UIMode::Fork) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::Fork(ForkType::NewDraft(f, output)), + }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::ChangeMode(UIMode::Fork), + }); return; - }, + } UIEventType::Input(Key::Char('i')) if self.unfocused == true => { self.unfocused = false; self.dirty = true; self.view = None; - - }, + } UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => { let folder_length = context.accounts[self.cursor_pos.0].len(); let accounts_length = context.accounts.len(); @@ -465,29 +522,29 @@ impl Component for MailListing { self.new_cursor_pos.1 = self.cursor_pos.1 + 1; self.dirty = true; self.refresh_mailbox(context); - } else if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1 { + } else if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1 + { self.new_cursor_pos.0 = self.cursor_pos.0 + 1; self.new_cursor_pos.1 = 0; self.dirty = true; self.refresh_mailbox(context); } - }, + } 'K' => { if self.cursor_pos.1 > 0 { self.new_cursor_pos.1 = self.cursor_pos.1 - 1; self.dirty = true; self.refresh_mailbox(context); - } else if self.cursor_pos.0 > 0 { + } else if self.cursor_pos.0 > 0 { self.new_cursor_pos.0 = self.cursor_pos.0 - 1; self.new_cursor_pos.1 = 0; self.dirty = true; self.refresh_mailbox(context); } - }, - _ => { - }, + } + _ => {} } - }, + } UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => { let accounts_length = context.accounts.len(); match k { @@ -496,40 +553,38 @@ impl Component for MailListing { self.new_cursor_pos.1 = 0; self.dirty = true; self.refresh_mailbox(context); - }, + } 'l' if self.cursor_pos.0 > 0 => { self.new_cursor_pos.0 = self.cursor_pos.0 - 1; self.new_cursor_pos.1 = 0; self.dirty = true; self.refresh_mailbox(context); - }, - _ => { - }, + } + _ => {} } - }, + } UIEventType::RefreshMailbox(_) => { self.dirty = true; self.view = None; - }, + } UIEventType::ChangeMode(UIMode::Normal) => { self.dirty = true; - }, + } UIEventType::Resize => { self.dirty = true; - }, - UIEventType::Action(ref action) => { - match action { - Action::MailListing(MailListingAction::ToggleThreaded) => { - context.accounts[self.cursor_pos.0].runtime_settings.threaded = !context.accounts[self.cursor_pos.0].runtime_settings.threaded; - self.refresh_mailbox(context); - self.dirty = true; - - - }, + } + UIEventType::Action(ref action) => match action { + Action::MailListing(MailListingAction::ToggleThreaded) => { + context.accounts[self.cursor_pos.0] + .runtime_settings + .threaded = !context.accounts[self.cursor_pos.0] + .runtime_settings + .threaded; + self.refresh_mailbox(context); + self.dirty = true; } }, - _ => { - }, + _ => {} } if let Some(ref mut v) = self.view { v.process_event(event, context); diff --git a/ui/src/components/mail/mod.rs b/ui/src/components/mail/mod.rs index 65e28ee30..c19a1b11a 100644 --- a/ui/src/components/mail/mod.rs +++ b/ui/src/components/mail/mod.rs @@ -1,5 +1,5 @@ /*! Entities that handle Mail specific functions. - */ + */ use super::*; pub mod listing; @@ -7,7 +7,6 @@ pub mod view; pub use listing::*; pub use view::*; - #[derive(Debug)] struct AccountMenuEntry { name: String, @@ -15,7 +14,6 @@ struct AccountMenuEntry { entries: Vec<(usize, Folder)>, } - #[derive(Debug)] pub struct AccountMenu { accounts: Vec, @@ -25,8 +23,10 @@ pub struct AccountMenu { impl AccountMenu { pub fn new(accounts: &Vec) -> Self { - let accounts = accounts.iter().enumerate().map(|(i, a)| { - AccountMenuEntry { + let accounts = accounts + .iter() + .enumerate() + .map(|(i, a)| AccountMenuEntry { name: a.name().to_string(), index: i, entries: { @@ -34,9 +34,10 @@ impl AccountMenu { for (idx, acc) in a.list_folders().iter().enumerate() { entries.push((idx, acc.clone())); } - entries} - } - }).collect(); + entries + }, + }) + .collect(); AccountMenu { accounts: accounts, dirty: true, @@ -50,10 +51,9 @@ impl AccountMenu { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); + let highlight = self.cursor.map(|(x, _)| x == a.index).unwrap_or(false); - let highlight = self.cursor.map(|(x,_)| x == a.index).unwrap_or(false); - - let mut parents: Vec> = vec!(None; a.entries.len()); + let mut parents: Vec> = vec![None; a.entries.len()]; for (idx, e) in a.entries.iter().enumerate() { for c in e.1.children() { @@ -79,15 +79,22 @@ impl AccountMenu { depth.push(c); } - fn print(root: usize, parents: &Vec>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String, inc: &mut usize) -> () { + fn print( + root: usize, + parents: &Vec>, + depth: &mut String, + entries: &Vec<(usize, Folder)>, + s: &mut String, + inc: &mut usize, + ) -> () { let len = s.len(); - s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.name())); + s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.name())); *inc += 1; let children_no = entries[root].1.children().len(); for (idx, child) in entries[root].1.children().iter().enumerate() { let len = s.len(); s.insert_str(len, &format!("{}├─", depth)); - push(depth, if idx == children_no - 1 {'│'} else { ' ' }); + push(depth, if idx == children_no - 1 { '│' } else { ' ' }); print(*child, parents, depth, entries, s, inc); pop(depth); } @@ -128,17 +135,24 @@ impl AccountMenu { Color::Default }; - let (x, _) = write_string_to_grid(&s, - grid, - color_fg, - color_bg, - (set_y(upper_left, y), bottom_right), - false); + let (x, _) = write_string_to_grid( + &s, + grid, + color_fg, + color_bg, + (set_y(upper_left, y), bottom_right), + false, + ); if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 { - change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg); + change_colors( + grid, + ((x, y), (get_x(bottom_right) + 1, y)), + color_fg, + color_bg, + ); } else { - change_colors(grid, ((x, y),set_y(bottom_right, y)), color_fg , color_bg); + change_colors(grid, ((x, y), set_y(bottom_right, y)), color_fg, color_bg); } idx += 1; } @@ -147,7 +161,6 @@ impl AccountMenu { } else { idx - 1 } - } } @@ -162,9 +175,7 @@ impl Component for AccountMenu { self.dirty = false; let mut y = get_y(upper_left); for a in &self.accounts { - y += self.print_account(grid, - (set_y(upper_left, y), bottom_right), - &a); + y += self.print_account(grid, (set_y(upper_left, y), bottom_right), &a); } context.dirty_areas.push_back(area); @@ -174,15 +185,14 @@ impl Component for AccountMenu { UIEventType::RefreshMailbox(c) => { self.cursor = Some(c); self.dirty = true; - }, + } UIEventType::ChangeMode(UIMode::Normal) => { self.dirty = true; - }, + } UIEventType::Resize => { self.dirty = true; - }, - _ => { - }, + } + _ => {} } } fn is_dirty(&self) -> bool { diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs index 10aa37e1c..770f04e73 100644 --- a/ui/src/components/mod.rs +++ b/ui/src/components/mod.rs @@ -26,16 +26,16 @@ */ use super::*; -pub mod utilities; pub mod mail; pub mod notifications; +pub mod utilities; -pub use utilities::*; pub use mail::*; +pub use utilities::*; -use super::cells::{Color, CellBuffer}; -use super::position::{Area, }; -use super::{UIEvent, UIEventType, Key}; +use super::cells::{CellBuffer, Color}; +use super::position::Area; +use super::{Key, UIEvent, UIEventType}; /// The upper and lower boundary char. const HORZ_BOUNDARY: char = '─'; @@ -84,11 +84,18 @@ pub trait Component { } } - // TODO: word break. -pub fn copy_area_with_break(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, src: Area) { +pub fn copy_area_with_break( + grid_dest: &mut CellBuffer, + grid_src: &CellBuffer, + dest: Area, + src: Area, +) { if !is_valid_area!(dest) || !is_valid_area!(src) { - eprintln!("BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}", src, dest); + eprintln!( + "BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}", + src, dest + ); return; } let mut src_x = get_x(upper_left!(src)); @@ -105,7 +112,7 @@ pub fn copy_area_with_break(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, d continue 'y_; } - grid_dest[(x,y)] = grid_src[(src_x, src_y)]; + grid_dest[(x, y)] = grid_src[(src_x, src_y)]; src_x += 1; if src_x >= get_x(bottom_right!(src)) { src_y += 1; @@ -120,11 +127,13 @@ pub fn copy_area_with_break(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, d } } - /// Copy a source `Area` to a destination. pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, src: Area) { if !is_valid_area!(dest) || !is_valid_area!(src) { - eprintln!("BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}", src, dest); + eprintln!( + "BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}", + src, dest + ); return; } let mut src_x = get_x(upper_left!(src)); @@ -132,7 +141,7 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) { 'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) { - grid_dest[(x,y)] = grid_src[(src_x, src_y)]; + grid_dest[(x, y)] = grid_src[(src_x, src_y)]; if src_x >= get_x(bottom_right!(src)) { break 'for_x; } @@ -140,7 +149,10 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, } src_x = get_x(upper_left!(src)); if src_y >= get_y(bottom_right!(src)) { - clear_area(grid_dest, ((get_x(upper_left!(dest)), y), bottom_right!(dest))); + clear_area( + grid_dest, + ((get_x(upper_left!(dest)), y), bottom_right!(dest)), + ); break; } src_y += 1; @@ -155,32 +167,41 @@ pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_colo } for y in get_y(upper_left!(area))..=get_y(bottom_right!(area)) { for x in get_x(upper_left!(area))..=get_x(bottom_right!(area)) { - grid[(x,y)].set_fg(fg_color); - grid[(x,y)].set_bg(bg_color); + grid[(x, y)].set_fg(fg_color); + grid[(x, y)].set_bg(bg_color); } } } - /// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors. -fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area, line_break: bool) -> Pos { +fn write_string_to_grid( + s: &str, + grid: &mut CellBuffer, + fg_color: Color, + bg_color: Color, + area: Area, + line_break: bool, +) -> Pos { let bounds = grid.size(); let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let (mut x, mut y) = upper_left; - if y > (get_y(bottom_right)) || x > get_x(bottom_right) || - y > get_y(bounds) || x > get_x(bounds) { - eprintln!(" Invalid area with string {} and area {:?}", s, area); + if y > (get_y(bottom_right)) + || x > get_x(bottom_right) + || y > get_y(bounds) + || x > get_x(bounds) + { + eprintln!(" Invalid area with string {} and area {:?}", s, area); return (x, y); } for l in s.lines() { 'char: for c in l.chars() { - grid[(x,y)].set_ch(c); - grid[(x,y)].set_fg(fg_color); - grid[(x,y)].set_bg(bg_color); + grid[(x, y)].set_ch(c); + grid[(x, y)].set_fg(fg_color); + grid[(x, y)].set_bg(bg_color); 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); y += 1; if y >= (get_y(bottom_right)) || y > get_y(bounds) { @@ -204,9 +225,9 @@ fn clear_area(grid: &mut CellBuffer, area: Area) { let bottom_right = bottom_right!(area); for y in get_y(upper_left)..=get_y(bottom_right) { for x in get_x(upper_left)..=get_x(bottom_right) { - grid[(x,y)].set_ch(' '); - grid[(x,y)].set_bg(Color::Default); - grid[(x,y)].set_fg(Color::Default); + grid[(x, y)].set_ch(' '); + grid[(x, y)].set_bg(Color::Default); + grid[(x, y)].set_fg(Color::Default); } } } diff --git a/ui/src/components/notifications.rs b/ui/src/components/notifications.rs index 92f64e52c..7c394281f 100644 --- a/ui/src/components/notifications.rs +++ b/ui/src/components/notifications.rs @@ -9,20 +9,18 @@ use super::*; pub struct XDGNotifications {} impl Component for XDGNotifications { - 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) { match event.event_type { UIEventType::Notification(ref t) => { - notify_Notification::new() - .summary("Refresh Event") - .body(t) - .icon("dialog-information") - .show().unwrap(); - }, + notify_Notification::new() + .summary("Refresh Event") + .body(t) + .icon("dialog-information") + .show() + .unwrap(); + } _ => {} } } } - diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index 6bfd1605d..fa14985d2 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -1,5 +1,5 @@ /*! Various useful components that can be used in a generic fashion. - */ + */ use super::*; /// A horizontally split in half container. @@ -21,7 +21,6 @@ impl HSplit { } } - impl Component for HSplit { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if !is_valid_area!(area) { @@ -30,7 +29,7 @@ impl Component for HSplit { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let total_rows = get_y(bottom_right) - get_y(upper_left); - let bottom_entity_height = (self.ratio*total_rows )/100; + let bottom_entity_height = (self.ratio * total_rows) / 100; let mid = get_y(upper_left) + total_rows - bottom_entity_height; if self.show_divider { @@ -38,19 +37,26 @@ impl Component for HSplit { grid[(i, mid)].set_ch('─'); } } - let _ = self.top.component.draw(grid, - (upper_left, (get_x(bottom_right), get_y(upper_left) + mid-1)), - context); - let _ = self.bottom.component.draw(grid, - ((get_x(upper_left), get_y(upper_left) + mid), bottom_right), - context); + let _ = self.top.component.draw( + grid, + ( + upper_left, + (get_x(bottom_right), get_y(upper_left) + mid - 1), + ), + context, + ); + let _ = self.bottom.component.draw( + grid, + ((get_x(upper_left), get_y(upper_left) + mid), bottom_right), + context, + ); } fn process_event(&mut self, event: &UIEvent, context: &mut Context) { self.top.rcv_event(event, context); self.bottom.rcv_event(event, context); } fn is_dirty(&self) -> bool { - self.top.component.is_dirty() || self.bottom.component.is_dirty() + self.top.component.is_dirty() || self.bottom.component.is_dirty() } } @@ -74,7 +80,6 @@ impl VSplit { } } - impl Component for VSplit { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if !is_valid_area!(area) { @@ -83,16 +88,18 @@ impl Component for VSplit { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let total_cols = get_x(bottom_right) - get_x(upper_left); - let right_entity_width = (self.ratio*total_cols )/100; + let right_entity_width = (self.ratio * total_cols) / 100; let mid = get_x(bottom_right) - right_entity_width; - if get_y(upper_left)> 1 { - let c = grid.get(mid, get_y(upper_left)-1).map(|a| a.ch()).unwrap_or_else(|| ' '); + if get_y(upper_left) > 1 { + let c = grid.get(mid, get_y(upper_left) - 1) + .map(|a| a.ch()) + .unwrap_or_else(|| ' '); match c { HORZ_BOUNDARY => { - grid[(mid, get_y(upper_left)-1)].set_ch(LIGHT_DOWN_AND_HORIZONTAL); - }, - _ => {}, + grid[(mid, get_y(upper_left) - 1)].set_ch(LIGHT_DOWN_AND_HORIZONTAL); + } + _ => {} } } @@ -102,29 +109,33 @@ impl Component for VSplit { grid[(mid, i)].set_fg(Color::Default); grid[(mid, i)].set_bg(Color::Default); } - if get_y(bottom_right)> 1 { - let c = grid.get(mid, get_y(bottom_right)-1).map(|a| a.ch()).unwrap_or_else(|| ' '); + if get_y(bottom_right) > 1 { + let c = grid.get(mid, get_y(bottom_right) - 1) + .map(|a| a.ch()) + .unwrap_or_else(|| ' '); match 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); + } + _ => {} } } } - let _ = self.left.component.draw(grid, - (upper_left, (mid-1, get_y(bottom_right))), - context); - let _ = self.right.component.draw(grid, - ((mid+1, get_y(upper_left)), bottom_right), - context); + let _ = + self.left + .component + .draw(grid, (upper_left, (mid - 1, get_y(bottom_right))), context); + let _ = + self.right + .component + .draw(grid, ((mid + 1, get_y(upper_left)), bottom_right), context); } fn process_event(&mut self, event: &UIEvent, context: &mut Context) { self.left.rcv_event(event, context); self.right.rcv_event(event, context); } fn is_dirty(&self) -> bool { - self.left.component.is_dirty() || self.right.component.is_dirty() + self.left.component.is_dirty() || self.right.component.is_dirty() } } @@ -152,13 +163,18 @@ impl Pager { .spawn() .expect("Failed to start pager filter process"); { - let mut stdin = - filter_child.stdin.as_mut().expect("failed to open stdin"); - stdin.write_all(text.as_bytes()).expect("Failed to write to stdin"); + let mut stdin = filter_child.stdin.as_mut().expect("failed to open stdin"); + stdin + .write_all(text.as_bytes()) + .expect("Failed to write to stdin"); } - - text = String::from_utf8_lossy(&filter_child.wait_with_output().expect("Failed to wait on filter").stdout).to_string(); + text = String::from_utf8_lossy( + &filter_child + .wait_with_output() + .expect("Failed to wait on filter") + .stdout, + ).to_string(); } let lines: Vec<&str> = text.trim().split('\n').collect(); @@ -219,12 +235,14 @@ impl Pager { let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); if width > 0 { for (i, l) in lines.iter().enumerate() { - write_string_to_grid(l, - content, - Color::Default, - Color::Default, - ((0, i), (width -1, i)), - true); + write_string_to_grid( + l, + content, + Color::Default, + Color::Default, + ((0, i), (width - 1, i)), + true, + ); } } } @@ -249,13 +267,17 @@ impl Component for Pager { return; } - clear_area(grid, - (upper_left, bottom_right)); + clear_area(grid, (upper_left, bottom_right)); //let pager_context: usize = context.settings.pager.pager_context; //let pager_stop: bool = context.settings.pager.pager_stop; //let rows = y(bottom_right) - y(upper_left); //let page_length = rows / self.height; - copy_area_with_break(grid, &self.content, area, ((0, self.cursor_pos), (self.width - 1, self.height - 1))); + copy_area_with_break( + grid, + &self.content, + area, + ((0, self.cursor_pos), (self.width - 1, self.height - 1)), + ); context.dirty_areas.push_back(area); } fn process_event(&mut self, event: &UIEvent, _context: &mut Context) { @@ -265,21 +287,20 @@ impl Component for Pager { self.cursor_pos -= 1; self.dirty = true; } - }, + } UIEventType::Input(Key::Char('j')) => { if self.height > 0 && self.cursor_pos < self.height - 1 { self.cursor_pos += 1; self.dirty = true; } - }, + } UIEventType::ChangeMode(UIMode::Normal) => { self.dirty = true; - }, + } UIEventType::Resize => { self.dirty = true; - }, - _ => { - }, + } + _ => {} } } fn is_dirty(&self) -> bool { @@ -314,34 +335,35 @@ impl StatusBar { clear_area(grid, area); if let Some(n) = self.notifications.pop_front() { self.dirty = true; - write_string_to_grid(&n, - grid, - Color::Byte(219), - Color::Byte(88), - area, false); + write_string_to_grid(&n, grid, Color::Byte(219), Color::Byte(88), area, false); } else { - write_string_to_grid(&self.status, - grid, - Color::Byte(123), - Color::Byte(26), - area, false); + write_string_to_grid( + &self.status, + grid, + Color::Byte(123), + Color::Byte(26), + area, + false, + ); } change_colors(grid, area, Color::Byte(123), Color::Byte(26)); context.dirty_areas.push_back(area); } fn draw_execute_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { clear_area(grid, area); - write_string_to_grid(&self.ex_buffer, - grid, - Color::Byte(219), - Color::Byte(88), - area, false); + write_string_to_grid( + &self.ex_buffer, + grid, + Color::Byte(219), + Color::Byte(88), + area, + false, + ); change_colors(grid, area, Color::Byte(219), Color::Byte(88)); context.dirty_areas.push_back(area); } } - impl Component for StatusBar { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if !is_valid_area!(area) { @@ -356,36 +378,57 @@ impl Component for StatusBar { } let height = self.height; - let _ = self.container.component.draw(grid, - (upper_left, (get_x(bottom_right), get_y(bottom_right) - height)), - context); + let _ = self.container.component.draw( + grid, + ( + upper_left, + (get_x(bottom_right), get_y(bottom_right) - height), + ), + context, + ); if !self.is_dirty() { return; } self.dirty = false; - self.draw_status_bar(grid, (set_y(upper_left, get_y(bottom_right)), bottom_right), context); + self.draw_status_bar( + grid, + (set_y(upper_left, get_y(bottom_right)), bottom_right), + context, + ); match self.mode { - UIMode::Normal => { - }, + UIMode::Normal => {} UIMode::Execute => { - self.draw_execute_bar(grid, - (set_y(upper_left, get_y(bottom_right) - height + 1), set_y(bottom_right, get_y(bottom_right) - height+1)), - context); - }, - _ => {}, - + self.draw_execute_bar( + grid, + ( + set_y(upper_left, get_y(bottom_right) - height + 1), + set_y(bottom_right, get_y(bottom_right) - height + 1), + ), + context, + ); + } + _ => {} } } fn process_event(&mut self, event: &UIEvent, context: &mut Context) { self.container.rcv_event(event, context); match &event.event_type { UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => { - let m = &context.accounts[*idx_a][*idx_f].as_ref().unwrap().as_ref().unwrap(); - self.status = format!("{} |Mailbox: {}, Messages: {}, New: {}", self.mode, m.folder.name(), m.collection.len(), m.collection.iter().filter(|e| !e.is_seen()).count()); + let m = &context.accounts[*idx_a][*idx_f] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); + self.status = format!( + "{} |Mailbox: {}, Messages: {}, New: {}", + self.mode, + m.folder.name(), + m.collection.len(), + m.collection.iter().filter(|e| !e.is_seen()).count() + ); self.dirty = true; - - }, + } UIEventType::ChangeMode(m) => { let offset = self.status.find('|').unwrap_or(self.status.len()); self.status.replace_range(..offset, &format!("{} ", m)); @@ -394,27 +437,30 @@ impl Component for StatusBar { match m { UIMode::Normal => { self.height = 1; - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Command(self.ex_buffer.clone())}); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::Command(self.ex_buffer.clone()), + }); self.ex_buffer.clear() - }, + } UIMode::Execute => { self.height = 2; - }, - _ => {}, + } + _ => {} }; - }, + } UIEventType::ExInput(Key::Char(c)) => { self.dirty = true; self.ex_buffer.push(*c); - }, + } UIEventType::Resize => { self.dirty = true; - }, + } UIEventType::StatusNotification(s) => { self.notifications.push_back(s.clone()); self.dirty = true; - }, - _ => {}, + } + _ => {} } } fn is_dirty(&self) -> bool { @@ -422,7 +468,6 @@ impl Component for StatusBar { } } - // A box with a text content. pub struct TextBox { _content: String, @@ -430,16 +475,12 @@ pub struct TextBox { impl TextBox { pub fn new(s: String) -> Self { - TextBox { - _content: s, - } + TextBox { _content: s } } } 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) { return; } diff --git a/ui/src/execute/mod.rs b/ui/src/execute/mod.rs index c0e4a722a..53b7ed9be 100644 --- a/ui/src/execute/mod.rs +++ b/ui/src/execute/mod.rs @@ -1,11 +1,15 @@ /*! A parser module for user commands passed through the Ex mode. */ +use nom::digit; use std; -use nom::{digit, }; - -named!(usize_c, - map_res!(map_res!(ws!(digit), std::str::from_utf8), std::str::FromStr::from_str)); +named!( + usize_c, + map_res!( + map_res!(ws!(digit), std::str::from_utf8), + std::str::FromStr::from_str + ) +); named!(pub goto, preceded!(tag!("b "), diff --git a/ui/src/helpers.rs b/ui/src/helpers.rs index c79168554..8576ee53a 100644 --- a/ui/src/helpers.rs +++ b/ui/src/helpers.rs @@ -1,6 +1,6 @@ use std; -use std::path::PathBuf; use std::io::Write; +use std::path::PathBuf; use uuid::Uuid; @@ -9,7 +9,6 @@ pub struct File { path: PathBuf, } - impl File { pub fn file(&mut self) -> std::fs::File { std::fs::File::create(&self.path).unwrap() @@ -19,7 +18,6 @@ impl File { } } - //TODO: add temp files to a list to reap them when dropped pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { let mut dir = std::env::temp_dir(); @@ -28,7 +26,10 @@ pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { p } else { dir.push("meli"); - std::fs::DirBuilder::new().recursive(true).create(&dir).unwrap(); + std::fs::DirBuilder::new() + .recursive(true) + .create(&dir) + .unwrap(); let u = Uuid::new_v4(); dir.push(u.hyphenated().to_string()); &dir @@ -38,8 +39,5 @@ pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { f.write(bytes).unwrap(); f.flush().unwrap(); - File { - path: path.clone(), - } + File { path: path.clone() } } - diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 87c3e53a5..8b20b338a 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -19,7 +19,6 @@ * along with meli. If not, see . */ - /*! The UI crate has an Entity-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct. @@ -38,10 +37,10 @@ pub use helpers::*; #[macro_use] mod execute; -use execute::goto; -pub use self::position::*; use self::cells::*; pub use self::components::*; +pub use self::position::*; +use execute::goto; extern crate melib; extern crate mime_apps; @@ -53,15 +52,15 @@ extern crate linkify; extern crate uuid; use melib::*; -use std::io::{Write, }; use std::collections::VecDeque; use std::fmt; +use std::io::Write; extern crate termion; -use termion::{clear, style, cursor}; -use termion::raw::IntoRawMode; -use termion::event::{Key as TermionKey, }; +use termion::event::Key as TermionKey; use termion::input::TermRead; +use termion::raw::IntoRawMode; use termion::screen::AlternateScreen; +use termion::{clear, cursor, style}; #[macro_use] extern crate nom; @@ -79,7 +78,9 @@ pub enum ThreadEvent { /// User input. Input(Key), /// A watched folder has been refreshed. - RefreshMailbox{ name: String }, + RefreshMailbox { + name: String, + }, UIEventType(UIEventType), //Decode { _ }, // For gpg2 signature check } @@ -96,12 +97,11 @@ pub enum ForkType { NewDraft(File, std::process::Child), } - #[derive(Debug)] pub enum UIEventType { Input(Key), ExInput(Key), - RefreshMailbox((usize,usize)), + RefreshMailbox((usize, usize)), //Quit? Resize, /// Force redraw. @@ -115,7 +115,6 @@ pub enum UIEventType { StatusNotification(String), } - /// An event passed from `State` to its Entities. #[derive(Debug)] pub struct UIEvent { @@ -132,11 +131,15 @@ pub enum UIMode { impl fmt::Display for UIMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - UIMode::Normal => { "NORMAL" }, - UIMode::Execute => { "EX" }, - UIMode::Fork => { "FORK" }, - }) + write!( + f, + "{}", + match *self { + UIMode::Normal => "NORMAL", + UIMode::Execute => "EX", + UIMode::Fork => "FORK", + } + ) } } @@ -173,7 +176,6 @@ impl Context { } } - /// A State object to manage and own components and entities of the UI. `State` is responsible for /// managing the terminal and interfacing with `melib` pub struct State { @@ -187,13 +189,19 @@ pub struct State { sender: Sender, entities: Vec, pub context: Context, - } impl Drop for State { fn drop(&mut self) { // When done, restore the defaults to avoid messing with the terminal. - write!(self.stdout(), "{}{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1), cursor::Show).unwrap(); + write!( + self.stdout(), + "{}{}{}{}", + clear::All, + style::Reset, + cursor::Goto(1, 1), + cursor::Show + ).unwrap(); self.flush(); } } @@ -207,12 +215,16 @@ impl State { let stdout = AlternateScreen::from(_stdout.into_raw_mode().unwrap()); let termsize = termion::terminal_size().ok(); - let termcols = termsize.map(|(w,_)| w); - let termrows = termsize.map(|(_,h)| h); + let termcols = termsize.map(|(w, _)| w); + let termrows = termsize.map(|(_, h)| h); let cols = termcols.unwrap_or(0) as usize; let rows = termrows.unwrap_or(0) as usize; - let mut accounts: Vec = settings.accounts.iter().map(|(n, a_s)| { Account::new(n.to_string(), a_s.clone(), &backends) }).collect(); - accounts.sort_by(|a,b| a.name().cmp(&b.name()) ); + let mut accounts: Vec = settings + .accounts + .iter() + .map(|(n, a_s)| Account::new(n.to_string(), a_s.clone(), &backends)) + .collect(); + accounts.sort_by(|a, b| a.name().cmp(&b.name())); let mut s = State { cols: cols, rows: rows, @@ -223,7 +235,6 @@ impl State { sender: sender, entities: Vec::with_capacity(1), - context: Context { accounts: accounts, _backends: backends, @@ -235,19 +246,29 @@ impl State { input_thread: input_thread, }, }; - write!(s.stdout(), "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap(); + write!( + s.stdout(), + "{}{}{}", + cursor::Hide, + clear::All, + cursor::Goto(1, 1) + ).unwrap(); s.flush(); for account in &mut s.context.accounts { let sender = s.sender.clone(); account.watch(RefreshEventConsumer::new(Box::new(move |r| { sender.send(ThreadEvent::from(r)); }))); - } s } pub fn to_main_screen(&mut self) { - write!(self.stdout(), "{}{}", termion::screen::ToMainScreen, cursor::Show).unwrap(); + write!( + self.stdout(), + "{}{}", + termion::screen::ToMainScreen, + cursor::Show + ).unwrap(); self.flush(); self.stdout = None; self.context.input_thread.send(false); @@ -257,24 +278,36 @@ impl State { s.lock(); self.stdout = Some(AlternateScreen::from(s.into_raw_mode().unwrap())); - write!(self.stdout(), "{}{}", termion::screen::ToAlternateScreen, cursor::Hide).unwrap(); + write!( + self.stdout(), + "{}{}", + termion::screen::ToAlternateScreen, + cursor::Hide + ).unwrap(); self.flush(); } } impl State { pub fn update_size(&mut self) { let termsize = termion::terminal_size().ok(); - let termcols = termsize.map(|(w,_)| w); - let termrows = termsize.map(|(_,h)| h); - if termcols.unwrap_or(72) as usize != self.cols || termrows.unwrap_or(120) as usize != self.rows { - eprintln!("Size updated, from ({}, {}) -> ({:?}, {:?})", self.cols, self.rows, termcols, termrows); - + let termcols = termsize.map(|(w, _)| w); + let termrows = termsize.map(|(_, h)| h); + if termcols.unwrap_or(72) as usize != self.cols + || termrows.unwrap_or(120) as usize != self.rows + { + eprintln!( + "Size updated, from ({}, {}) -> ({:?}, {:?})", + self.cols, self.rows, termcols, termrows + ); } self.cols = termcols.unwrap_or(72) as usize; self.rows = termrows.unwrap_or(120) as usize; self.grid.resize(self.cols, self.rows, Cell::with_char(' ')); - self.rcv_event(UIEvent { id: 0, event_type: UIEventType::Resize }); + self.rcv_event(UIEvent { + id: 0, + event_type: UIEventType::Resize, + }); } pub fn redraw(&mut self) { @@ -292,9 +325,13 @@ impl State { let bottom_right = bottom_right!(area); for y in get_y(upper_left)..=get_y(bottom_right) { - write!(self.stdout(), "{}", cursor::Goto(get_x(upper_left) as u16 + 1,(y+1) as u16)).unwrap(); + write!( + self.stdout(), + "{}", + cursor::Goto(get_x(upper_left) as u16 + 1, (y + 1) as u16) + ).unwrap(); for x in get_x(upper_left)..=get_x(bottom_right) { - let c = self.grid[(x,y)]; + let c = self.grid[(x, y)]; if c.bg() != cells::Color::Default { write!(self.stdout(), "{}", termion::color::Bg(c.bg().as_termion())).unwrap(); @@ -302,14 +339,21 @@ impl State { if c.fg() != cells::Color::Default { write!(self.stdout(), "{}", termion::color::Fg(c.fg().as_termion())).unwrap(); } - write!(self.stdout(), "{}",c.ch()).unwrap(); + write!(self.stdout(), "{}", c.ch()).unwrap(); if c.bg() != cells::Color::Default { - write!(self.stdout(), "{}", termion::color::Bg(termion::color::Reset)).unwrap(); + write!( + self.stdout(), + "{}", + termion::color::Bg(termion::color::Reset) + ).unwrap(); } if c.fg() != cells::Color::Default { - write!(self.stdout(), "{}", termion::color::Fg(termion::color::Reset)).unwrap(); + write!( + self.stdout(), + "{}", + termion::color::Fg(termion::color::Reset) + ).unwrap(); } - } } self.flush(); @@ -324,17 +368,19 @@ impl State { let cols = self.cols; let rows = self.rows; - self.draw_area(((0, 0), (cols-1, rows-1))); + self.draw_area(((0, 0), (cols - 1, rows - 1))); } pub fn draw_entity(&mut self, idx: usize) { let entity = &mut self.entities[idx]; - let upper_left = (0,0); - let bottom_right = (self.cols-1, self.rows-1); + let upper_left = (0, 0); + let bottom_right = (self.cols - 1, self.rows - 1); if entity.component.is_dirty() { - entity.component.draw(&mut self.grid, - (upper_left, bottom_right), - &mut self.context); + entity.component.draw( + &mut self.grid, + (upper_left, bottom_right), + &mut self.context, + ); } } pub fn register_entity(&mut self, entity: Entity) { @@ -357,22 +403,22 @@ impl State { UIEventType::Command(cmd) => { self.parse_command(cmd); return; - }, + } UIEventType::Fork(child) => { self.mode = UIMode::Fork; self.child = Some(child); self.flush(); return; - }, + } UIEventType::EditDraft(mut file) => { - use std::process::{Command, Stdio}; use std::io::Read; + use std::process::{Command, Stdio}; let mut output = Command::new("msmtp") .arg("-t") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() - .expect("failed to execute process") ; + .expect("failed to execute process"); { let mut in_pipe = output.stdin.as_mut().unwrap(); let mut buf = Vec::new(); @@ -385,13 +431,20 @@ impl State { output.wait_with_output().expect("Failed to read stdout"); return; + } + UIEventType::Input(Key::Char('t')) => for i in 0..self.entities.len() { + self.entities[i].rcv_event( + &UIEvent { + id: 0, + event_type: UIEventType::Action(Action::MailListing( + MailListingAction::ToggleThreaded, + )), + }, + &mut self.context, + ); }, - UIEventType::Input(Key::Char('t')) => - for i in 0..self.entities.len() { - self.entities[i].rcv_event(&UIEvent{ id: 0, event_type: UIEventType::Action(Action::MailListing(MailListingAction::ToggleThreaded)) }, &mut self.context); - } - _ => {}, + _ => {} } /* inform each entity */ for i in 0..self.entities.len() { @@ -402,45 +455,56 @@ impl State { /// Tries to load a mailbox's content pub fn refresh_mailbox(&mut self, account_idx: usize, folder_idx: usize) { let flag = match &mut self.context.accounts[account_idx][folder_idx] { - Some(Ok(_)) => { - true - }, - Some(Err(e)) => { eprintln!("error {:?}", e); false }, - None => { eprintln!("None"); false }, + Some(Ok(_)) => true, + Some(Err(e)) => { + eprintln!("error {:?}", e); + false + } + None => { + eprintln!("None"); + false + } }; if flag { - - self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((account_idx, folder_idx)) }); + self.rcv_event(UIEvent { + id: 0, + event_type: UIEventType::RefreshMailbox((account_idx, folder_idx)), + }); } } pub fn try_wait_on_child(&mut self) -> Option { if { match self.child { - Some(ForkType::NewDraft(_,ref mut c)) => { + Some(ForkType::NewDraft(_, ref mut c)) => { let mut w = c.try_wait(); match w { - Ok(Some(_)) => { true }, - Ok(None) => { false }, - Err(_) => { return None; }, + Ok(Some(_)) => true, + Ok(None) => false, + Err(_) => { + return None; + } } - }, + } Some(ForkType::Generic(ref mut c)) => { let mut w = c.try_wait(); match w { - Ok(Some(_)) => { true }, - Ok(None) => { false }, - Err(_) => { return None; }, + Ok(Some(_)) => true, + Ok(None) => false, + Err(_) => { + return None; + } } - }, + } _ => { return None; } } - } - { + } { if let Some(ForkType::NewDraft(f, _)) = std::mem::replace(&mut self.child, None) { - self.rcv_event(UIEvent { id: 0, event_type: UIEventType::EditDraft(f) }); - + self.rcv_event(UIEvent { + id: 0, + event_type: UIEventType::EditDraft(f), + }); } return Some(true); } @@ -448,15 +512,12 @@ impl State { } fn flush(&mut self) { self.stdout.as_mut().map(|s| s.flush().unwrap()); - } fn stdout(&mut self) -> &mut termion::screen::AlternateScreen> { self.stdout.as_mut().unwrap() - } } - #[derive(Debug)] pub enum Key { /// Backspace. @@ -499,9 +560,8 @@ pub enum Key { Esc, } - impl From for Key { - fn from(k: TermionKey ) -> Self { + fn from(k: TermionKey) -> Self { match k { TermionKey::Backspace => Key::Backspace, TermionKey::Left => Key::Left, @@ -525,7 +585,6 @@ impl From for Key { } } - /* * If we fork (for example start $EDITOR) we want the input-thread to stop reading from stdin. The * best way I came up with right now is to send a signal to the thread that is read in the first @@ -534,7 +593,12 @@ impl From for Key { * * The main loop uses try_wait_on_child() to check if child has exited. */ -pub fn get_events(stdin: std::io::Stdin, mut closure: impl FnMut(Key), mut exit: impl FnMut(), rx: chan::Receiver) -> (){ +pub fn get_events( + stdin: std::io::Stdin, + mut closure: impl FnMut(Key), + mut exit: impl FnMut(), + rx: chan::Receiver, +) -> () { for c in stdin.keys() { chan_select! { default => {}, diff --git a/ui/src/position.rs b/ui/src/position.rs index 844e559fc..277a5ed40 100644 --- a/ui/src/position.rs +++ b/ui/src/position.rs @@ -28,22 +28,28 @@ pub fn set_y(p: Pos, new_y: usize) -> Pos { pub type Area = (Pos, Pos); #[macro_export] -macro_rules! upper_left { ($a:expr) => ( $a.0 ) } +macro_rules! upper_left { + ($a:expr) => { + $a.0 + }; +} #[macro_export] -macro_rules! bottom_right { ($a:expr) => ( $a.1 ) } +macro_rules! bottom_right { + ($a:expr) => { + $a.1 + }; +} #[macro_export] -macro_rules! is_valid_area { ($a:expr) => - { - { - let upper_left = upper_left!($a); - let bottom_right = bottom_right!($a); - if get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right) { - false - } else { - true - } +macro_rules! is_valid_area { + ($a:expr) => {{ + let upper_left = upper_left!($a); + let bottom_right = bottom_right!($a); + if get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right) { + false + } else { + true } - } + }}; } /// A `(cols, rows)` size.