diff --git a/melib/src/async.rs b/melib/src/async.rs index 26a024a1a..b9ba191f6 100644 --- a/melib/src/async.rs +++ b/melib/src/async.rs @@ -1,3 +1,24 @@ +/* + * meli - async module + * + * Copyright 2017 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! * Primitive Async/Wait implementation. * @@ -69,7 +90,7 @@ impl Async { pub fn extract(self) -> T { self.value.unwrap() } - /// Polls worker thread and returns result. + /// Polls worker thread and returns result. pub fn poll(&mut self) -> Result { if self.value.is_some() { return Ok(AsyncStatus::Finished); @@ -99,4 +120,3 @@ impl Async { return Ok(AsyncStatus::Finished); } } - diff --git a/melib/src/conf/mod.rs b/melib/src/conf/mod.rs index 1e718b8cd..7092961ec 100644 --- a/melib/src/conf/mod.rs +++ b/melib/src/conf/mod.rs @@ -27,9 +27,9 @@ pub mod pager; use pager::PagerSettings; 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)] diff --git a/melib/src/conf/pager.rs b/melib/src/conf/pager.rs index f30fdbeae..54d3a5529 100644 --- a/melib/src/conf/pager.rs +++ b/melib/src/conf/pager.rs @@ -1,3 +1,26 @@ +/* + * meli - pager conf module + * + * Copyright 2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +// TODO: Move this to `ui` crate. + fn false_val() -> bool { true } diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 56d7d740c..1953a8a1e 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -18,10 +18,10 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +pub mod async; pub mod conf; pub mod error; pub mod mailbox; -pub mod async; #[macro_use] extern crate serde_derive; diff --git a/melib/src/mailbox/accounts.rs b/melib/src/mailbox/accounts.rs index 82fb34287..837b5627d 100644 --- a/melib/src/mailbox/accounts.rs +++ b/melib/src/mailbox/accounts.rs @@ -23,10 +23,10 @@ * Account management from user configuration. */ +use async::*; use conf::{AccountSettings, Folder}; use mailbox::backends::{Backends, RefreshEventConsumer}; use mailbox::*; -use async::*; use std::ops::{Index, IndexMut}; use std::result; @@ -89,73 +89,73 @@ impl Account { &mut self.workers } fn load_mailbox(&mut self, index: usize, envelopes: Result>) -> () { - let folder = &self.settings.folders[index]; - if self.sent_folder.is_some() { - let id = self.sent_folder.unwrap(); - if id == index { - self.folders[index] = - Some(Mailbox::new(folder, &None, envelopes)); - } else { - let (sent, cur) = { - let ptr = self.folders.as_mut_ptr(); - unsafe { - use std::slice::from_raw_parts_mut; - ( - from_raw_parts_mut(ptr.offset(id as isize), id + 1), - from_raw_parts_mut(ptr.offset(index as isize), index + 1), - ) - } - }; - let sent_path = &self.settings.folders[id]; - if sent[0].is_none() { - sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone())); - } - cur[0] = Some(Mailbox::new(folder, &sent[0], envelopes)); - } - } else { + let folder = &self.settings.folders[index]; + if self.sent_folder.is_some() { + let id = self.sent_folder.unwrap(); + if id == index { self.folders[index] = Some(Mailbox::new(folder, &None, envelopes)); - }; + } else { + let (sent, cur) = { + let ptr = self.folders.as_mut_ptr(); + unsafe { + use std::slice::from_raw_parts_mut; + ( + from_raw_parts_mut(ptr.offset(id as isize), id + 1), + from_raw_parts_mut(ptr.offset(index as isize), index + 1), + ) + } + }; + let sent_path = &self.settings.folders[id]; + if sent[0].is_none() { + sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone())); + } + cur[0] = Some(Mailbox::new(folder, &sent[0], envelopes)); + } + } else { + self.folders[index] = Some(Mailbox::new(folder, &None, envelopes)); + }; } pub fn status(&mut self, index: usize) -> result::Result<(), usize> { match self.workers[index].as_mut() { - None => { return Ok(()); }, - Some(ref mut w) => { - match w.poll() { - Ok(AsyncStatus::NoUpdate) => { - return Err(0); - }, - Ok(AsyncStatus::Finished) => { - }, - Ok(AsyncStatus::ProgressReport(n)) => { - return Err(n); - }, - a => { - eprintln!("{:?}", a); - return Err(0); - } + None => { + return Ok(()); + } + Some(ref mut w) => match w.poll() { + Ok(AsyncStatus::NoUpdate) => { + return Err(0); + } + Ok(AsyncStatus::Finished) => {} + Ok(AsyncStatus::ProgressReport(n)) => { + return Err(n); + } + a => { + eprintln!("{:?}", a); + return Err(0); } }, }; - let m = self.workers[index].take().unwrap().extract(); + let m = self.workers[index].take().unwrap().extract(); self.load_mailbox(index, m); self.workers[index] = None; Ok(()) } - } impl Index for Account { type Output = Result; fn index(&self, index: usize) -> &Result { - &self.folders[index].as_ref().expect("BUG: Requested mailbox that is not yet available.") - + &self.folders[index] + .as_ref() + .expect("BUG: Requested mailbox that is not yet available.") } } /// Will panic if mailbox hasn't loaded, ask `status()` first. impl IndexMut for Account { fn index_mut(&mut self, index: usize) -> &mut Result { - self.folders[index].as_mut().expect("BUG: Requested mailbox that is not yet available.") + self.folders[index] + .as_mut() + .expect("BUG: Requested mailbox that is not yet available.") } } diff --git a/melib/src/mailbox/backends/imap.rs b/melib/src/mailbox/backends/imap.rs index d33be68b6..6ff26cb7a 100644 --- a/melib/src/mailbox/backends/imap.rs +++ b/melib/src/mailbox/backends/imap.rs @@ -19,11 +19,11 @@ * along with meli. If not, see . */ +use async::*; use conf::Folder; use error::Result; -use async::*; use mailbox::backends::{MailBackend, RefreshEventConsumer}; -use mailbox::email::{Envelope, }; +use mailbox::email::Envelope; /// `BackendOp` implementor for Imap #[derive(Debug, Default, Clone)] @@ -35,7 +35,6 @@ impl ImapOp { } } - /* impl BackendOp for ImapOp { diff --git a/melib/src/mailbox/backends/maildir.rs b/melib/src/mailbox/backends/maildir.rs index fe3065fc6..5a54bf0bc 100644 --- a/melib/src/mailbox/backends/maildir.rs +++ b/melib/src/mailbox/backends/maildir.rs @@ -40,10 +40,10 @@ use std::sync::mpsc::channel; use std::thread; extern crate crossbeam; use memmap::{Mmap, Protection}; -use std::path::PathBuf; use std::collections::hash_map::DefaultHasher; -use std::hash::Hasher; use std::fs; +use std::hash::Hasher; +use std::path::PathBuf; /// `BackendOp` implementor for Maildir #[derive(Debug, Default)] @@ -115,7 +115,10 @@ impl BackendOp for MaildirOp { flag } fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> { - let idx: usize = self.path.rfind(":2,").ok_or(MeliError::new(format!("Invalid email filename: {:?}", self)))? + 3; + let idx: usize = self.path.rfind(":2,").ok_or(MeliError::new(format!( + "Invalid email filename: {:?}", + self + )))? + 3; let mut new_name: String = self.path[..idx].to_string(); let mut flags = self.fetch_flags(); flags.toggle(*f); @@ -139,13 +142,9 @@ impl BackendOp for MaildirOp { } fs::rename(&self.path, &new_name)?; - envelope.set_operation_token( - Box::new( - BackendOpGenerator::new( - Box::new( move || Box::new(MaildirOp::new(new_name.clone()))) - ) - ) - ); + envelope.set_operation_token(Box::new(BackendOpGenerator::new(Box::new(move || { + Box::new(MaildirOp::new(new_name.clone())) + })))); Ok(()) } } @@ -189,10 +188,8 @@ impl MailBackend for MaildirType { let mut hasher = DefaultHasher::new(); hasher.write(path.as_bytes()); sender.send(RefreshEvent { - folder: format!( - "{}", path - ), - hash: hasher.finish(), + folder: format!("{}", path), + hash: hasher.finish(), }); } _ => {} @@ -201,7 +198,7 @@ impl MailBackend for MaildirType { } } }) - .unwrap(); + .unwrap(); } } @@ -219,9 +216,9 @@ impl MaildirType { p.push(d); if !p.is_dir() { return Err(MeliError::new(format!( - "{} is not a valid maildir folder", - path - ))); + "{} is not a valid maildir folder", + path + ))); } p.pop(); } @@ -236,7 +233,7 @@ impl MaildirType { thread::Builder::new() .name(format!("parsing {:?}", folder)) - .spawn(move || { + .spawn(move || { MaildirType::is_valid(&folder)?; let path = folder.path(); let mut path = PathBuf::from(path); @@ -264,21 +261,24 @@ impl MaildirType { let mut tx = tx.clone(); let s = scope.spawn(move || { let len = chunk.len(); - let size = if len <= 100 { 100 } else { (len / 100) * 100}; - let mut local_r: Vec = Vec::with_capacity(chunk.len()); + let size = if len <= 100 { 100 } else { (len / 100) * 100 }; + let mut local_r: Vec< + Envelope, + > = Vec::with_capacity(chunk.len()); for c in chunk.chunks(size) { let len = c.len(); for e in c { let e_copy = e.to_string(); - if let Some(mut e) = - Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new( - move || Box::new(MaildirOp::new(e_copy.clone())), - )))) { - if e.populate_headers().is_err() { - continue; - } - local_r.push(e); + if let Some(mut e) = Envelope::from_token(Box::new( + BackendOpGenerator::new(Box::new(move || { + Box::new(MaildirOp::new(e_copy.clone())) + })), + )) { + if e.populate_headers().is_err() { + continue; } + local_r.push(e); + } } tx.send(AsyncStatus::ProgressReport(len)); } @@ -294,8 +294,9 @@ impl MaildirType { } tx.send(AsyncStatus::Finished); Ok(r) - }).unwrap() - }; + }) + .unwrap() + }; w.build(handle) } } diff --git a/melib/src/mailbox/backends/mbox.rs b/melib/src/mailbox/backends/mbox.rs index 3d2185489..0701666a0 100644 --- a/melib/src/mailbox/backends/mbox.rs +++ b/melib/src/mailbox/backends/mbox.rs @@ -19,11 +19,15 @@ * along with meli. If not, see . */ +/*! + * https://wiki2.dovecot.org/MailboxFormat/mbox + */ + +use async::*; use conf::Folder; use error::Result; -use async::*; use mailbox::backends::{MailBackend, RefreshEventConsumer}; -use mailbox::email::{Envelope, }; +use mailbox::email::Envelope; /// `BackendOp` implementor for Mbox #[derive(Debug, Default, Clone)] diff --git a/melib/src/mailbox/backends/mod.rs b/melib/src/mailbox/backends/mod.rs index 6d75644a3..2e998d980 100644 --- a/melib/src/mailbox/backends/mod.rs +++ b/melib/src/mailbox/backends/mod.rs @@ -22,9 +22,9 @@ pub mod imap; pub mod maildir; pub mod mbox; +use async::*; use conf::Folder; use error::Result; -use async::*; use mailbox::backends::imap::ImapType; use mailbox::backends::maildir::MaildirType; use mailbox::backends::mbox::MboxType; diff --git a/melib/src/mailbox/email/attachments.rs b/melib/src/mailbox/email/attachments.rs index 639db8c8b..e3ef22d6d 100644 --- a/melib/src/mailbox/email/attachments.rs +++ b/melib/src/mailbox/email/attachments.rs @@ -47,7 +47,9 @@ impl Display for MultipartType { 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/{}", String::from_utf8_lossy(t)), + MultipartType::Unsupported { tag: ref t } => { + write!(f, "multipart/{}", String::from_utf8_lossy(t)) + } } } } @@ -136,7 +138,7 @@ impl AttachmentBuilder { let mut boundary = None; for (n, v) in params { if n.eq_ignore_ascii_case(b"boundary") { - let mut vec: Vec = Vec::with_capacity(v.len()+4); + let mut vec: Vec = Vec::with_capacity(v.len() + 4); vec.extend_from_slice(b"--"); vec.extend(v); vec.extend_from_slice(b"--"); @@ -148,9 +150,7 @@ impl AttachmentBuilder { self.content_type.0 = ContentType::Multipart { boundary: boundary.unwrap(), }; - self.content_type.1 = ContentSubType::Other { - tag: cst.into(), - }; + self.content_type.1 = ContentSubType::Other { tag: cst.into() }; } else if ct.eq_ignore_ascii_case(b"text") { self.content_type.0 = ContentType::Text; if !cst.eq_ignore_ascii_case(b"plain") { @@ -203,25 +203,23 @@ impl AttachmentBuilder { .as_bytes(), ) { Ok(ref s) => { - let s:Vec = s.clone(); - { - let slice = &s[..]; - if slice.find(b"\r\n").is_some() { - s.replace(b"\r\n", b"\n"); + let s: Vec = s.clone(); + { + let slice = &s[..]; + if slice.find(b"\r\n").is_some() { + s.replace(b"\r\n", b"\n"); + } } - } - s + s } - _ => self.raw.clone() + _ => self.raw.clone(), }, ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw) .to_full_result() .unwrap(), ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit - | ContentTransferEncoding::Other { .. } => { - self.raw.clone() - } + | ContentTransferEncoding::Other { .. } => self.raw.clone(), } } pub fn build(self) -> Attachment { @@ -235,7 +233,7 @@ impl AttachmentBuilder { b"mixed" => MultipartType::Mixed, b"alternative" => MultipartType::Alternative, b"digest" => MultipartType::Digest, - _ => MultipartType::Unsupported { tag:tag.clone() }, + _ => MultipartType::Unsupported { tag: tag.clone() }, }, _ => panic!(), }; diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index 53aba9903..c81e8959b 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -25,19 +25,19 @@ pub mod attachments; pub mod parser; -use parser::BytesExt; pub use self::attachments::*; use error::{MeliError, Result}; use mailbox::backends::BackendOpGenerator; +use parser::BytesExt; +use std::borrow::Cow; use std::cmp::Ordering; +use std::collections::hash_map::DefaultHasher; use std::fmt; +use std::hash::Hasher; use std::option::Option; use std::string::String; use std::sync::Arc; -use std::borrow::Cow; -use std::collections::hash_map::DefaultHasher; -use std::hash::Hasher; use chrono; use chrono::TimeZone; @@ -257,7 +257,7 @@ impl Envelope { Some(e) } pub fn set_operation_token(&mut self, operation_token: Box) { - self.operation_token= Arc::new(operation_token); + self.operation_token = Arc::new(operation_token); } pub fn populate_headers(&mut self) -> Result<()> { @@ -373,7 +373,10 @@ impl Envelope { } pub fn bytes(&self) -> Vec { let mut operation = self.operation_token.generate(); - operation.as_bytes().map(|v| v.into()).unwrap_or_else(|_| Vec::new()) + operation + .as_bytes() + .map(|v| v.into()) + .unwrap_or_else(|_| Vec::new()) } pub fn body(&self) -> Attachment { let mut operation = self.operation_token.generate(); @@ -385,7 +388,7 @@ impl Envelope { eprintln!("error in parsing mail\n{}", operation.description()); let error_msg = b"Mail cannot be shown because of errors."; let mut builder = AttachmentBuilder::new(error_msg); - return builder.build() + return builder.build(); } }; let mut builder = AttachmentBuilder::new(body); @@ -513,7 +516,8 @@ impl Envelope { } pub fn references(&self) -> Vec<&MessageID> { match self.references { - Some(ref s) => s.refs + Some(ref s) => s + .refs .iter() .fold(Vec::with_capacity(s.refs.len()), |mut acc, x| { acc.push(x); diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index b383ac102..487ffebc7 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -57,7 +57,8 @@ impl BytesExt for [u8] { } // https://stackoverflow.com/a/35907071 fn find(&self, needle: &[u8]) -> Option { - self.windows(needle.len()).position(|window| window == needle) + self.windows(needle.len()) + .position(|window| window == needle) } fn replace(&self, from: &[u8], to: &[u8]) -> Vec { let mut ret = self.to_vec(); @@ -68,7 +69,6 @@ impl BytesExt for [u8] { } } - fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> { if input.len() < 3 { IResult::Incomplete(Needed::Size(1)) @@ -107,9 +107,9 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> { let input_len = input.len(); for (i, x) in input.iter().enumerate() { if *x == b'\n' { - if (i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t' { - return IResult::Done(&input[(i + 1)..], &input[0..i]); - } else if i + 1 == input_len { + if ((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t') + || i + 1 == input_len + { return IResult::Done(&input[(i + 1)..], &input[0..i]); } } @@ -281,7 +281,8 @@ named!( acc += x.len(); acc }); - let bytes = list.iter() + let bytes = list + .iter() .fold(Vec::with_capacity(list_len), |mut acc, x| { acc.append(&mut x.clone()); acc @@ -636,8 +637,9 @@ pub fn date(input: &[u8]) -> Option> { let parsed_result = phrase(&eat_comments(input)) .to_full_result() .unwrap() - .replace(b"-",b"+"); - chrono::DateTime::parse_from_rfc2822(String::from_utf8_lossy(parsed_result.trim()).as_ref()).ok() + .replace(b"-", b"+"); + chrono::DateTime::parse_from_rfc2822(String::from_utf8_lossy(parsed_result.trim()).as_ref()) + .ok() } #[test] @@ -712,10 +714,11 @@ fn test_attachments() { named!( content_type_parameter<(&[u8], &[u8])>, do_parse!( - tag!(";") >> - name: terminated!(ws!(take_until!("=")) , tag!("=")) >> - value: ws!(alt_complete!( delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";"))) >> - ({ (name, value) }) + tag!(";") >> name: terminated!(ws!(take_until!("=")), tag!("=")) + >> value: + ws!(alt_complete!( + delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";") + )) >> ({ (name, value) }) ) ); diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index 8684ab937..fda6db66b 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -385,7 +385,8 @@ pub fn build_threads( if indentation > 0 && thread.has_message() { let subject = collection[thread.message().unwrap()].subject(); if subject == root_subject - || subject.starts_with("Re: ") && subject.as_ref().ends_with(root_subject.as_ref()) + || subject.starts_with("Re: ") + && subject.as_ref().ends_with(root_subject.as_ref()) { threads[i].set_show_subject(false); } diff --git a/src/bin.rs b/src/bin.rs index 064acfb93..65aabc699 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -63,7 +63,7 @@ fn make_input_thread( UIMode::Fork, ))); }, - rx, + &rx, ) }) .unwrap() @@ -134,11 +134,11 @@ fn main() { receiver.recv() -> r => { match r.unwrap() { ThreadEvent::Input(Key::Ctrl('z')) => { - state.to_main_screen(); + state.switch_to_main_screen(); //_thread_handler.join().expect("Couldn't join on the associated thread"); let self_pid = nix::unistd::Pid::this(); nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGSTOP).unwrap(); - state.to_alternate_screen(); + state.switch_to_alternate_screen(); _thread_handler = make_input_thread(sender.clone(), rx.clone()); // BUG: thread sends input event after one received key state.update_size(); diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 01cd46cf2..15c18e9cc 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -1,8 +1,28 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + use super::*; const MAX_COLS: usize = 500; - /// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a /// `MailView`. pub struct MailListing { @@ -21,6 +41,12 @@ pub struct MailListing { view: Option, } +impl Default for MailListing { + fn default() -> Self { + Self::new() + } +} + impl MailListing { /// Helper function to format entry strings for MailListing */ /* TODO: Make this configurable */ @@ -67,11 +93,9 @@ impl MailListing { // Get mailbox as a reference. // loop { - match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { - Ok(()) => { break; }, - Err(_) => { - // TODO: Show progress visually - } + // TODO: Show progress visually + if let Ok(()) = context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { + break; } } let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] @@ -92,7 +116,7 @@ impl MailListing { Color::Default, ((0, 0), (MAX_COLS - 1, 0)), true, - ); + ); self.content = content; return; } @@ -101,31 +125,29 @@ impl MailListing { if threaded { let mut indentations: Vec = Vec::with_capacity(6); let mut thread_idx = 0; // needed for alternate thread colors - /* Draw threaded view. */ + /* Draw threaded view. */ let mut local_collection: Vec = mailbox.threaded_collection.clone(); - let mut threads: Vec<&Container> = mailbox.threads.iter().map(|v| v).collect(); - local_collection.sort_by(|a, b| { - match self.sort { - (SortField::Date, SortOrder::Desc) => { - mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date()) - }, - (SortField::Date, SortOrder::Asc) => { - mailbox.thread(*a).date().cmp(&mailbox.thread(*b).date()) - }, - (SortField::Subject, SortOrder::Desc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - ma.subject().cmp(&mb.subject()) - }, - (SortField::Subject, SortOrder::Asc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - mb.subject().cmp(&ma.subject()) - }, + let threads: &Vec = &mailbox.threads; + local_collection.sort_by(|a, b| match self.sort { + (SortField::Date, SortOrder::Desc) => { + mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date()) + } + (SortField::Date, SortOrder::Asc) => { + mailbox.thread(*a).date().cmp(&mailbox.thread(*b).date()) + } + (SortField::Subject, SortOrder::Desc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + ma.subject().cmp(&mb.subject()) + } + (SortField::Subject, SortOrder::Asc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + mb.subject().cmp(&ma.subject()) } }); let mut iter = local_collection.iter().enumerate().peekable(); @@ -137,14 +159,14 @@ impl MailListing { .count(); /* This is just a desugared for loop so that we can use .peek() */ while let Some((idx, i)) = iter.next() { - let container = threads[*i]; + let container = &threads[*i]; let indentation = container.indentation(); if indentation == 0 { thread_idx += 1; } - assert!(container.has_message() == true); + assert!(container.has_message()); match iter.peek() { Some(&(_, x)) if threads[*x].indentation() == indentation => { indentations.pop(); @@ -180,13 +202,13 @@ impl MailListing { container, &indentations, len, - ), - &mut content, - fg_color, - bg_color, - ((0, idx), (MAX_COLS - 1, idx)), - false, - ); + ), + &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); @@ -236,7 +258,7 @@ impl MailListing { bg_color, ((0, y), (MAX_COLS - 1, y)), false, - ); + ); for x in x..MAX_COLS { content[(x, y)].set_ch(' '); @@ -269,15 +291,19 @@ impl MailListing { } else { Color::Default }; - let bg_color = - if !envelope.is_seen() { - Color::Byte(251) - } else if idx % 2 == 0 { - Color::Byte(236) - } else { - Color::Default - }; - change_colors(&mut self.content, ((0, idx), (MAX_COLS-1, idx)), fg_color, bg_color); + let bg_color = if !envelope.is_seen() { + Color::Byte(251) + } else if idx % 2 == 0 { + Color::Byte(236) + } else { + Color::Default + }; + change_colors( + &mut self.content, + ((0, idx), (MAX_COLS - 1, idx)), + fg_color, + bg_color, + ); } fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) { @@ -301,14 +327,12 @@ impl MailListing { }; let bg_color = if self.cursor_pos.2 == idx { Color::Byte(246) + } else if !envelope.is_seen() { + Color::Byte(251) + } else if idx % 2 == 0 { + Color::Byte(236) } else { - if !envelope.is_seen() { - Color::Byte(251) - } else if idx % 2 == 0 { - Color::Byte(236) - } else { - Color::Default - } + Color::Default }; change_colors(grid, area, fg_color, bg_color); } @@ -337,7 +361,7 @@ impl MailListing { if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no { let old_cursor_pos = self.cursor_pos; self.cursor_pos = self.new_cursor_pos; - for idx in [old_cursor_pos.2, self.new_cursor_pos.2].iter() { + for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] { if *idx >= self.length { continue; //bounds check } @@ -370,8 +394,6 @@ impl MailListing { context, ); context.dirty_areas.push_back(area); - - } fn make_thread_entry( @@ -379,7 +401,7 @@ impl MailListing { idx: usize, indent: usize, container: &Container, - indentations: &Vec, + indentations: &[bool], idx_width: usize, ) -> String { let has_sibling = container.has_sibling(); @@ -508,10 +530,9 @@ impl Component for MailListing { } { /* TODO: Move the box drawing business in separate functions */ - if get_x(upper_left) > 0 { - if grid[(get_x(upper_left) - 1, mid)].ch() == VERT_BOUNDARY { - grid[(get_x(upper_left) - 1, mid)].set_ch(LIGHT_VERTICAL_AND_RIGHT); - } + if get_x(upper_left) > 0 && grid[(get_x(upper_left) - 1, mid)].ch() == VERT_BOUNDARY + { + grid[(get_x(upper_left) - 1, mid)].set_ch(LIGHT_VERTICAL_AND_RIGHT); } for i in get_x(upper_left)..=get_x(bottom_right) { @@ -524,17 +545,18 @@ impl Component for MailListing { // 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)); + if let Some(v) = self.view.as_mut() { + 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().unwrap().draw( + grid, + (set_y(upper_left, mid + 1), bottom_right), + context, + ); self.dirty = false; - } } fn process_event(&mut self, event: &UIEvent, context: &mut Context) { @@ -551,11 +573,11 @@ impl Component for MailListing { self.dirty = true; } } - UIEventType::Input(Key::Char('\n')) if self.unfocused == false => { + UIEventType::Input(Key::Char('\n')) if !self.unfocused => { self.unfocused = true; self.dirty = true; } - UIEventType::Input(Key::Char('m')) if self.unfocused == false => { + UIEventType::Input(Key::Char('m')) if !self.unfocused => { use std::process::{Command, Stdio}; /* Kill input thread so that spawned command can be sole receiver of stdin */ { @@ -602,7 +624,7 @@ impl Component for MailListing { }); return; } - UIEventType::Input(Key::Char('i')) if self.unfocused == true => { + UIEventType::Input(Key::Char('i')) if self.unfocused => { self.unfocused = false; self.dirty = true; self.view = None; @@ -683,20 +705,20 @@ impl Component for MailListing { self.refresh_mailbox(context); self.dirty = true; return; - }, + } Action::ViewMailbox(idx) => { self.new_cursor_pos.1 = *idx; self.dirty = true; self.refresh_mailbox(context); return; - }, + } Action::Sort(field, order) => { self.sort = (field.clone(), order.clone()); self.dirty = true; self.refresh_mailbox(context); return; - }, - _ => {}, + } + _ => {} }, _ => {} } diff --git a/ui/src/components/mail/mod.rs b/ui/src/components/mail/mod.rs index 775c4f7bf..b15b069ae 100644 --- a/ui/src/components/mail/mod.rs +++ b/ui/src/components/mail/mod.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! Entities that handle Mail specific functions. */ use super::*; @@ -23,7 +44,7 @@ pub struct AccountMenu { } impl AccountMenu { - pub fn new(accounts: &Vec) -> Self { + pub fn new(accounts: &[Account]) -> Self { let accounts = accounts .iter() .enumerate() @@ -40,12 +61,18 @@ impl AccountMenu { }) .collect(); AccountMenu { - accounts: accounts, + accounts, dirty: true, cursor: None, } } - fn print_account(&self, grid: &mut CellBuffer, area: Area, a: &AccountMenuEntry, context: &mut Context) -> usize { + fn print_account( + &self, + grid: &mut CellBuffer, + area: Area, + a: &AccountMenuEntry, + context: &mut Context, + ) -> usize { if !is_valid_area!(area) { eprintln!("BUG: invalid area in print_account"); } @@ -70,7 +97,7 @@ impl AccountMenu { let mut inc = 0; let mut depth = String::from(""); - let mut s = String::from(format!("{}\n", a.name)); + let mut s = format!("{}\n", a.name); fn pop(depth: &mut String) { depth.pop(); depth.pop(); @@ -82,24 +109,33 @@ impl AccountMenu { fn print( root: usize, - parents: &Vec>, + parents: &[Option], depth: &mut String, - entries: &Vec<(usize, Folder)>, + entries: &[(usize, Folder)], s: &mut String, inc: &mut usize, index: usize, //account index context: &mut Context, - ) -> () { + ) -> () { let len = s.len(); match context.accounts[index].status(root) { - Ok(()) => {}, + Ok(()) => {} Err(_) => { return; // TODO: Show progress visually } } - let count = context.accounts[index][root].as_ref().unwrap().collection.iter().filter(|e| !e.is_seen()).count(); - s.insert_str(len, &format!("{} {} {}\n ", *inc, &entries[root].1.name(), count)); + let count = context.accounts[index][root] + .as_ref() + .unwrap() + .collection + .iter() + .filter(|e| !e.is_seen()) + .count(); + s.insert_str( + len, + &format!("{} {} {}\n ", *inc, &entries[root].1.name(), count), + ); *inc += 1; let children_no = entries[root].1.children().len(); for (idx, child) in entries[root].1.children().iter().enumerate() { @@ -111,7 +147,9 @@ impl AccountMenu { } } for r in roots { - print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc, a.index, context); + print( + r, &parents, &mut depth, &a.entries, &mut s, &mut inc, a.index, context, + ); } let lines: Vec<&str> = s.lines().collect(); @@ -125,9 +163,9 @@ impl AccountMenu { break; } let s = if idx == lines_len - 2 { - format!("{}", lines[idx].replace("├", "└")) + lines[idx].replace("├", "└") } else { - format!("{}", lines[idx]) + lines[idx].to_string() }; let (color_fg, color_bg) = if highlight { if idx > 1 && self.cursor.unwrap().1 == idx - 2 { diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 8c1a47cf8..8a8c22122 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + use super::*; use linkify::{Link, LinkFinder}; use std::process::{Command, Stdio}; @@ -40,9 +61,9 @@ impl MailView { subview: Option>, ) -> Self { MailView { - coordinates: coordinates, - pager: pager, - subview: subview, + coordinates, + pager, + subview, dirty: true, mode: ViewMode::Normal, @@ -73,9 +94,7 @@ impl Component for MailView { if self.mode == ViewMode::Raw { clear_area(grid, area); - context - .dirty_areas - .push_back(area); + context.dirty_areas.push_back(area); (envelope_idx, get_y(upper_left) - 1) } else { let (x, y) = write_string_to_grid( @@ -85,7 +104,7 @@ impl Component for MailView { Color::Default, area, true, - ); + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); @@ -98,7 +117,7 @@ impl Component for MailView { Color::Default, (set_y(upper_left, y + 1), bottom_right), true, - ); + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); @@ -111,7 +130,7 @@ impl Component for MailView { Color::Default, (set_y(upper_left, y + 1), bottom_right), true, - ); + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); @@ -124,7 +143,7 @@ impl Component for MailView { Color::Default, (set_y(upper_left, y + 1), bottom_right), true, - ); + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); @@ -137,7 +156,7 @@ impl Component for MailView { Color::Default, (set_y(upper_left, y + 1), bottom_right), true, - ); + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); @@ -174,9 +193,7 @@ impl Component for MailView { } t } - ViewMode::Raw => { - String::from_utf8_lossy(&envelope.bytes()).into_owned() - }, + ViewMode::Raw => String::from_utf8_lossy(&envelope.bytes()).into_owned(), ViewMode::Url => { let mut t = envelope.body().text().to_string(); for (lidx, l) in finder.links(&envelope.body().text()).enumerate() { @@ -195,28 +212,25 @@ impl Component for MailView { } ViewMode::Attachment(aidx) => { let attachments = envelope.body().attachments(); - let mut ret = format!("Viewing attachment. Press `r` to return \n"); + let mut ret = "Viewing attachment. Press `r` to return \n".to_string(); ret.push_str(&attachments[aidx].text()); ret } }; let mut buf = CellBuffer::from(&text); - match self.mode { - ViewMode::Url => { - // URL indexes must be colored (ugh..) - let lines: Vec<&str> = text.split('\n').collect(); - let mut shift = 0; - for r in lines.iter() { - for l in finder.links(&r) { - buf[(l.start() + shift - 1, 0)].set_fg(Color::Byte(226)); - buf[(l.start() + shift - 2, 0)].set_fg(Color::Byte(226)); - buf[(l.start() + shift - 3, 0)].set_fg(Color::Byte(226)); - } - // Each Cell represents one char so next line will be: - shift += r.chars().count() + 1; + if self.mode == ViewMode::Url { + // URL indexes must be colored (ugh..) + let lines: Vec<&str> = text.split('\n').collect(); + let mut shift = 0; + for r in &lines { + for l in finder.links(&r) { + buf[(l.start() + shift - 1, 0)].set_fg(Color::Byte(226)); + buf[(l.start() + shift - 2, 0)].set_fg(Color::Byte(226)); + buf[(l.start() + shift - 3, 0)].set_fg(Color::Byte(226)); } + // Each Cell represents one char so next line will be: + shift += r.chars().count() + 1; } - _ => {} } buf }; @@ -225,12 +239,13 @@ impl Component for MailView { } else { self.pager.as_mut().map(|p| p.cursor_pos()) }; - self.pager = Some(Pager::from_buf(buf, cursor_pos)); + self.pager = Some(Pager::from_buf(&buf, cursor_pos)); self.dirty = false; } - self.pager - .as_mut() - .map(|p| p.draw(grid, (set_y(upper_left, y + 1), bottom_right), context)); + + if let Some(p) = self.pager.as_mut() { + p.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); + } } fn process_event(&mut self, event: &UIEvent, context: &mut Context) { @@ -241,7 +256,9 @@ impl Component for MailView { UIEventType::Input(Key::Char(c)) if c >= '0' && c <= '9' => { self.cmd_buf.push(c); } - UIEventType::Input(Key::Char('r')) if self.mode == ViewMode::Normal || self.mode == ViewMode::Raw => { + UIEventType::Input(Key::Char('r')) + if self.mode == ViewMode::Normal || self.mode == ViewMode::Raw => + { self.mode = if self.mode == ViewMode::Raw { ViewMode::Normal } else { @@ -254,7 +271,7 @@ impl Component for MailView { self.dirty = true; } UIEventType::Input(Key::Char('a')) - if self.cmd_buf.len() > 0 && self.mode == ViewMode::Normal => + if !self.cmd_buf.is_empty() && self.mode == ViewMode::Normal => { let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); @@ -282,9 +299,9 @@ impl Component for MailView { ContentType::Multipart { .. } => { context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::StatusNotification(format!( - "Multipart attachments are not supported yet." - )), + event_type: UIEventType::StatusNotification( + "Multipart attachments are not supported yet.".to_string(), + ), }); return; } @@ -298,7 +315,9 @@ impl Component for MailView { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() - .expect(&format!("Failed to start {}", binary.display())); + .unwrap_or_else(|_| { + panic!("Failed to start {}", binary.display()) + }); } else { context.replies.push_back(UIEvent { id: 0, @@ -324,7 +343,7 @@ impl Component for MailView { }; } UIEventType::Input(Key::Char('g')) - if self.cmd_buf.len() > 0 && self.mode == ViewMode::Url => + if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url => { let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); @@ -378,10 +397,8 @@ impl Component for MailView { } if let Some(ref mut sub) = self.subview { sub.process_event(event, context); - } else { - if let Some(ref mut p) = self.pager { - p.process_event(event, context); - } + } else if let Some(ref mut p) = self.pager { + p.process_event(event, context); } } fn is_dirty(&self) -> bool { diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs index c900d1e2a..490c6f1c8 100644 --- a/ui/src/components/mod.rs +++ b/ui/src/components/mod.rs @@ -1,5 +1,5 @@ /* - * meli - ui module. + * meli - ui crate. * * Copyright 2017-2018 Manos Pitsidianakis * @@ -35,7 +35,6 @@ pub mod notifications; pub mod utilities; pub use self::utilities::*; - use super::{Key, UIEvent, UIEventType}; /// The upper and lower boundary char. const HORZ_BOUNDARY: char = '─'; @@ -140,11 +139,8 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, let mut src_y = get_y(upper_left!(src)); let (cols, rows) = grid_src.size(); if src_x >= cols || src_y >= rows { - eprintln!( - "DEBUG: src area outside of grid_src in copy_area", - ); + eprintln!("DEBUG: src area outside of grid_src in copy_area",); return; - } for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) { diff --git a/ui/src/components/notifications.rs b/ui/src/components/notifications.rs index 7c394281f..1478420b9 100644 --- a/ui/src/components/notifications.rs +++ b/ui/src/components/notifications.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! Notification handling components. */ @@ -11,16 +32,13 @@ pub struct XDGNotifications {} impl Component for XDGNotifications { 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(); - } - _ => {} + if let UIEventType::Notification(ref t) = event.event_type { + 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 f28ddf05c..e41d8826f 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! Various useful components that can be used in a generic fashion. */ use super::*; @@ -13,10 +34,10 @@ pub struct HSplit { impl HSplit { pub fn new(top: Entity, bottom: Entity, ratio: usize, show_divider: bool) -> Self { HSplit { - top: top, - bottom: bottom, - show_divider: show_divider, - ratio: ratio, + top, + bottom, + show_divider, + ratio, } } } @@ -37,7 +58,7 @@ impl Component for HSplit { grid[(i, mid)].set_ch('─'); } } - let _ = self.top.component.draw( + self.top.component.draw( grid, ( upper_left, @@ -45,7 +66,7 @@ impl Component for HSplit { ), context, ); - let _ = self.bottom.component.draw( + self.bottom.component.draw( grid, ((get_x(upper_left), get_y(upper_left) + mid), bottom_right), context, @@ -72,10 +93,10 @@ pub struct VSplit { impl VSplit { pub fn new(left: Entity, right: Entity, ratio: usize, show_divider: bool) -> Self { VSplit { - left: left, - right: right, - show_divider: show_divider, - ratio: ratio, + left, + right, + show_divider, + ratio, } } } @@ -92,14 +113,12 @@ impl Component for VSplit { 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) + 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); - } - _ => {} + if let HORZ_BOUNDARY = c { + grid[(mid, get_y(upper_left) - 1)].set_ch(LIGHT_DOWN_AND_HORIZONTAL); } } @@ -110,7 +129,8 @@ impl Component for VSplit { grid[(mid, i)].set_bg(Color::Default); } if get_y(bottom_right) > 1 { - let c = grid.get(mid, get_y(bottom_right) - 1) + let c = grid + .get(mid, get_y(bottom_right) - 1) .map(|a| a.ch()) .unwrap_or_else(|| ' '); match c { @@ -121,14 +141,12 @@ impl Component for VSplit { } } } - 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); + self.left + .component + .draw(grid, (upper_left, (mid - 1, get_y(bottom_right))), context); + 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); @@ -199,35 +217,33 @@ impl Pager { Pager::print_string(&mut content, s); Pager { cursor_pos: cursor_pos.unwrap_or(0), - height: height, - width: width, + height, + width, dirty: true, - content: content, + content, } } - pub fn from_buf(buf: CellBuffer, cursor_pos: Option) -> Self { + pub fn from_buf(buf: &CellBuffer, cursor_pos: Option) -> Self { let lines: Vec<&[Cell]> = buf.split(|cell| cell.ch() == '\n').collect(); let height = lines.len(); let width = lines.iter().map(|l| l.len()).max().unwrap_or(0) + 1; let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); { let mut x; - let mut y = 0; let c_slice: &mut [Cell] = &mut content; - for l in lines { + for (y, l) in lines.iter().enumerate() { let y_r = y * width; x = l.len() + y_r; c_slice[y_r..x].copy_from_slice(l); c_slice[x].set_ch('\n'); - y += 1; } } Pager { cursor_pos: cursor_pos.unwrap_or(0), - height: height, - width: width, + height, + width, dirty: true, - content: content, + content, } } pub fn print_string(content: &mut CellBuffer, s: &str) { @@ -320,7 +336,7 @@ pub struct StatusBar { impl StatusBar { pub fn new(container: Entity) -> Self { StatusBar { - container: container, + container, status: String::with_capacity(256), notifications: VecDeque::new(), ex_buffer: String::with_capacity(256), @@ -376,7 +392,7 @@ impl Component for StatusBar { } let height = self.height; - let _ = self.container.component.draw( + self.container.component.draw( grid, ( upper_left, @@ -414,14 +430,12 @@ impl Component for StatusBar { match &event.event_type { UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => { match context.accounts[*idx_a].status(*idx_f) { - Ok(()) => {}, + Ok(()) => {} Err(_) => { return; } } - let m = &context.accounts[*idx_a][*idx_f] - .as_ref() - .unwrap(); + let m = &context.accounts[*idx_a][*idx_f].as_ref().unwrap(); self.status = format!( "{} | Mailbox: {}, Messages: {}, New: {}", self.mode, @@ -435,7 +449,7 @@ impl Component for StatusBar { let offset = self.status.find('|').unwrap_or_else(|| self.status.len()); self.status.replace_range(..offset, &format!("{} ", m)); self.dirty = true; - self.mode = m.clone(); + self.mode = *m; match m { UIMode::Normal => { self.height = 1; @@ -498,7 +512,7 @@ impl Progress { pub fn new(s: String, total_work: usize) -> Self { Progress { description: s, - total_work: total_work, + total_work, finished: 0, } } diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index e615670b4..105fa5327 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! * User actions that need to be handled by the UI */ @@ -21,7 +42,6 @@ pub enum SortField { Date, } - impl FromStr for SortField { type Err = (); fn from_str(s: &str) -> Result { @@ -30,7 +50,7 @@ impl FromStr for SortField { "subject" | "s" | "sub" | "sbj" | "subj" => { eprintln!("parsed: subject"); } - "date" | "d" => { + "date" | "d" => { eprintln!("parsed date"); } _ => { @@ -39,8 +59,8 @@ impl FromStr for SortField { } match s.trim() { "subject" | "s" | "sub" | "sbj" | "subj" => Ok(SortField::Subject), - "date" | "d" => Ok(SortField::Date), - _ => Err(()) + "date" | "d" => Ok(SortField::Date), + _ => Err(()), } } } @@ -52,7 +72,7 @@ impl FromStr for SortOrder { match s.trim() { "asc" => Ok(SortOrder::Asc), "desc" => Ok(SortOrder::Desc), - _ => Err(()) + _ => Err(()), } } } diff --git a/ui/src/execute/mod.rs b/ui/src/execute/mod.rs index d37d23510..307721144 100644 --- a/ui/src/execute/mod.rs +++ b/ui/src/execute/mod.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! A parser module for user commands passed through the Ex mode. */ use nom::{digit, not_line_ending}; @@ -5,7 +26,6 @@ use std; pub mod actions; pub use actions::*; - named!( usize_c, map_res!( @@ -14,46 +34,50 @@ named!( ) ); -named!(sortfield, - map_res!( - map_res!(take_until_s!(" "), std::str::from_utf8), - std::str::FromStr::from_str)); +named!( + sortfield, + map_res!( + map_res!(take_until_s!(" "), std::str::from_utf8), + std::str::FromStr::from_str + ) +); +named!( + sortorder, + map_res!( + map_res!(call!(not_line_ending), std::str::from_utf8), + std::str::FromStr::from_str + ) +); -named!(sortorder, - map_res!( - map_res!(call!(not_line_ending), std::str::from_utf8), - std::str::FromStr::from_str)); +named!( + goto, + preceded!(tag!("b "), map!(call!(usize_c), Action::ViewMailbox)) +); +named!( + subsort, + do_parse!(tag!("subsort ") >> p: pair!(sortfield, sortorder) >> (Action::SubSort(p.0, p.1))) +); +named!( + sort, + do_parse!( + tag!("sort ") + >> p: separated_pair!(sortfield, tag!(" "), sortorder) + >> (Action::Sort(p.0, p.1)) + ) +); - -named!(goto, - preceded!(tag!("b "), - map!(call!(usize_c), |v| Action::ViewMailbox(v)) - )); - -named!(subsort, do_parse!( - tag!("subsort ") >> - p: pair!(sortfield,sortorder) >> - ( - Action::SubSort(p.0, p.1) - - ) - )); -named!(sort, do_parse!( - tag!("sort ") >> - p: separated_pair!(sortfield,tag!(" "), sortorder) >> - ( - Action::Sort(p.0, p.1) - - ) - )); - -named!(threaded, - map!(ws!(tag!("threaded")), |_| Action::MailListing(MailListingAction::ToggleThreaded))); -named!(toggle, - preceded!(tag!("toggle "), - alt_complete!( threaded ))); +named!( + threaded, + map!(ws!(tag!("threaded")), |_| { + Action::MailListing(MailListingAction::ToggleThreaded) + }) +); +named!( + toggle, + preceded!(tag!("toggle "), alt_complete!(threaded)) +); named!(pub parse_command, alt_complete!( goto | toggle | sort | subsort) diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 9d49f4dbb..5ae7b78b0 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -1,7 +1,7 @@ /* - * meli - ui module. + * meli - ui crate. * - * Copyright 2017 Manos Pitsidianakis + * Copyright 2017-2018 Manos Pitsidianakis * * This file is part of meli. * diff --git a/ui/src/state.rs b/ui/src/state.rs index f910a3916..edc5135dd 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! The application's state. 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. @@ -10,13 +31,12 @@ use super::*; use chan::Sender; use fnv::FnvHashMap; -use termion::raw::IntoRawMode; -use termion::screen::AlternateScreen; -use termion::{clear, cursor, style}; use std::io::Write; use std::thread; use std::time; - +use termion::raw::IntoRawMode; +use termion::screen::AlternateScreen; +use termion::{clear, cursor, style}; /// A context container for loaded settings, accounts, UI changes, etc. pub struct Context { @@ -116,39 +136,41 @@ impl State { sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)); thread::sleep(dur); } - }).unwrap() + }) + .unwrap() }; let mut s = State { - cols: cols, - rows: rows, + cols, + rows, grid: CellBuffer::new(cols, rows, Cell::with_char(' ')), stdout: Some(stdout), child: None, mode: UIMode::Normal, - sender: sender, + sender, entities: Vec::with_capacity(1), context: Context { - accounts: accounts, + accounts, _backends: backends, settings: settings.clone(), runtime_settings: settings, dirty_areas: VecDeque::with_capacity(5), replies: VecDeque::with_capacity(5), - input_thread: input_thread, + input_thread, }, startup_thread: Some(startup_tx), threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()), }; - s.threads.insert(startup_thread.thread().id(), startup_thread); + s.threads + .insert(startup_thread.thread().id(), startup_thread); write!( s.stdout(), "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1, 1) - ).unwrap(); + ).unwrap(); s.flush(); for account in &mut s.context.accounts { let sender = s.sender.clone(); @@ -174,14 +196,14 @@ impl State { return; } { - let tx = self.startup_thread.take().unwrap(); + let tx = self.startup_thread.take().unwrap(); tx.send(true); } } /// Switch back to the terminal's main screen (The command line the user sees before opening /// the application) - pub fn to_main_screen(&mut self) { + pub fn switch_to_main_screen(&mut self) { write!( self.stdout(), "{}{}", @@ -192,7 +214,7 @@ impl State { self.stdout = None; self.context.input_thread.send(false); } - pub fn to_alternate_screen(&mut self) { + pub fn switch_to_alternate_screen(&mut self) { let s = std::io::stdout(); s.lock(); self.stdout = Some(AlternateScreen::from(s.into_raw_mode().unwrap())); @@ -312,13 +334,16 @@ impl State { self.entities.push(entity); } /// Convert user commands to actions/method calls. - fn parse_command(&mut self, cmd: String) { + fn parse_command(&mut self, cmd: &str) { eprintln!("cmd is {}", cmd); let result = parse_command(&cmd.as_bytes()).to_full_result(); eprintln!("rseult is {:?}", result); if let Ok(v) = result { - self.rcv_event(UIEvent { id: 0, event_type: UIEventType::Action(v) }); + self.rcv_event(UIEvent { + id: 0, + event_type: UIEventType::Action(v), + }); } } @@ -327,7 +352,7 @@ impl State { match event.event_type { // Command type is handled only by State. UIEventType::Command(cmd) => { - self.parse_command(cmd); + self.parse_command(&cmd); return; } UIEventType::Fork(child) => { @@ -351,7 +376,7 @@ impl State { let mut f = file.file(); f.read_to_end(&mut buf).unwrap(); - in_pipe.write(&buf).unwrap(); + in_pipe.write_all(&buf).unwrap(); std::fs::remove_file(file.path()).unwrap(); } output.wait_with_output().expect("Failed to read stdout"); @@ -378,7 +403,7 @@ impl State { } if !self.context.replies.is_empty() { - let replies: Vec= self.context.replies.drain(0..).collect(); + let replies: Vec = self.context.replies.drain(0..).collect(); // Pass replies to self and call count on the map iterator to force evaluation replies.into_iter().map(|r| self.rcv_event(r)).count(); } @@ -402,33 +427,32 @@ impl State { } pub fn try_wait_on_child(&mut self) -> Option { - if { - match self.child { - Some(ForkType::NewDraft(_, ref mut c)) => { - let mut w = c.try_wait(); - match w { - Ok(Some(_)) => true, - Ok(None) => false, - Err(_) => { - return None; - } + if match self.child { + Some(ForkType::NewDraft(_, ref mut c)) => { + let mut w = c.try_wait(); + match w { + 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; - } - } - } - _ => { - 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; + } + } + } + _ => { + return None; + } + } + { if let Some(ForkType::NewDraft(f, _)) = std::mem::replace(&mut self.child, None) { self.rcv_event(UIEvent { id: 0, @@ -440,7 +464,7 @@ impl State { Some(false) } fn flush(&mut self) { - self.stdout.as_mut().map(|s| s.flush().unwrap()); + if let Some(s) = self.stdout.as_mut() { s.flush().unwrap(); } } fn stdout(&mut self) -> &mut termion::screen::AlternateScreen> { self.stdout.as_mut().unwrap() diff --git a/ui/src/types/cells.rs b/ui/src/types/cells.rs index fb0e85d27..1e1c01694 100644 --- a/ui/src/types/cells.rs +++ b/ui/src/types/cells.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! Define a (x, y) point in the terminal display as a holder of a character, foreground/background colors and attributes. @@ -88,8 +109,8 @@ impl CellBuffer { /// `cell` as a blank. pub fn new(cols: usize, rows: usize, cell: Cell) -> CellBuffer { CellBuffer { - cols: cols, - rows: rows, + cols, + rows, buf: vec![cell; cols * rows], } } @@ -130,13 +151,13 @@ impl CellAccessor for CellBuffer { impl Deref for CellBuffer { type Target = [Cell]; - fn deref<'a>(&'a self) -> &'a [Cell] { + fn deref(&self) -> &[Cell] { &self.buf } } impl DerefMut for CellBuffer { - fn deref_mut<'a>(&'a mut self) -> &'a mut [Cell] { + fn deref_mut(&mut self) -> &mut [Cell] { &mut self.buf } } @@ -144,14 +165,14 @@ impl DerefMut for CellBuffer { impl Index for CellBuffer { type Output = Cell; - fn index<'a>(&'a self, index: Pos) -> &'a Cell { + fn index(&self, index: Pos) -> &Cell { let (x, y) = index; self.get(x, y).expect("index out of bounds") } } impl IndexMut for CellBuffer { - fn index_mut<'a>(&'a mut self, index: Pos) -> &'a mut Cell { + fn index_mut(&mut self, index: Pos) -> &mut Cell { let (x, y) = index; self.get_mut(x, y).expect("index out of bounds") } @@ -178,7 +199,7 @@ impl<'a> From<&'a String> for CellBuffer { 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 { + for x in 0..self.cols { let c: &char = &self[(x, y)].ch(); write!(f, "{}", *c).unwrap(); if *c == '\n' { @@ -217,12 +238,7 @@ impl Cell { /// assert_eq!(cell.attrs(), Attr::Default); /// ``` pub fn new(ch: char, fg: Color, bg: Color, attrs: Attr) -> Cell { - Cell { - ch: ch, - fg: fg, - bg: bg, - attrs: attrs, - } + Cell { ch, fg, bg, attrs } } /// Creates a new `Cell` with the given `char` and default style. @@ -426,8 +442,8 @@ pub enum Color { impl Color { /// Returns the `u8` representation of the `Color`. - pub fn as_byte(&self) -> u8 { - match *self { + pub fn as_byte(self) -> u8 { + match self { Color::Black => 0x00, Color::Red => 0x01, Color::Green => 0x02, @@ -441,8 +457,8 @@ impl Color { } } - pub fn as_termion(&self) -> AnsiValue { - match *self { + pub fn as_termion(self) -> AnsiValue { + match self { b @ Color::Black | b @ Color::Red | b @ Color::Green diff --git a/ui/src/types/helpers.rs b/ui/src/types/helpers.rs index e5484018f..ab083055f 100644 --- a/ui/src/types/helpers.rs +++ b/ui/src/types/helpers.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + use std; use std::io::Write; use std::path::PathBuf; @@ -42,7 +63,7 @@ pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { let mut f = std::fs::File::create(path).unwrap(); - f.write(bytes).unwrap(); + f.write_all(bytes).unwrap(); f.flush().unwrap(); File { path: path.clone() } } diff --git a/ui/src/types/keys.rs b/ui/src/types/keys.rs index f1b6bbf60..e4efa86ea 100644 --- a/ui/src/types/keys.rs +++ b/ui/src/types/keys.rs @@ -1,8 +1,28 @@ -use termion::event::Key as TermionKey; -use termion::input::TermRead; +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + use chan; use std::io; - +use termion::event::Key as TermionKey; +use termion::input::TermRead; #[derive(Debug)] pub enum Key { @@ -83,7 +103,7 @@ pub fn get_events( stdin: io::Stdin, mut closure: impl FnMut(Key), mut exit: impl FnMut(), - rx: chan::Receiver, + rx: &chan::Receiver, ) -> () { for c in stdin.keys() { chan_select! { diff --git a/ui/src/types/mod.rs b/ui/src/types/mod.rs index 914083768..0dd55a83e 100644 --- a/ui/src/types/mod.rs +++ b/ui/src/types/mod.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + #[macro_use] mod cells; #[macro_use] @@ -15,8 +36,8 @@ use super::execute::Action; use melib::RefreshEvent; use std; -use std::thread; use std::fmt; +use std::thread; /// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads /// to the main process. diff --git a/ui/src/types/position.rs b/ui/src/types/position.rs index 277a5ed40..34b89f18f 100644 --- a/ui/src/types/position.rs +++ b/ui/src/types/position.rs @@ -1,3 +1,24 @@ +/* + * meli - ui crate. + * + * Copyright 2017-2018 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + /*! Simple type definitions and macro helper for a (x,y) position on the terminal and the areas they define. @@ -25,30 +46,65 @@ pub fn set_y(p: Pos, new_y: usize) -> Pos { } /// An `Area` consists of two points: the upper left and bottom right corners. +/// +/// Example: +/// ``` +/// use ui::position; +/// +/// let new_area = ((0, 0), (1, 1)); +/// ``` pub type Area = (Pos, Pos); +/// Get the upper left Position of an area +/// +/// Example: +/// ``` +/// use ui::position; +/// +/// let new_area = ((0, 0), (1, 1)); +/// assert_eq!(upper_left!(new_area), (0, 0)); +/// ``` #[macro_export] macro_rules! upper_left { ($a:expr) => { $a.0 }; } + +/// Get the bottom right Position of an area +/// +/// Example: +/// ``` +/// use ui::position; +/// +/// let new_area = ((0, 0), (1, 1)); +/// assert_eq!(bottom_right!(new_area), (1, 1)); +/// ``` #[macro_export] macro_rules! bottom_right { ($a:expr) => { $a.1 }; } + +/// Check if area is valid. +/// +/// Example: +/// ``` +/// use ui::position; +/// +/// let valid_area = ((0, 0), (1, 1)); +/// assert!(is_valid_area!(valid_area)); +/// +/// let invalid_area = ((2, 2), (1, 1)); +/// assert!(!is_valid_area!(invalid_area)); +/// #[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 - } + !(get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right)) }}; }