mirror of
https://git.meli.delivery/meli/meli
synced 2024-10-30 21:20:34 +00:00
Run clippy and rustfmt
This commit is contained in:
parent
43ad31d2ab
commit
c30f77a312
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Primitive Async/Wait implementation.
|
||||
*
|
||||
@ -69,7 +90,7 @@ impl<T> Async<T> {
|
||||
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<AsyncStatus, ()> {
|
||||
if self.value.is_some() {
|
||||
return Ok(AsyncStatus::Finished);
|
||||
@ -99,4 +120,3 @@ impl<T> Async<T> {
|
||||
return Ok(AsyncStatus::Finished);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TODO: Move this to `ui` crate.
|
||||
|
||||
fn false_val() -> bool {
|
||||
true
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
pub mod async;
|
||||
pub mod conf;
|
||||
pub mod error;
|
||||
pub mod mailbox;
|
||||
pub mod async;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
@ -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<Vec<Envelope>>) -> () {
|
||||
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<usize> for Account {
|
||||
type Output = Result<Mailbox>;
|
||||
fn index(&self, index: usize) -> &Result<Mailbox> {
|
||||
&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<usize> for Account {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Result<Mailbox> {
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 {
|
||||
|
@ -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<Envelope> = 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)
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,15 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* 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)]
|
||||
|
@ -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;
|
||||
|
@ -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<u8> = Vec::with_capacity(v.len()+4);
|
||||
let mut vec: Vec<u8> = 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<u8> = s.clone();
|
||||
{
|
||||
let slice = &s[..];
|
||||
if slice.find(b"\r\n").is_some() {
|
||||
s.replace(b"\r\n", b"\n");
|
||||
let s: Vec<u8> = 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!(),
|
||||
};
|
||||
|
@ -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<BackendOpGenerator>) {
|
||||
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<u8> {
|
||||
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);
|
||||
|
@ -57,7 +57,8 @@ impl BytesExt for [u8] {
|
||||
}
|
||||
// https://stackoverflow.com/a/35907071
|
||||
fn find(&self, needle: &[u8]) -> Option<usize> {
|
||||
self.windows(needle.len()).position(|window| window == needle)
|
||||
self.windows(needle.len())
|
||||
.position(|window| window == needle)
|
||||
}
|
||||
fn replace(&self, from: &[u8], to: &[u8]) -> Vec<u8> {
|
||||
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<chrono::DateTime<chrono::FixedOffset>> {
|
||||
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) })
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<MailView>,
|
||||
}
|
||||
|
||||
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<bool> = 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<usize> = 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<Container> = &mailbox.threads;
|
||||
local_collection.sort_by(|a, b| match self.sort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date())
|
||||
}
|
||||
(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<bool>,
|
||||
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;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Entities that handle Mail specific functions.
|
||||
*/
|
||||
use super::*;
|
||||
@ -23,7 +44,7 @@ pub struct AccountMenu {
|
||||
}
|
||||
|
||||
impl AccountMenu {
|
||||
pub fn new(accounts: &Vec<Account>) -> 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<Option<usize>>,
|
||||
parents: &[Option<usize>],
|
||||
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 {
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use linkify::{Link, LinkFinder};
|
||||
use std::process::{Command, Stdio};
|
||||
@ -40,9 +61,9 @@ impl MailView {
|
||||
subview: Option<Box<MailView>>,
|
||||
) -> 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::<usize>().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::<usize>().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 {
|
||||
|
@ -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)) {
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! 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<usize>) -> Self {
|
||||
pub fn from_buf(buf: &CellBuffer, cursor_pos: Option<usize>) -> 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,
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* 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<Self, Self::Err> {
|
||||
@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! 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<usize>,
|
||||
map_res!(
|
||||
@ -14,46 +34,50 @@ named!(
|
||||
)
|
||||
);
|
||||
|
||||
named!(sortfield<SortField>,
|
||||
map_res!(
|
||||
map_res!(take_until_s!(" "), std::str::from_utf8),
|
||||
std::str::FromStr::from_str));
|
||||
named!(
|
||||
sortfield<SortField>,
|
||||
map_res!(
|
||||
map_res!(take_until_s!(" "), std::str::from_utf8),
|
||||
std::str::FromStr::from_str
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
sortorder<SortOrder>,
|
||||
map_res!(
|
||||
map_res!(call!(not_line_ending), std::str::from_utf8),
|
||||
std::str::FromStr::from_str
|
||||
)
|
||||
);
|
||||
|
||||
named!(sortorder<SortOrder>,
|
||||
map_res!(
|
||||
map_res!(call!(not_line_ending), std::str::from_utf8),
|
||||
std::str::FromStr::from_str));
|
||||
named!(
|
||||
goto<Action>,
|
||||
preceded!(tag!("b "), map!(call!(usize_c), Action::ViewMailbox))
|
||||
);
|
||||
|
||||
named!(
|
||||
subsort<Action>,
|
||||
do_parse!(tag!("subsort ") >> p: pair!(sortfield, sortorder) >> (Action::SubSort(p.0, p.1)))
|
||||
);
|
||||
named!(
|
||||
sort<Action>,
|
||||
do_parse!(
|
||||
tag!("sort ")
|
||||
>> p: separated_pair!(sortfield, tag!(" "), sortorder)
|
||||
>> (Action::Sort(p.0, p.1))
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
named!(goto<Action>,
|
||||
preceded!(tag!("b "),
|
||||
map!(call!(usize_c), |v| Action::ViewMailbox(v))
|
||||
));
|
||||
|
||||
named!(subsort<Action>, do_parse!(
|
||||
tag!("subsort ") >>
|
||||
p: pair!(sortfield,sortorder) >>
|
||||
(
|
||||
Action::SubSort(p.0, p.1)
|
||||
|
||||
)
|
||||
));
|
||||
named!(sort<Action>, do_parse!(
|
||||
tag!("sort ") >>
|
||||
p: separated_pair!(sortfield,tag!(" "), sortorder) >>
|
||||
(
|
||||
Action::Sort(p.0, p.1)
|
||||
|
||||
)
|
||||
));
|
||||
|
||||
named!(threaded<Action>,
|
||||
map!(ws!(tag!("threaded")), |_| Action::MailListing(MailListingAction::ToggleThreaded)));
|
||||
named!(toggle<Action>,
|
||||
preceded!(tag!("toggle "),
|
||||
alt_complete!( threaded )));
|
||||
named!(
|
||||
threaded<Action>,
|
||||
map!(ws!(tag!("threaded")), |_| {
|
||||
Action::MailListing(MailListingAction::ToggleThreaded)
|
||||
})
|
||||
);
|
||||
named!(
|
||||
toggle<Action>,
|
||||
preceded!(tag!("toggle "), alt_complete!(threaded))
|
||||
);
|
||||
|
||||
named!(pub parse_command<Action>,
|
||||
alt_complete!( goto | toggle | sort | subsort)
|
||||
|
@ -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.
|
||||
*
|
||||
|
114
ui/src/state.rs
114
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! 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<std::io::Stdout> {
|
||||
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<std::io::Stdout> {
|
||||
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<std::io::Stdout> {
|
||||
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<W: Write> State<W> {
|
||||
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<W: Write> State<W> {
|
||||
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<W: Write> State<W> {
|
||||
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<W: Write> State<W> {
|
||||
}
|
||||
|
||||
if !self.context.replies.is_empty() {
|
||||
let replies: Vec<UIEvent>= self.context.replies.drain(0..).collect();
|
||||
let replies: Vec<UIEvent> = 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<W: Write> State<W> {
|
||||
}
|
||||
|
||||
pub fn try_wait_on_child(&mut self) -> Option<bool> {
|
||||
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<W: Write> State<W> {
|
||||
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<termion::raw::RawTerminal<W>> {
|
||||
self.stdout.as_mut().unwrap()
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
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<Pos> 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<Pos> 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
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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() }
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<bool>,
|
||||
rx: &chan::Receiver<bool>,
|
||||
) -> () {
|
||||
for c in stdin.keys() {
|
||||
chan_select! {
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[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.
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
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))
|
||||
}};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user