Run clippy lints
parent
b617fc0136
commit
2b6d1e0dbf
|
@ -58,6 +58,12 @@ pub struct Async<T> {
|
||||||
rx: chan::Receiver<AsyncStatus>,
|
rx: chan::Receiver<AsyncStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for AsyncBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
AsyncBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsyncBuilder {
|
impl AsyncBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let (sender, receiver) = chan::sync(::std::mem::size_of::<AsyncStatus>());
|
let (sender, receiver) = chan::sync(::std::mem::size_of::<AsyncStatus>());
|
||||||
|
@ -117,6 +123,6 @@ impl<T> Async<T> {
|
||||||
}
|
}
|
||||||
let v = self.worker.take().unwrap().join().unwrap();
|
let v = self.worker.take().unwrap().join().unwrap();
|
||||||
self.value = Some(v);
|
self.value = Some(v);
|
||||||
return Ok(AsyncStatus::Finished);
|
Ok(AsyncStatus::Finished)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,14 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern crate xdg;
|
|
||||||
extern crate bincode;
|
extern crate bincode;
|
||||||
|
extern crate xdg;
|
||||||
|
|
||||||
use async::*;
|
use async::*;
|
||||||
use conf::AccountSettings;
|
use conf::AccountSettings;
|
||||||
use error::{MeliError, Result};
|
use error::{MeliError, Result};
|
||||||
use mailbox::backends::{
|
use mailbox::backends::{
|
||||||
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent,
|
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer,
|
||||||
RefreshEventConsumer,
|
|
||||||
};
|
};
|
||||||
use mailbox::email::parser;
|
use mailbox::email::parser;
|
||||||
use mailbox::email::{Envelope, Flag};
|
use mailbox::email::{Envelope, Flag};
|
||||||
|
@ -46,11 +45,11 @@ extern crate crossbeam;
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::sync::{Mutex, Arc};
|
|
||||||
use std::hash::{Hasher, Hash};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
extern crate fnv;
|
extern crate fnv;
|
||||||
use self::fnv::FnvHashMap;
|
use self::fnv::FnvHashMap;
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ impl Clone for MaildirOp {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
MaildirOp {
|
MaildirOp {
|
||||||
hash_index: self.hash_index.clone(),
|
hash_index: self.hash_index.clone(),
|
||||||
hash: self.hash.clone(),
|
hash: self.hash,
|
||||||
slice: None,
|
slice: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +88,7 @@ impl MaildirOp {
|
||||||
|
|
||||||
impl<'a> BackendOp for MaildirOp {
|
impl<'a> BackendOp for MaildirOp {
|
||||||
fn description(&self) -> String {
|
fn description(&self) -> String {
|
||||||
format!("Path of file:")// self.0ipath)
|
format!("Path of file: {}", self.path())
|
||||||
}
|
}
|
||||||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||||
if self.slice.is_none() {
|
if self.slice.is_none() {
|
||||||
|
@ -133,10 +132,10 @@ impl<'a> BackendOp for MaildirOp {
|
||||||
|
|
||||||
fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> {
|
fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> {
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
let idx: usize = path.rfind(":2,").ok_or(MeliError::new(format!(
|
let idx: usize = path
|
||||||
"Invalid email filename: {:?}",
|
.rfind(":2,")
|
||||||
self
|
.ok_or_else(|| MeliError::new(format!("Invalid email filename: {:?}", self)))?
|
||||||
)))? + 3;
|
+ 3;
|
||||||
let mut new_name: String = path[..idx].to_string();
|
let mut new_name: String = path[..idx].to_string();
|
||||||
let mut flags = self.fetch_flags();
|
let mut flags = self.fetch_flags();
|
||||||
if !(flags & *f).is_empty() {
|
if !(flags & *f).is_empty() {
|
||||||
|
@ -212,24 +211,24 @@ impl MailBackend for MaildirType {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(event) => match event {
|
Ok(event) => match event {
|
||||||
DebouncedEvent::Create(mut pathbuf)
|
DebouncedEvent::Create(mut pathbuf)
|
||||||
| DebouncedEvent::Remove(mut pathbuf) => {
|
| DebouncedEvent::Remove(mut pathbuf) => {
|
||||||
if pathbuf.is_dir() {
|
if pathbuf.is_dir() {
|
||||||
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
|
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
|
||||||
pathbuf.pop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pathbuf.pop();
|
pathbuf.pop();
|
||||||
pathbuf.pop();
|
}
|
||||||
};
|
} else {
|
||||||
eprintln!(" got event in {}", pathbuf.display());
|
pathbuf.pop();
|
||||||
|
pathbuf.pop();
|
||||||
|
};
|
||||||
|
eprintln!(" got event in {}", pathbuf.display());
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
pathbuf.hash(&mut hasher);
|
pathbuf.hash(&mut hasher);
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
folder: format!("{}", pathbuf.display()),
|
folder: format!("{}", pathbuf.display()),
|
||||||
hash: hasher.finish(),
|
hash: hasher.finish(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Err(e) => eprintln!("watch error: {:?}", e),
|
Err(e) => eprintln!("watch error: {:?}", e),
|
||||||
|
@ -261,7 +260,7 @@ impl MaildirType {
|
||||||
path.to_str().unwrap().to_string(),
|
path.to_str().unwrap().to_string(),
|
||||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
path_children,
|
path_children,
|
||||||
) {
|
) {
|
||||||
folders.push(f);
|
folders.push(f);
|
||||||
children.push(folders.len() - 1);
|
children.push(folders.len() - 1);
|
||||||
}
|
}
|
||||||
|
@ -277,7 +276,7 @@ impl MaildirType {
|
||||||
path.to_str().unwrap().to_string(),
|
path.to_str().unwrap().to_string(),
|
||||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
Vec::with_capacity(0),
|
Vec::with_capacity(0),
|
||||||
) {
|
) {
|
||||||
folders.push(f);
|
folders.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +284,10 @@ impl MaildirType {
|
||||||
MaildirType {
|
MaildirType {
|
||||||
name: f.name().to_string(),
|
name: f.name().to_string(),
|
||||||
folders,
|
folders,
|
||||||
hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(0, Default::default()))),
|
hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
|
||||||
|
0,
|
||||||
|
Default::default(),
|
||||||
|
))),
|
||||||
path: f.root_folder().to_string(),
|
path: f.root_folder().to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,28 +344,33 @@ impl MaildirType {
|
||||||
let s = scope.builder().name(name.clone()).spawn(move || {
|
let s = scope.builder().name(name.clone()).spawn(move || {
|
||||||
let len = chunk.len();
|
let len = chunk.len();
|
||||||
let size = if len <= 100 { 100 } else { (len / 100) * 100 };
|
let size = if len <= 100 { 100 } else { (len / 100) * 100 };
|
||||||
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
let mut local_r: Vec<
|
||||||
|
Envelope,
|
||||||
|
> = Vec::with_capacity(chunk.len());
|
||||||
for c in chunk.chunks(size) {
|
for c in chunk.chunks(size) {
|
||||||
let map = map.clone();
|
let map = map.clone();
|
||||||
let len = c.len();
|
let len = c.len();
|
||||||
for file in c {
|
for file in c {
|
||||||
let ri = file.rfind("/").unwrap() + 1;
|
let ri = file.rfind('/').unwrap() + 1;
|
||||||
let file_name = &file[ri..];
|
let file_name = &file[ri..];
|
||||||
if let Some(cached) = cache_dir.find_cache_file(file_name) {
|
if let Some(cached) =
|
||||||
|
cache_dir.find_cache_file(file_name)
|
||||||
|
{
|
||||||
// TODO:: error checking
|
// TODO:: error checking
|
||||||
let reader = io::BufReader::new(fs::File::open(cached).unwrap());
|
let reader = io::BufReader::new(
|
||||||
|
fs::File::open(cached).unwrap(),
|
||||||
|
);
|
||||||
let env: Envelope = bincode::deserialize_from(reader).unwrap();
|
let env: Envelope = bincode::deserialize_from(reader).unwrap();
|
||||||
{
|
{
|
||||||
let mut map = map.lock().unwrap();
|
let mut map = map.lock().unwrap();
|
||||||
let hash = env.hash();
|
let hash = env.hash();
|
||||||
if (*map).contains_key(&hash) {
|
if (*map).contains_key(&hash) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(*map).insert(hash, (0, file.to_string()));
|
|
||||||
local_r.push(env);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
(*map).insert(hash, (0, file.to_string()));
|
||||||
|
local_r.push(env);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let e_copy = file.to_string();
|
let e_copy = file.to_string();
|
||||||
/*
|
/*
|
||||||
|
@ -376,41 +383,48 @@ impl MaildirType {
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
let hash = {
|
let hash = {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut f = fs::File::open(&e_copy).expect(&format!("Can't open {}", e_copy));
|
let mut f = fs::File::open(&e_copy)
|
||||||
f.read_to_end(&mut buf).expect(&format!("Can't read {}", e_copy));
|
.unwrap_or_else(|_| {
|
||||||
/* Unwrap is safe since we use ? above. */
|
panic!("Can't open {}", e_copy)
|
||||||
hasher.write(&buf);
|
});
|
||||||
hasher.finish()
|
f.read_to_end(&mut buf).unwrap_or_else(|_| {
|
||||||
};
|
panic!("Can't read {}", e_copy)
|
||||||
{
|
});
|
||||||
let mut map = map.lock().unwrap();
|
/* Unwrap is safe since we use ? above. */
|
||||||
if (*map).contains_key(&hash) {
|
hasher.write(&buf);
|
||||||
continue;
|
hasher.finish()
|
||||||
}
|
|
||||||
(*map).insert(hash, (0, e_copy));
|
|
||||||
}
|
|
||||||
// TODO: Check cache
|
|
||||||
let op = Box::new(MaildirOp::new(hash, map.clone()));
|
|
||||||
if let Some(mut e) = Envelope::from_token(op, hash) {
|
|
||||||
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
|
|
||||||
let f = match fs::File::create(cached) {
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(e) => {
|
|
||||||
panic!("{}",e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let writer = io::BufWriter::new(f);
|
{
|
||||||
bincode::serialize_into(writer, &e).unwrap();
|
let mut map = map.lock().unwrap();
|
||||||
}
|
if (*map).contains_key(&hash) {
|
||||||
local_r.push(e);
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
(*map).insert(hash, (0, e_copy));
|
||||||
|
}
|
||||||
|
// TODO: Check cache
|
||||||
|
let op =
|
||||||
|
Box::new(MaildirOp::new(hash, map.clone()));
|
||||||
|
if let Some(mut e) = Envelope::from_token(op, hash)
|
||||||
|
{
|
||||||
|
if let Ok(cached) =
|
||||||
|
cache_dir.place_cache_file(file_name)
|
||||||
|
{
|
||||||
|
let f = match fs::File::create(cached) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
panic!("{}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let writer = io::BufWriter::new(f);
|
||||||
|
bincode::serialize_into(writer, &e)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
local_r.push(e);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.send(AsyncStatus::ProgressReport(len));
|
tx.send(AsyncStatus::ProgressReport(len));
|
||||||
|
@ -429,16 +443,15 @@ impl MaildirType {
|
||||||
for (idx, e) in r.iter().enumerate() {
|
for (idx, e) in r.iter().enumerate() {
|
||||||
let mut y = (*map)[&e.hash()].clone();
|
let mut y = (*map)[&e.hash()].clone();
|
||||||
y.0 = idx;
|
y.0 = idx;
|
||||||
(*map).insert(e.hash(),y);
|
(*map).insert(e.hash(), y);
|
||||||
}
|
}
|
||||||
tx.send(AsyncStatus::Finished);
|
tx.send(AsyncStatus::Finished);
|
||||||
Ok(r)
|
Ok(r)
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
w.build(handle)
|
w.build(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -459,7 +472,7 @@ impl MaildirFolder {
|
||||||
hash: h.finish(),
|
hash: h.finish(),
|
||||||
name: file_name,
|
name: file_name,
|
||||||
path: pathbuf,
|
path: pathbuf,
|
||||||
children: children,
|
children,
|
||||||
};
|
};
|
||||||
ret.is_valid()?;
|
ret.is_valid()?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
@ -474,9 +487,9 @@ impl MaildirFolder {
|
||||||
p.push(d);
|
p.push(d);
|
||||||
if !p.is_dir() {
|
if !p.is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"{} is not a valid maildir folder",
|
"{} is not a valid maildir folder",
|
||||||
path.display()
|
path.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
p.pop();
|
p.pop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ pub struct Backends {
|
||||||
map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>,
|
map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Backends {
|
||||||
|
fn default() -> Self {
|
||||||
|
Backends::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Backends {
|
impl Backends {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut b = Backends {
|
let mut b = Backends {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use mailbox::email::parser::BytesExt;
|
|
||||||
use mailbox::email::attachments::Attachment;
|
use mailbox::email::attachments::Attachment;
|
||||||
|
use mailbox::email::parser::BytesExt;
|
||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
@ -20,13 +20,11 @@ impl SliceBuild {
|
||||||
//fn length(&self) -> usize {
|
//fn length(&self) -> usize {
|
||||||
// self.end - self.offset + 1
|
// self.end - self.offset + 1
|
||||||
//}
|
//}
|
||||||
pub fn get<'a>(&self, slice:&'a [u8]) -> &'a [u8] {
|
pub fn get<'a>(&self, slice: &'a [u8]) -> &'a [u8] {
|
||||||
&slice[self.offset..self.end]
|
&slice[self.offset..self.end]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Charset {
|
pub enum Charset {
|
||||||
Ascii,
|
Ascii,
|
||||||
|
@ -72,7 +70,7 @@ impl<'a> From<&'a [u8]> for Charset {
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("unknown tag is {:?}", str::from_utf8(b));
|
eprintln!("unknown tag is {:?}", str::from_utf8(b));
|
||||||
Charset::Ascii
|
Charset::Ascii
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,10 +100,19 @@ impl Display for MultipartType {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum ContentType {
|
pub enum ContentType {
|
||||||
Text { kind: Text, charset: Charset },
|
Text {
|
||||||
Multipart { boundary: SliceBuild, kind: MultipartType, subattachments: Vec<Attachment>},
|
kind: Text,
|
||||||
|
charset: Charset,
|
||||||
|
},
|
||||||
|
Multipart {
|
||||||
|
boundary: SliceBuild,
|
||||||
|
kind: MultipartType,
|
||||||
|
subattachments: Vec<Attachment>,
|
||||||
|
},
|
||||||
MessageRfc822,
|
MessageRfc822,
|
||||||
Unsupported { tag: Vec<u8> },
|
Unsupported {
|
||||||
|
tag: Vec<u8>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ContentType {
|
impl Default for ContentType {
|
||||||
|
@ -137,7 +144,10 @@ impl ContentType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_text_html(&self) -> bool {
|
pub fn is_text_html(&self) -> bool {
|
||||||
if let ContentType::Text { kind: Text::Html, .. } = self {
|
if let ContentType::Text {
|
||||||
|
kind: Text::Html, ..
|
||||||
|
} = self
|
||||||
|
{
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use data_encoding::BASE64_MIME;
|
use data_encoding::BASE64_MIME;
|
||||||
use mailbox::email::EnvelopeWrapper;
|
|
||||||
use mailbox::email::parser;
|
use mailbox::email::parser;
|
||||||
use mailbox::email::parser::BytesExt;
|
use mailbox::email::parser::BytesExt;
|
||||||
|
use mailbox::email::EnvelopeWrapper;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ impl AttachmentBuilder {
|
||||||
let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize);
|
let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize);
|
||||||
let boundary = SliceBuild::new(offset, _boundary.len());
|
let boundary = SliceBuild::new(offset, _boundary.len());
|
||||||
let subattachments = Self::subattachments(&self.raw, boundary.get(&value));
|
let subattachments = Self::subattachments(&self.raw, boundary.get(&value));
|
||||||
assert!(subattachments.len() > 0);
|
assert!(!subattachments.is_empty());
|
||||||
self.content_type = ContentType::Multipart {
|
self.content_type = ContentType::Multipart {
|
||||||
boundary,
|
boundary,
|
||||||
kind: if cst.eq_ignore_ascii_case(b"mixed") {
|
kind: if cst.eq_ignore_ascii_case(b"mixed") {
|
||||||
|
@ -96,7 +96,7 @@ impl AttachmentBuilder {
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
},
|
},
|
||||||
subattachments
|
subattachments,
|
||||||
};
|
};
|
||||||
} else if ct.eq_ignore_ascii_case(b"text") {
|
} else if ct.eq_ignore_ascii_case(b"text") {
|
||||||
self.content_type = ContentType::Text {
|
self.content_type = ContentType::Text {
|
||||||
|
@ -105,28 +105,28 @@ impl AttachmentBuilder {
|
||||||
};
|
};
|
||||||
for (n, v) in params {
|
for (n, v) in params {
|
||||||
if n.eq_ignore_ascii_case(b"charset") {
|
if n.eq_ignore_ascii_case(b"charset") {
|
||||||
match self.content_type {
|
if let ContentType::Text {
|
||||||
ContentType::Text { charset: ref mut c, .. } => {
|
charset: ref mut c, ..
|
||||||
*c = Charset::from(v);
|
} = self.content_type
|
||||||
},
|
{
|
||||||
_ => {},
|
*c = Charset::from(v);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cst.eq_ignore_ascii_case(b"html") {
|
if cst.eq_ignore_ascii_case(b"html") {
|
||||||
match self.content_type {
|
if let ContentType::Text {
|
||||||
ContentType::Text { kind: ref mut k, .. } => {
|
kind: ref mut k, ..
|
||||||
*k = Text::Html;
|
} = self.content_type
|
||||||
},
|
{
|
||||||
_ => {},
|
*k = Text::Html;
|
||||||
}
|
}
|
||||||
} else if !cst.eq_ignore_ascii_case(b"plain") {
|
} else if !cst.eq_ignore_ascii_case(b"plain") {
|
||||||
match self.content_type {
|
if let ContentType::Text {
|
||||||
ContentType::Text { kind: ref mut k, .. } => {
|
kind: ref mut k, ..
|
||||||
*k = Text::Other { tag: cst.into() };
|
} = self.content_type
|
||||||
},
|
{
|
||||||
_ => {},
|
*k = Text::Other { tag: cst.into() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") {
|
} else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") {
|
||||||
|
@ -136,9 +136,7 @@ impl AttachmentBuilder {
|
||||||
tag.extend(ct);
|
tag.extend(ct);
|
||||||
tag.push(b'/');
|
tag.push(b'/');
|
||||||
tag.extend(cst);
|
tag.extend(cst);
|
||||||
self.content_type = ContentType::Unsupported {
|
self.content_type = ContentType::Unsupported { tag };
|
||||||
tag
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
Err(v) => {
|
Err(v) => {
|
||||||
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
|
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
|
||||||
|
@ -197,8 +195,7 @@ impl AttachmentBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
|
pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
|
||||||
match parser::attachments(raw, boundary).to_full_result()
|
match parser::attachments(raw, boundary).to_full_result() {
|
||||||
{
|
|
||||||
Ok(attachments) => {
|
Ok(attachments) => {
|
||||||
let mut vec = Vec::with_capacity(attachments.len());
|
let mut vec = Vec::with_capacity(attachments.len());
|
||||||
for a in attachments {
|
for a in attachments {
|
||||||
|
@ -246,37 +243,29 @@ impl AttachmentBuilder {
|
||||||
impl fmt::Display for Attachment {
|
impl fmt::Display for Attachment {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.content_type {
|
match self.content_type {
|
||||||
ContentType::MessageRfc822 => {
|
ContentType::MessageRfc822 => match EnvelopeWrapper::new(self.bytes().to_vec()) {
|
||||||
match EnvelopeWrapper::new(self.bytes().to_vec()) {
|
Ok(wrapper) => write!(
|
||||||
Ok(wrapper) => write!(f, "message/rfc822: {} - {} - {}", wrapper.date(), wrapper.from_to_string(), wrapper.subject()),
|
f,
|
||||||
Err(e) => write!(f, "{}", e),
|
"message/rfc822: {} - {} - {}",
|
||||||
}
|
wrapper.date(),
|
||||||
|
wrapper.field_from_to_string(),
|
||||||
|
wrapper.subject()
|
||||||
|
),
|
||||||
|
Err(e) => write!(f, "{}", e),
|
||||||
},
|
},
|
||||||
ContentType::Unsupported { .. } => {
|
ContentType::Unsupported { .. } => {
|
||||||
write!(f, "Data attachment of type {}", self.mime_type())
|
write!(f, "Data attachment of type {}", self.mime_type())
|
||||||
}
|
}
|
||||||
ContentType::Text { .. } => {
|
ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
|
||||||
write!(f, "Text attachment of type {}", self.mime_type())
|
|
||||||
}
|
|
||||||
ContentType::Multipart {
|
ContentType::Multipart {
|
||||||
kind: ref multipart_type,
|
|
||||||
subattachments: ref sub_att_vec,
|
subattachments: ref sub_att_vec,
|
||||||
..
|
..
|
||||||
} => if *multipart_type == MultipartType::Alternative {
|
} => write!(
|
||||||
write!(
|
f,
|
||||||
f,
|
"{} attachment with {} subs",
|
||||||
"{} attachment with {} subs",
|
self.mime_type(),
|
||||||
self.mime_type(),
|
sub_att_vec.len()
|
||||||
sub_att_vec.len()
|
),
|
||||||
)
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{} attachment with {} subs",
|
|
||||||
self.mime_type(),
|
|
||||||
sub_att_vec.len()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,20 +286,23 @@ impl Attachment {
|
||||||
} => match *multipart_type {
|
} => match *multipart_type {
|
||||||
MultipartType::Alternative => {
|
MultipartType::Alternative => {
|
||||||
for a in sub_att_vec {
|
for a in sub_att_vec {
|
||||||
if let ContentType::Text { kind: Text::Plain, .. } = a.content_type {
|
if let ContentType::Text {
|
||||||
|
kind: Text::Plain, ..
|
||||||
|
} = a.content_type
|
||||||
|
{
|
||||||
a.get_text_recursive(text);
|
a.get_text_recursive(text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
MultipartType::Mixed | MultipartType::Digest => {
|
MultipartType::Mixed | MultipartType::Digest => {
|
||||||
for a in sub_att_vec {
|
for a in sub_att_vec {
|
||||||
a.get_text_recursive(text);
|
a.get_text_recursive(text);
|
||||||
text.extend_from_slice(b"\n\n");
|
text.extend_from_slice(b"\n\n");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn text(&self) -> String {
|
pub fn text(&self) -> String {
|
||||||
|
@ -356,7 +348,9 @@ impl Attachment {
|
||||||
}
|
}
|
||||||
pub fn is_html(&self) -> bool {
|
pub fn is_html(&self) -> bool {
|
||||||
match self.content_type {
|
match self.content_type {
|
||||||
ContentType::Text { kind: Text::Html, .. } => true,
|
ContentType::Text {
|
||||||
|
kind: Text::Html, ..
|
||||||
|
} => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,13 +363,13 @@ pub fn interpret_format_flowed(_t: &str) -> String {
|
||||||
|
|
||||||
fn decode_rfc822(_raw: &[u8]) -> Attachment {
|
fn decode_rfc822(_raw: &[u8]) -> Attachment {
|
||||||
let builder = AttachmentBuilder::new(b"");
|
let builder = AttachmentBuilder::new(b"");
|
||||||
return builder.build();
|
builder.build()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
eprintln!("raw is\n{:?}", str::from_utf8(raw).unwrap());
|
eprintln!("raw is\n{:?}", str::from_utf8(raw).unwrap());
|
||||||
let e = match Envelope::from_bytes(raw) {
|
let e = match Envelope::from_bytes(raw) {
|
||||||
Some(e) => e,
|
Some(e) => e,
|
||||||
None => {
|
None => {
|
||||||
eprintln!("error in parsing mail");
|
eprintln!("error in parsing mail");
|
||||||
let error_msg = b"Mail cannot be shown because of errors.";
|
let error_msg = b"Mail cannot be shown because of errors.";
|
||||||
let mut builder = AttachmentBuilder::new(error_msg);
|
let mut builder = AttachmentBuilder::new(error_msg);
|
||||||
|
@ -384,10 +378,11 @@ fn decode_rfc822(_raw: &[u8]) -> Attachment {
|
||||||
};
|
};
|
||||||
e.body(None)
|
e.body(None)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> {
|
type Filter = Box<Fn(&Attachment, &mut Vec<u8>) -> ()>;
|
||||||
|
|
||||||
|
fn decode_rec_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
|
||||||
let mut ret = match a.content_type {
|
let mut ret = match a.content_type {
|
||||||
ContentType::Unsupported { .. } => Vec::new(),
|
ContentType::Unsupported { .. } => Vec::new(),
|
||||||
ContentType::Text { .. } => decode_helper(a, filter),
|
ContentType::Text { .. } => decode_helper(a, filter),
|
||||||
|
@ -398,7 +393,10 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Ve
|
||||||
..
|
..
|
||||||
} => if *multipart_type == MultipartType::Alternative {
|
} => if *multipart_type == MultipartType::Alternative {
|
||||||
for a in sub_att_vec {
|
for a in sub_att_vec {
|
||||||
if let ContentType::Text { kind: Text::Plain, .. } = a.content_type {
|
if let ContentType::Text {
|
||||||
|
kind: Text::Plain, ..
|
||||||
|
} = a.content_type
|
||||||
|
{
|
||||||
return decode_helper(a, filter);
|
return decode_helper(a, filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,10 +415,10 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Ve
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
pub fn decode_rec(a: &Attachment, filter: Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> {
|
pub fn decode_rec(a: &Attachment, filter: Option<Filter>) -> Vec<u8> {
|
||||||
decode_rec_helper(a, &filter)
|
decode_rec_helper(a, &filter)
|
||||||
}
|
}
|
||||||
fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> {
|
fn decode_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
|
||||||
let charset = match a.content_type {
|
let charset = match a.content_type {
|
||||||
ContentType::Text { charset: c, .. } => c,
|
ContentType::Text { charset: c, .. } => c,
|
||||||
_ => Default::default(),
|
_ => Default::default(),
|
||||||
|
@ -449,12 +447,11 @@ fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment, &mut Vec<u8
|
||||||
bytes.to_vec()
|
bytes.to_vec()
|
||||||
};
|
};
|
||||||
if let Some(filter) = filter {
|
if let Some(filter) = filter {
|
||||||
filter(a, &mut ret);
|
filter(a, &mut ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
}
|
}
|
||||||
pub fn decode(a: &Attachment, filter: Option<Box<Fn(&Attachment, &mut Vec<u8>) -> ()>>) -> Vec<u8> {
|
pub fn decode(a: &Attachment, filter: Option<Filter>) -> Vec<u8> {
|
||||||
decode_helper(a, &filter)
|
decode_helper(a, &filter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,10 +129,10 @@ impl StrBuilder {
|
||||||
let offset = self.offset;
|
let offset = self.offset;
|
||||||
let length = self.length;
|
let length = self.length;
|
||||||
String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
|
String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] {
|
fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] {
|
||||||
&b[self.offset..(self.offset+self.length)]
|
&b[self.offset..(self.offset + self.length)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ impl StrBuild for MessageID {
|
||||||
MessageID(
|
MessageID(
|
||||||
string.to_owned(),
|
string.to_owned(),
|
||||||
StrBuilder {
|
StrBuilder {
|
||||||
offset: offset,
|
offset,
|
||||||
length: slice.len() + 1,
|
length: slice.len() + 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -197,12 +197,12 @@ struct References {
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct Flag: u8 {
|
pub struct Flag: u8 {
|
||||||
const PASSED = 0b00000001;
|
const PASSED = 0b0000_0001;
|
||||||
const REPLIED = 0b00000010;
|
const REPLIED = 0b0000_0010;
|
||||||
const SEEN = 0b00000100;
|
const SEEN = 0b0000_0100;
|
||||||
const TRASHED = 0b00001000;
|
const TRASHED = 0b0000_1000;
|
||||||
const DRAFT = 0b00010000;
|
const DRAFT = 0b0001_0000;
|
||||||
const FLAGGED = 0b00100000;
|
const FLAGGED = 0b0010_0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,6 @@ pub struct EnvelopeWrapper {
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
impl Deref for EnvelopeWrapper {
|
impl Deref for EnvelopeWrapper {
|
||||||
|
@ -317,7 +316,6 @@ impl Envelope {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
pub fn populate_headers(&mut self, bytes: &[u8]) -> Result<()> {
|
pub fn populate_headers(&mut self, bytes: &[u8]) -> Result<()> {
|
||||||
|
|
||||||
let (headers, _) = match parser::mail(bytes).to_full_result() {
|
let (headers, _) = match parser::mail(bytes).to_full_result() {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -372,13 +370,13 @@ impl Envelope {
|
||||||
self.set_in_reply_to(value);
|
self.set_in_reply_to(value);
|
||||||
in_reply_to = Some(value);
|
in_reply_to = Some(value);
|
||||||
} else if name.eq_ignore_ascii_case(b"date") {
|
} else if name.eq_ignore_ascii_case(b"date") {
|
||||||
let parse_result = parser::phrase(value);
|
let parse_result = parser::phrase(value);
|
||||||
if parse_result.is_done() {
|
if parse_result.is_done() {
|
||||||
let value = parse_result.to_full_result().unwrap();
|
let value = parse_result.to_full_result().unwrap();
|
||||||
self.set_date(value.as_slice());
|
self.set_date(value.as_slice());
|
||||||
} else {
|
} else {
|
||||||
self.set_date(value);
|
self.set_date(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -400,24 +398,21 @@ impl Envelope {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn populate_headers_from_token(&mut self, mut operation: Box<BackendOp>) -> Result<()> {
|
pub fn populate_headers_from_token(&mut self, mut operation: Box<BackendOp>) -> Result<()> {
|
||||||
{
|
let headers = operation.fetch_headers()?;
|
||||||
let headers = operation.fetch_headers()?;
|
self.populate_headers(headers)
|
||||||
return self.populate_headers(headers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn date(&self) -> u64 {
|
pub fn date(&self) -> u64 {
|
||||||
self.timestamp
|
self.timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
|
pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
|
||||||
if let Some(d) = parser::date(&self.date.as_bytes()) {
|
if let Some(d) = parser::date(&self.date.as_bytes()) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
chrono::FixedOffset::west(0)
|
chrono::FixedOffset::west(0)
|
||||||
.ymd(1970, 1, 1)
|
.ymd(1970, 1, 1)
|
||||||
.and_hms(0, 0, 0)
|
.and_hms(0, 0, 0)
|
||||||
}
|
}
|
||||||
pub fn date_as_str(&self) -> &str {
|
pub fn date_as_str(&self) -> &str {
|
||||||
&self.date
|
&self.date
|
||||||
|
@ -426,14 +421,14 @@ impl Envelope {
|
||||||
&self.from
|
&self.from
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_to_string(&self) -> String {
|
pub fn field_from_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.from.iter().map(|a| format!("{}", a)).collect();
|
let _strings: Vec<String> = self.from.iter().map(|a| format!("{}", a)).collect();
|
||||||
_strings.join(", ")
|
_strings.join(", ")
|
||||||
}
|
}
|
||||||
pub fn to(&self) -> &Vec<Address> {
|
pub fn to(&self) -> &Vec<Address> {
|
||||||
&self.to
|
&self.to
|
||||||
}
|
}
|
||||||
pub fn to_to_string(&self) -> String {
|
pub fn field_to_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
|
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
|
||||||
_strings.join(", ")
|
_strings.join(", ")
|
||||||
}
|
}
|
||||||
|
@ -504,7 +499,7 @@ impl Envelope {
|
||||||
}
|
}
|
||||||
pub fn in_reply_to_raw(&self) -> Cow<str> {
|
pub fn in_reply_to_raw(&self) -> Cow<str> {
|
||||||
match self.in_reply_to {
|
match self.in_reply_to {
|
||||||
Some(ref s) => String::from_utf8_lossy(s.raw()).into(),
|
Some(ref s) => String::from_utf8_lossy(s.raw()),
|
||||||
_ => Cow::from(String::new()),
|
_ => Cow::from(String::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,12 +114,11 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
|
||||||
fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
let input_len = input.len();
|
let input_len = input.len();
|
||||||
for (i, x) in input.iter().enumerate() {
|
for (i, x) in input.iter().enumerate() {
|
||||||
if *x == b'\n' {
|
if *x == b'\n'
|
||||||
if ((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t')
|
&& (((i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t')
|
||||||
|| i + 1 == input_len
|
|| i + 1 == input_len)
|
||||||
{
|
{
|
||||||
return IResult::Done(&input[(i + 1)..], &input[0..i]);
|
return IResult::Done(&input[(i + 1)..], &input[0..i]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IResult::Incomplete(Needed::Unknown)
|
IResult::Incomplete(Needed::Unknown)
|
||||||
|
@ -146,7 +145,7 @@ pub fn headers_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
return IResult::Done(&input[(i + 1)..], &input[0..i + 1]);
|
return IResult::Done(&input[(i + 1)..], &input[0..i + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IResult::Error(error_code!(ErrorKind::Custom(43)));
|
IResult::Error(error_code!(ErrorKind::Custom(43)))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(pub body_raw<&[u8]>,
|
named!(pub body_raw<&[u8]>,
|
||||||
|
@ -337,9 +336,9 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
|
||||||
IResult::Done(
|
IResult::Done(
|
||||||
rest,
|
rest,
|
||||||
Address::Mailbox(MailboxAddress {
|
Address::Mailbox(MailboxAddress {
|
||||||
raw: raw,
|
raw,
|
||||||
display_name: display_name,
|
display_name,
|
||||||
address_spec: address_spec,
|
address_spec,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -414,12 +413,11 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match mailbox_list(&input[dlength..]) {
|
match mailbox_list(&input[dlength..]) {
|
||||||
IResult::Error(e) => {
|
IResult::Error(e) => IResult::Error(e),
|
||||||
return IResult::Error(e);
|
|
||||||
}
|
|
||||||
IResult::Done(rest, vec) => {
|
IResult::Done(rest, vec) => {
|
||||||
let size: usize = (rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize);
|
let size: usize =
|
||||||
return IResult::Done(
|
(rest.as_ptr() as usize).wrapping_sub((&input[0..] as &[u8]).as_ptr() as usize);
|
||||||
|
IResult::Done(
|
||||||
rest,
|
rest,
|
||||||
Address::Group(GroupAddress {
|
Address::Group(GroupAddress {
|
||||||
raw: input[0..size].into(),
|
raw: input[0..size].into(),
|
||||||
|
@ -429,11 +427,9 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
|
||||||
},
|
},
|
||||||
mailbox_list: vec,
|
mailbox_list: vec,
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
}
|
|
||||||
IResult::Incomplete(i) => {
|
|
||||||
return IResult::Incomplete(i);
|
|
||||||
}
|
}
|
||||||
|
IResult::Incomplete(i) => IResult::Incomplete(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +555,7 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<
|
||||||
return IResult::Error(error_code!(ErrorKind::Custom(43)));
|
return IResult::Error(error_code!(ErrorKind::Custom(43)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IResult::Done(input, ret);
|
IResult::Done(input, ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
|
named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
|
||||||
|
@ -603,13 +599,11 @@ named!(
|
||||||
acc += x.len();
|
acc += x.len();
|
||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
let bytes = list
|
list.iter()
|
||||||
.iter()
|
|
||||||
.fold(Vec::with_capacity(list_len), |mut acc, x| {
|
.fold(Vec::with_capacity(list_len), |mut acc, x| {
|
||||||
acc.append(&mut x.clone());
|
acc.append(&mut x.clone());
|
||||||
acc
|
acc
|
||||||
});
|
})
|
||||||
bytes
|
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -685,7 +679,7 @@ pub fn phrase(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
||||||
acc.push(b' ');
|
acc.push(b' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IResult::Done(&[], acc);
|
IResult::Done(&[], acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub mod backends;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use mailbox::backends::{folder_default, Folder};
|
use mailbox::backends::{folder_default, Folder};
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub use mailbox::thread::{build_threads, Container, Threads, SortOrder, SortField};
|
pub use mailbox::thread::{build_threads, Container, SortField, SortOrder, Threads};
|
||||||
|
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
|
|
||||||
|
@ -74,10 +74,13 @@ impl Mailbox {
|
||||||
let threads = build_threads(&mut collection, sent_folder);
|
let threads = build_threads(&mut collection, sent_folder);
|
||||||
Ok(Mailbox {
|
Ok(Mailbox {
|
||||||
folder: (*folder).clone(),
|
folder: (*folder).clone(),
|
||||||
collection: collection,
|
collection,
|
||||||
threads: threads,
|
threads,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.collection.is_empty()
|
||||||
|
}
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.collection.len()
|
self.collection.len()
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,11 @@ use mailbox::Mailbox;
|
||||||
extern crate fnv;
|
extern crate fnv;
|
||||||
use self::fnv::FnvHashMap;
|
use self::fnv::FnvHashMap;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::{Index, };
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::result::Result as StdResult;
|
|
||||||
use std::cell::{Ref, RefCell};
|
use std::cell::{Ref, RefCell};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::ops::Index;
|
||||||
|
use std::result::Result as StdResult;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum SortOrder {
|
pub enum SortOrder {
|
||||||
|
@ -122,7 +122,6 @@ impl ContainerTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Threads {
|
pub struct Threads {
|
||||||
containers: Vec<Container>,
|
containers: Vec<Container>,
|
||||||
|
@ -136,10 +135,10 @@ pub struct Threads {
|
||||||
pub struct ThreadIterator<'a> {
|
pub struct ThreadIterator<'a> {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
stack: Vec<usize>,
|
stack: Vec<usize>,
|
||||||
tree: Ref<'a ,Vec<ContainerTree>>,
|
tree: Ref<'a, Vec<ContainerTree>>,
|
||||||
}
|
}
|
||||||
impl<'a> Iterator for ThreadIterator<'a> {
|
impl<'a> Iterator for ThreadIterator<'a> {
|
||||||
type Item = usize;
|
type Item = usize;
|
||||||
fn next(&mut self) -> Option<usize> {
|
fn next(&mut self) -> Option<usize> {
|
||||||
{
|
{
|
||||||
let mut tree = &(*self.tree);
|
let mut tree = &(*self.tree);
|
||||||
|
@ -163,7 +162,7 @@ impl<'a> Iterator for ThreadIterator<'a> {
|
||||||
return Some(ret);
|
return Some(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.next();
|
self.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,25 +171,28 @@ impl<'a> IntoIterator for &'a Threads {
|
||||||
type IntoIter = ThreadIterator<'a>;
|
type IntoIter = ThreadIterator<'a>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
ThreadIterator { pos: 0, stack: Vec::new(), tree: self.tree.borrow() }
|
ThreadIterator {
|
||||||
|
pos: 0,
|
||||||
|
stack: Vec::new(),
|
||||||
|
tree: self.tree.borrow(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct RootIterator<'a> {
|
pub struct RootIterator<'a> {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
tree: Ref<'a ,Vec<ContainerTree>>,
|
tree: Ref<'a, Vec<ContainerTree>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for RootIterator<'a> {
|
impl<'a> Iterator for RootIterator<'a> {
|
||||||
type Item = (usize, usize, bool);
|
type Item = (usize, usize, bool);
|
||||||
fn next(&mut self) -> Option<(usize, usize, bool)> {
|
fn next(&mut self) -> Option<(usize, usize, bool)> {
|
||||||
if self.pos == self.tree.len() {
|
if self.pos == self.tree.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let node = &self.tree[self.pos];
|
let node = &self.tree[self.pos];
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
return Some((node.id, node.len, node.has_unseen));
|
Some((node.id, node.len, node.has_unseen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +203,11 @@ impl Threads {
|
||||||
pub fn root_set(&self) -> &Vec<usize> {
|
pub fn root_set(&self) -> &Vec<usize> {
|
||||||
&self.root_set
|
&self.root_set
|
||||||
}
|
}
|
||||||
pub fn root_set_iter(&self) -> RootIterator {
|
pub fn root_set_iter(&self) -> RootIterator {
|
||||||
RootIterator { pos: 0, tree: self.tree.borrow() }
|
RootIterator {
|
||||||
|
pos: 0,
|
||||||
|
tree: self.tree.borrow(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn thread_to_mail(&self, i: usize) -> usize {
|
pub fn thread_to_mail(&self, i: usize) -> usize {
|
||||||
let thread = self.containers[self.threaded_collection[i]];
|
let thread = self.containers[self.threaded_collection[i]];
|
||||||
|
@ -219,8 +224,8 @@ impl Threads {
|
||||||
let tree = &mut self.tree.borrow_mut();
|
let tree = &mut self.tree.borrow_mut();
|
||||||
let containers = &self.containers;
|
let containers = &self.containers;
|
||||||
for mut t in tree.iter_mut() {
|
for mut t in tree.iter_mut() {
|
||||||
if let Some(ref mut c ) = t.children {
|
if let Some(ref mut c) = t.children {
|
||||||
c.sort_by(|a, b| { match subsort {
|
c.sort_by(|a, b| match subsort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = containers[a.id];
|
let a = containers[a.id];
|
||||||
let b = containers[b.id];
|
let b = containers[b.id];
|
||||||
|
@ -253,7 +258,6 @@ impl Threads {
|
||||||
let mb = &collection[b.unwrap()];
|
let mb = &collection[b.unwrap()];
|
||||||
mb.subject().cmp(&ma.subject())
|
mb.subject().cmp(&ma.subject())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,12 +266,11 @@ impl Threads {
|
||||||
fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) {
|
fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) {
|
||||||
let tree = &mut self.tree.borrow_mut();
|
let tree = &mut self.tree.borrow_mut();
|
||||||
let containers = &self.containers;
|
let containers = &self.containers;
|
||||||
tree.sort_by(|a, b| { match sort {
|
tree.sort_by(|a, b| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = containers[a.id];
|
let a = containers[a.id];
|
||||||
let b = containers[b.id];
|
let b = containers[b.id];
|
||||||
b.date.cmp(&a.date)
|
b.date.cmp(&a.date)
|
||||||
|
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let a = containers[a.id];
|
let a = containers[a.id];
|
||||||
|
@ -296,10 +299,14 @@ impl Threads {
|
||||||
let mb = &collection[b.unwrap()];
|
let mb = &collection[b.unwrap()];
|
||||||
mb.subject().cmp(&ma.subject())
|
mb.subject().cmp(&ma.subject())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pub fn sort_by(&self, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), collection: &[Envelope]) {
|
pub fn sort_by(
|
||||||
|
&self,
|
||||||
|
sort: (SortField, SortOrder),
|
||||||
|
subsort: (SortField, SortOrder),
|
||||||
|
collection: &[Envelope],
|
||||||
|
) {
|
||||||
if *self.sort.borrow() != sort {
|
if *self.sort.borrow() != sort {
|
||||||
self.inner_sort_by(sort, collection);
|
self.inner_sort_by(sort, collection);
|
||||||
*self.sort.borrow_mut() = sort;
|
*self.sort.borrow_mut() = sort;
|
||||||
|
@ -308,7 +315,6 @@ impl Threads {
|
||||||
self.inner_subsort_by(subsort, collection);
|
self.inner_subsort_by(subsort, collection);
|
||||||
*self.subsort.borrow_mut() = subsort;
|
*self.subsort.borrow_mut() = subsort;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_collection(&mut self, collection: &[Envelope]) {
|
pub fn build_collection(&mut self, collection: &[Envelope]) {
|
||||||
|
@ -320,7 +326,7 @@ impl Threads {
|
||||||
i: usize,
|
i: usize,
|
||||||
root_subject_idx: usize,
|
root_subject_idx: usize,
|
||||||
collection: &[Envelope],
|
collection: &[Envelope],
|
||||||
) {
|
) {
|
||||||
let thread = containers[i];
|
let thread = containers[i];
|
||||||
if let Some(msg_idx) = containers[root_subject_idx].message() {
|
if let Some(msg_idx) = containers[root_subject_idx].message() {
|
||||||
let root_subject = collection[msg_idx].subject();
|
let root_subject = collection[msg_idx].subject();
|
||||||
|
@ -333,9 +339,9 @@ impl Threads {
|
||||||
if subject == root_subject
|
if subject == root_subject
|
||||||
|| subject.starts_with("Re: ")
|
|| subject.starts_with("Re: ")
|
||||||
&& subject.as_ref().ends_with(root_subject.as_ref())
|
&& subject.as_ref().ends_with(root_subject.as_ref())
|
||||||
{
|
{
|
||||||
containers[i].set_show_subject(false);
|
containers[i].set_show_subject(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if thread.has_parent() && !containers[thread.parent().unwrap()].has_message() {
|
if thread.has_parent() && !containers[thread.parent().unwrap()].has_message() {
|
||||||
|
@ -360,7 +366,15 @@ impl Threads {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut new_child_tree = ContainerTree::new(fc);
|
let mut new_child_tree = ContainerTree::new(fc);
|
||||||
build_threaded(&mut new_child_tree, containers, indentation, threaded, fc, i, collection);
|
build_threaded(
|
||||||
|
&mut new_child_tree,
|
||||||
|
containers,
|
||||||
|
indentation,
|
||||||
|
threaded,
|
||||||
|
fc,
|
||||||
|
i,
|
||||||
|
collection,
|
||||||
|
);
|
||||||
tree.has_unseen |= new_child_tree.has_unseen;
|
tree.has_unseen |= new_child_tree.has_unseen;
|
||||||
child_vec.push(new_child_tree);
|
child_vec.push(new_child_tree);
|
||||||
let thread_ = containers[fc];
|
let thread_ = containers[fc];
|
||||||
|
@ -384,7 +398,7 @@ impl Threads {
|
||||||
*i,
|
*i,
|
||||||
*i,
|
*i,
|
||||||
collection,
|
collection,
|
||||||
);
|
);
|
||||||
tree.push(tree_node);
|
tree.push(tree_node);
|
||||||
}
|
}
|
||||||
self.tree.replace(tree);
|
self.tree.replace(tree);
|
||||||
|
@ -397,11 +411,12 @@ impl Index<usize> for Threads {
|
||||||
type Output = Container;
|
type Output = Container;
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Container {
|
fn index(&self, index: usize) -> &Container {
|
||||||
self.containers.get(index).expect("thread index out of bounds")
|
self.containers
|
||||||
|
.get(index)
|
||||||
|
.expect("thread index out of bounds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
pub fn date(&self) -> UnixTimestamp {
|
pub fn date(&self) -> UnixTimestamp {
|
||||||
self.date
|
self.date
|
||||||
|
@ -474,7 +489,7 @@ fn build_collection(
|
||||||
threads: &mut Vec<Container>,
|
threads: &mut Vec<Container>,
|
||||||
id_table: &mut FnvHashMap<Cow<str>, usize>,
|
id_table: &mut FnvHashMap<Cow<str>, usize>,
|
||||||
collection: &mut [Envelope],
|
collection: &mut [Envelope],
|
||||||
) -> () {
|
) -> () {
|
||||||
for (i, x) in collection.iter_mut().enumerate() {
|
for (i, x) in collection.iter_mut().enumerate() {
|
||||||
let x_index; /* x's index in threads */
|
let x_index; /* x's index in threads */
|
||||||
let m_id = x.message_id_raw().into_owned();
|
let m_id = x.message_id_raw().into_owned();
|
||||||
|
@ -529,7 +544,7 @@ fn build_collection(
|
||||||
let parent_id = if id_table.contains_key(&r) {
|
let parent_id = if id_table.contains_key(&r) {
|
||||||
let p = id_table[r.as_ref()];
|
let p = id_table[r.as_ref()];
|
||||||
if !(threads[p].is_descendant(threads, &threads[curr_ref])
|
if !(threads[p].is_descendant(threads, &threads[curr_ref])
|
||||||
|| threads[curr_ref].is_descendant(threads, &threads[p]))
|
|| threads[curr_ref].is_descendant(threads, &threads[p]))
|
||||||
{
|
{
|
||||||
threads[curr_ref].parent = Some(p);
|
threads[curr_ref].parent = Some(p);
|
||||||
if threads[p].first_child.is_none() {
|
if threads[p].first_child.is_none() {
|
||||||
|
@ -589,7 +604,7 @@ fn build_collection(
|
||||||
pub fn build_threads(
|
pub fn build_threads(
|
||||||
collection: &mut Vec<Envelope>,
|
collection: &mut Vec<Envelope>,
|
||||||
sent_folder: &Option<Result<Mailbox>>,
|
sent_folder: &Option<Result<Mailbox>>,
|
||||||
) -> Threads {
|
) -> Threads {
|
||||||
/* To reconstruct thread information from the mails we need: */
|
/* To reconstruct thread information from the mails we need: */
|
||||||
|
|
||||||
/* a vector to hold thread members */
|
/* a vector to hold thread members */
|
||||||
|
@ -623,77 +638,77 @@ pub fn build_threads(
|
||||||
if id_table.contains_key(&m_id)
|
if id_table.contains_key(&m_id)
|
||||||
|| (!x.in_reply_to_raw().is_empty()
|
|| (!x.in_reply_to_raw().is_empty()
|
||||||
&& id_table.contains_key(&x.in_reply_to_raw()))
|
&& id_table.contains_key(&x.in_reply_to_raw()))
|
||||||
|
{
|
||||||
|
let mut x: Envelope = (*x).clone();
|
||||||
|
if id_table.contains_key(&m_id) {
|
||||||
|
let c = id_table[&m_id];
|
||||||
|
if threads[c].message.is_some() {
|
||||||
|
/* skip duplicate message-id, but this should be handled instead */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
threads[c].message = Some(idx);
|
||||||
|
assert!(threads[c].has_children());
|
||||||
|
threads[c].date = x.date();
|
||||||
|
x.set_thread(c);
|
||||||
|
} else if !x.in_reply_to_raw().is_empty()
|
||||||
|
&& id_table.contains_key(&x.in_reply_to_raw())
|
||||||
{
|
{
|
||||||
let mut x: Envelope = (*x).clone();
|
let p = id_table[&x_r_id];
|
||||||
if id_table.contains_key(&m_id) {
|
let c = if id_table.contains_key(&m_id) {
|
||||||
let c = id_table[&m_id];
|
id_table[&m_id]
|
||||||
if threads[c].message.is_some() {
|
} else {
|
||||||
/* skip duplicate message-id, but this should be handled instead */
|
threads.push(Container {
|
||||||
continue;
|
message: Some(idx),
|
||||||
|
id: tidx,
|
||||||
|
parent: Some(p),
|
||||||
|
first_child: None,
|
||||||
|
next_sibling: None,
|
||||||
|
date: x.date(),
|
||||||
|
indentation: 0,
|
||||||
|
show_subject: true,
|
||||||
|
});
|
||||||
|
id_table.insert(Cow::from(m_id.into_owned()), tidx);
|
||||||
|
x.set_thread(tidx);
|
||||||
|
tidx += 1;
|
||||||
|
tidx - 1
|
||||||
|
};
|
||||||
|
threads[c].parent = Some(p);
|
||||||
|
if threads[p].is_descendant(&threads, &threads[c])
|
||||||
|
|| threads[c].is_descendant(&threads, &threads[p])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if threads[p].first_child.is_none() {
|
||||||
|
threads[p].first_child = Some(c);
|
||||||
|
} else {
|
||||||
|
let mut fc = threads[p].first_child.unwrap();
|
||||||
|
while threads[fc].next_sibling.is_some() {
|
||||||
|
threads[fc].parent = Some(p);
|
||||||
|
fc = threads[fc].next_sibling.unwrap();
|
||||||
}
|
}
|
||||||
threads[c].message = Some(idx);
|
threads[fc].next_sibling = Some(c);
|
||||||
assert!(threads[c].has_children());
|
threads[fc].parent = Some(p);
|
||||||
threads[c].date = x.date();
|
}
|
||||||
x.set_thread(c);
|
/* update thread date */
|
||||||
} else if !x.in_reply_to_raw().is_empty()
|
let mut parent_iter = p;
|
||||||
&& id_table.contains_key(&x.in_reply_to_raw())
|
'date: loop {
|
||||||
{
|
let p = &mut threads[parent_iter];
|
||||||
let p = id_table[&x_r_id];
|
if p.date < x.date() {
|
||||||
let c = if id_table.contains_key(&m_id) {
|
p.date = x.date();
|
||||||
id_table[&m_id]
|
}
|
||||||
} else {
|
match p.parent {
|
||||||
threads.push(Container {
|
Some(p) => {
|
||||||
message: Some(idx),
|
parent_iter = p;
|
||||||
id: tidx,
|
|
||||||
parent: Some(p),
|
|
||||||
first_child: None,
|
|
||||||
next_sibling: None,
|
|
||||||
date: x.date(),
|
|
||||||
indentation: 0,
|
|
||||||
show_subject: true,
|
|
||||||
});
|
|
||||||
id_table.insert(Cow::from(m_id.into_owned()), tidx);
|
|
||||||
x.set_thread(tidx);
|
|
||||||
tidx += 1;
|
|
||||||
tidx - 1
|
|
||||||
};
|
|
||||||
threads[c].parent = Some(p);
|
|
||||||
if threads[p].is_descendant(&threads, &threads[c])
|
|
||||||
|| threads[c].is_descendant(&threads, &threads[p])
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if threads[p].first_child.is_none() {
|
|
||||||
threads[p].first_child = Some(c);
|
|
||||||
} else {
|
|
||||||
let mut fc = threads[p].first_child.unwrap();
|
|
||||||
while threads[fc].next_sibling.is_some() {
|
|
||||||
threads[fc].parent = Some(p);
|
|
||||||
fc = threads[fc].next_sibling.unwrap();
|
|
||||||
}
|
|
||||||
threads[fc].next_sibling = Some(c);
|
|
||||||
threads[fc].parent = Some(p);
|
|
||||||
}
|
}
|
||||||
/* update thread date */
|
None => {
|
||||||
let mut parent_iter = p;
|
break 'date;
|
||||||
'date: loop {
|
|
||||||
let p = &mut threads[parent_iter];
|
|
||||||
if p.date < x.date() {
|
|
||||||
p.date = x.date();
|
|
||||||
}
|
|
||||||
match p.parent {
|
|
||||||
Some(p) => {
|
|
||||||
parent_iter = p;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
break 'date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collection.push(x);
|
}
|
||||||
idx += 1;
|
|
||||||
}
|
}
|
||||||
|
collection.push(x);
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -704,14 +719,14 @@ pub fn build_threads(
|
||||||
if threads[*v].parent.is_none() {
|
if threads[*v].parent.is_none() {
|
||||||
if !threads[*v].has_message()
|
if !threads[*v].has_message()
|
||||||
&& threads[*v].has_children()
|
&& threads[*v].has_children()
|
||||||
&& !threads[threads[*v].first_child.unwrap()].has_sibling()
|
&& !threads[threads[*v].first_child.unwrap()].has_sibling()
|
||||||
{
|
{
|
||||||
/* Do not promote the children if doing so would promote them to the root set
|
/* Do not promote the children if doing so would promote them to the root set
|
||||||
* -- unless there is only one child, in which case, do. */
|
* -- unless there is only one child, in which case, do. */
|
||||||
root_set.push(threads[*v].first_child.unwrap());
|
root_set.push(threads[*v].first_child.unwrap());
|
||||||
|
|
||||||
continue 'root_set;
|
continue 'root_set;
|
||||||
}
|
}
|
||||||
root_set.push(*v);
|
root_set.push(*v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
#
|
#
|
||||||
|
|
||||||
exec find . -name "*rs" -exec rustfmt {} \;
|
exec git diff --name-only HEAD | grep ".*\.rs" | xargs rustfmt
|
||||||
|
#exec find . -name "*rs" -exec rustfmt {} \;
|
||||||
exec cargo +nightly clippy
|
exec cargo +nightly clippy
|
||||||
# If there are whitespace errors, print the offending file names and fail.
|
# If there are whitespace errors, print the offending file names and fail.
|
||||||
exec git diff-index --check --cached $against --
|
exec git diff-index --check --cached $against --
|
||||||
|
|
|
@ -35,7 +35,6 @@ extern crate ui;
|
||||||
pub use melib::*;
|
pub use melib::*;
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate chan;
|
extern crate chan;
|
||||||
extern crate chan_signal;
|
extern crate chan_signal;
|
||||||
|
@ -56,7 +55,6 @@ fn main() {
|
||||||
/* Catch SIGWINCH to handle terminal resizing */
|
/* Catch SIGWINCH to handle terminal resizing */
|
||||||
let signal = chan_signal::notify(&[Signal::WINCH]);
|
let signal = chan_signal::notify(&[Signal::WINCH]);
|
||||||
|
|
||||||
|
|
||||||
/* Create the application State. This is the 'System' part of an ECS architecture */
|
/* Create the application State. This is the 'System' part of an ECS architecture */
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl Component for Composer {
|
||||||
let width = width!(area);
|
let width = width!(area);
|
||||||
let mid = if width > 80 {
|
let mid = if width > 80 {
|
||||||
let width = width - 80;
|
let width = width - 80;
|
||||||
let mid = width / 2;;
|
let mid = width / 2;
|
||||||
|
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
for i in get_y(upper_left)..=get_y(bottom_right) {
|
for i in get_y(upper_left)..=get_y(bottom_right) {
|
||||||
|
@ -78,17 +78,22 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mid
|
mid
|
||||||
} else { 0 };
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
for i in get_x(upper_left)+ mid + 1..=get_x(upper_left) + mid + 79 {
|
for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + 79 {
|
||||||
grid[(i, header_height)].set_ch(HORZ_BOUNDARY);
|
grid[(i, header_height)].set_ch(HORZ_BOUNDARY);
|
||||||
grid[(i, header_height)].set_fg(Color::Default);
|
grid[(i, header_height)].set_fg(Color::Default);
|
||||||
grid[(i, header_height)].set_bg(Color::Default);
|
grid[(i, header_height)].set_bg(Color::Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_area = ((mid + 1, header_height+2), (mid + 78, get_y(bottom_right)));
|
let body_area = (
|
||||||
|
(mid + 1, header_height + 2),
|
||||||
|
(mid + 78, get_y(bottom_right)),
|
||||||
|
);
|
||||||
|
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
|
@ -97,8 +102,7 @@ impl Component for Composer {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Overview => {
|
ViewMode::Overview => {
|
||||||
self.pager.draw(grid, body_area, context);
|
self.pager.draw(grid, body_area, context);
|
||||||
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,12 +138,12 @@ impl Component for Composer {
|
||||||
.expect("failed to execute process");
|
.expect("failed to execute process");
|
||||||
let result = f.read_to_string();
|
let result = f.read_to_string();
|
||||||
self.buffer = result.clone();
|
self.buffer = result.clone();
|
||||||
self.pager.update_from_string(result);
|
self.pager.update_from_str(result.as_str());
|
||||||
context.restore_input();
|
context.restore_input();
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,14 +67,14 @@ impl CompactListing {
|
||||||
&CompactListing::format_date(e),
|
&CompactListing::format_date(e),
|
||||||
e.subject(),
|
e.subject(),
|
||||||
len
|
len
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{} {} {:.85}",
|
"{} {} {:.85}",
|
||||||
idx,
|
idx,
|
||||||
&CompactListing::format_date(e),
|
&CompactListing::format_date(e),
|
||||||
e.subject(),
|
e.subject(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ impl CompactListing {
|
||||||
length: 0,
|
length: 0,
|
||||||
sort: (Default::default(), Default::default()),
|
sort: (Default::default(), Default::default()),
|
||||||
subsort: (Default::default(), Default::default()),
|
subsort: (Default::default(), Default::default()),
|
||||||
content: content,
|
content,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
unfocused: false,
|
unfocused: false,
|
||||||
view: None,
|
view: None,
|
||||||
|
@ -110,7 +110,10 @@ impl CompactListing {
|
||||||
//
|
//
|
||||||
loop {
|
loop {
|
||||||
// TODO: Show progress visually
|
// TODO: Show progress visually
|
||||||
if let Ok(_) = context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
|
if context.accounts[self.cursor_pos.0]
|
||||||
|
.status(self.cursor_pos.1)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +121,6 @@ impl CompactListing {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
self.length = mailbox.threads.root_len();
|
self.length = mailbox.threads.root_len();
|
||||||
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
|
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
|
@ -139,9 +141,9 @@ impl CompactListing {
|
||||||
let i = if let Some(i) = container.message() {
|
let i = if let Some(i) = container.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
threads.containers()[
|
threads.containers()[container.first_child().unwrap()]
|
||||||
container.first_child().unwrap()
|
.message()
|
||||||
].message().unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let root_envelope: &Envelope = &mailbox.collection[i];
|
let root_envelope: &Envelope = &mailbox.collection[i];
|
||||||
let fg_color = if has_unseen {
|
let fg_color = if has_unseen {
|
||||||
|
@ -163,16 +165,13 @@ impl CompactListing {
|
||||||
bg_color,
|
bg_color,
|
||||||
((0, idx), (MAX_COLS - 1, idx)),
|
((0, idx), (MAX_COLS - 1, idx)),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
for x in x..MAX_COLS {
|
for x in x..MAX_COLS {
|
||||||
self.content[(x, idx)].set_ch(' ');
|
self.content[(x, idx)].set_ch(' ');
|
||||||
self.content[(x, idx)].set_bg(bg_color);
|
self.content[(x, idx)].set_bg(bg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
||||||
|
@ -185,9 +184,9 @@ impl CompactListing {
|
||||||
let i = if let Some(i) = container.message() {
|
let i = if let Some(i) = container.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
threads.containers()[
|
threads.containers()[container.first_child().unwrap()]
|
||||||
container.first_child().unwrap()
|
.message()
|
||||||
].message().unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let root_envelope: &Envelope = &mailbox.collection[i];
|
let root_envelope: &Envelope = &mailbox.collection[i];
|
||||||
let fg_color = if !root_envelope.is_seen() {
|
let fg_color = if !root_envelope.is_seen() {
|
||||||
|
@ -303,11 +302,7 @@ impl Component for CompactListing {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.view = Some(ThreadView::new(self.cursor_pos, context));
|
self.view = Some(ThreadView::new(self.cursor_pos, context));
|
||||||
self.view.as_mut().unwrap().draw(
|
self.view.as_mut().unwrap().draw(grid, area, context);
|
||||||
grid,
|
|
||||||
area,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,8 +435,7 @@ impl Component for CompactListing {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.refresh_mailbox(context);
|
self.refresh_mailbox(context);
|
||||||
return true;
|
return true;
|
||||||
}
|
} // _ => {}
|
||||||
// _ => {}
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -451,7 +445,9 @@ impl Component for CompactListing {
|
||||||
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||||
}
|
}
|
||||||
fn set_dirty(&mut self) {
|
fn set_dirty(&mut self) {
|
||||||
self.view.as_mut().map(|p| p.set_dirty());
|
if let Some(p) = self.view.as_mut() {
|
||||||
|
p.set_dirty();
|
||||||
|
}
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl MailListing {
|
||||||
local_collection: Vec::new(),
|
local_collection: Vec::new(),
|
||||||
sort: (Default::default(), Default::default()),
|
sort: (Default::default(), Default::default()),
|
||||||
subsort: (Default::default(), Default::default()),
|
subsort: (Default::default(), Default::default()),
|
||||||
content: content,
|
content,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
unfocused: false,
|
unfocused: false,
|
||||||
view: None,
|
view: None,
|
||||||
|
@ -107,7 +107,10 @@ impl MailListing {
|
||||||
//
|
//
|
||||||
loop {
|
loop {
|
||||||
// TODO: Show progress visually
|
// TODO: Show progress visually
|
||||||
if let Ok(_) = context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
|
if context.accounts[self.cursor_pos.0]
|
||||||
|
.status(self.cursor_pos.1)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +165,6 @@ impl MailListing {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
match iter.peek() {
|
match iter.peek() {
|
||||||
Some(&x) if threads[x].indentation() == indentation => {
|
Some(&x) if threads[x].indentation() == indentation => {
|
||||||
indentations.pop();
|
indentations.pop();
|
||||||
|
@ -198,7 +200,7 @@ impl MailListing {
|
||||||
container,
|
container,
|
||||||
&indentations,
|
&indentations,
|
||||||
len,
|
len,
|
||||||
// context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
|
// context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
|
||||||
),
|
),
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
fg_color,
|
fg_color,
|
||||||
|
@ -522,9 +524,7 @@ impl Component for MailListing {
|
||||||
.threaded();
|
.threaded();
|
||||||
let account = &mut context.accounts[self.cursor_pos.0];
|
let account = &mut context.accounts[self.cursor_pos.0];
|
||||||
let (hash, is_seen) = {
|
let (hash, is_seen) = {
|
||||||
let mailbox = &mut account[self.cursor_pos.1]
|
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
|
||||||
.as_mut()
|
|
||||||
.unwrap();
|
|
||||||
let envelope: &mut Envelope = if threaded {
|
let envelope: &mut Envelope = if threaded {
|
||||||
let i = mailbox.threaded_mail(idx);
|
let i = mailbox.threaded_mail(idx);
|
||||||
&mut mailbox.collection[i]
|
&mut mailbox.collection[i]
|
||||||
|
@ -538,9 +538,7 @@ impl Component for MailListing {
|
||||||
let backend = &account.backend;
|
let backend = &account.backend;
|
||||||
backend.operation(hash)
|
backend.operation(hash)
|
||||||
};
|
};
|
||||||
let mailbox = &mut account[self.cursor_pos.1]
|
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
|
||||||
.as_mut()
|
|
||||||
.unwrap();
|
|
||||||
let envelope: &mut Envelope = if threaded {
|
let envelope: &mut Envelope = if threaded {
|
||||||
let i = mailbox.threaded_mail(idx);
|
let i = mailbox.threaded_mail(idx);
|
||||||
&mut mailbox.collection[i]
|
&mut mailbox.collection[i]
|
||||||
|
@ -598,9 +596,7 @@ impl Component for MailListing {
|
||||||
.conf()
|
.conf()
|
||||||
.threaded();
|
.threaded();
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let mailbox = &account[self.cursor_pos.1]
|
let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
|
||||||
.as_ref()
|
|
||||||
.unwrap();
|
|
||||||
let mut coordinates = self.cursor_pos;
|
let mut coordinates = self.cursor_pos;
|
||||||
coordinates.2 = if threaded {
|
coordinates.2 = if threaded {
|
||||||
mailbox.threaded_mail(self.cursor_pos.2)
|
mailbox.threaded_mail(self.cursor_pos.2)
|
||||||
|
@ -792,8 +788,7 @@ impl Component for MailListing {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.refresh_mailbox(context);
|
self.refresh_mailbox(context);
|
||||||
return true;
|
return true;
|
||||||
}
|
} // _ => {}
|
||||||
// _ => {}
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -803,7 +798,9 @@ impl Component for MailListing {
|
||||||
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||||
}
|
}
|
||||||
fn set_dirty(&mut self) {
|
fn set_dirty(&mut self) {
|
||||||
self.view.as_mut().map(|p| p.set_dirty());
|
if let Some(p) = self.view.as_mut() {
|
||||||
|
p.set_dirty();
|
||||||
|
};
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,10 +65,8 @@ impl AccountMenu {
|
||||||
index: i,
|
index: i,
|
||||||
entries: {
|
entries: {
|
||||||
let mut entries = Vec::with_capacity(a.len());
|
let mut entries = Vec::with_capacity(a.len());
|
||||||
let mut idx = 0;
|
for (idx, acc) in a.list_folders().into_iter().enumerate() {
|
||||||
for acc in a.list_folders() {
|
|
||||||
entries.push((idx, acc));
|
entries.push((idx, acc));
|
||||||
idx += 1;
|
|
||||||
}
|
}
|
||||||
entries
|
entries
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl EnvelopeView {
|
||||||
wrapper: EnvelopeWrapper,
|
wrapper: EnvelopeWrapper,
|
||||||
pager: Option<Pager>,
|
pager: Option<Pager>,
|
||||||
subview: Option<Box<Component>>,
|
subview: Option<Box<Component>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
EnvelopeView {
|
EnvelopeView {
|
||||||
pager,
|
pager,
|
||||||
subview,
|
subview,
|
||||||
|
@ -81,33 +81,34 @@ impl EnvelopeView {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the string to be displayed in the Viewer
|
/// Returns the string to be displayed in the Viewer
|
||||||
fn attachment_to_text(&self, body: Attachment) -> String {
|
fn attachment_to_text(&self, body: &Attachment) -> String {
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let body_text = String::from_utf8_lossy(&decode_rec(
|
let body_text = String::from_utf8_lossy(&decode_rec(
|
||||||
&body,
|
&body,
|
||||||
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
||||||
if a.content_type().is_text_html() {
|
if a.content_type().is_text_html() {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
let mut html_filter = Command::new("w3m")
|
let mut html_filter = Command::new("w3m")
|
||||||
.args(&["-I", "utf-8", "-T", "text/html"])
|
.args(&["-I", "utf-8", "-T", "text/html"])
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to start html filter process");
|
.expect("Failed to start html filter process");
|
||||||
|
|
||||||
html_filter
|
html_filter
|
||||||
.stdin
|
.stdin
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&v)
|
.write_all(&v)
|
||||||
.expect("Failed to write to w3m stdin");
|
.expect("Failed to write to w3m stdin");
|
||||||
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n".to_vec();
|
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n"
|
||||||
v.extend(html_filter.wait_with_output().unwrap().stdout);
|
.to_vec();
|
||||||
}
|
v.extend(html_filter.wait_with_output().unwrap().stdout);
|
||||||
})),
|
}
|
||||||
)).into_owned();
|
})),
|
||||||
|
)).into_owned();
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Normal | ViewMode::Subview => {
|
ViewMode::Normal | ViewMode::Subview => {
|
||||||
let mut t = body_text.to_string();
|
let mut t = body_text.to_string();
|
||||||
|
@ -158,7 +159,7 @@ impl EnvelopeView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn plain_text_to_buf(s: &String, highlight_urls: bool) -> CellBuffer {
|
pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
|
||||||
let mut buf = CellBuffer::from(s);
|
let mut buf = CellBuffer::from(s);
|
||||||
|
|
||||||
if highlight_urls {
|
if highlight_urls {
|
||||||
|
@ -197,7 +198,7 @@ impl Component for EnvelopeView {
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
let y :usize = {
|
let y: usize = {
|
||||||
let envelope: &Envelope = &self.wrapper;
|
let envelope: &Envelope = &self.wrapper;
|
||||||
|
|
||||||
if self.mode == ViewMode::Raw {
|
if self.mode == ViewMode::Raw {
|
||||||
|
@ -219,7 +220,7 @@ impl Component for EnvelopeView {
|
||||||
grid[(x, y)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&format!("From: {}", envelope.from_to_string()),
|
&format!("From: {}", envelope.field_from_to_string()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
|
@ -232,7 +233,7 @@ impl Component for EnvelopeView {
|
||||||
grid[(x, y)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&format!("To: {}", envelope.to_to_string()),
|
&format!("To: {}", envelope.field_to_to_string()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
|
@ -293,7 +294,7 @@ impl Component for EnvelopeView {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let buf = {
|
let buf = {
|
||||||
let text = self.attachment_to_text(body);
|
let text = self.attachment_to_text(&body);
|
||||||
// URL indexes must be colored (ugh..)
|
// URL indexes must be colored (ugh..)
|
||||||
EnvelopeView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
|
EnvelopeView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
|
||||||
};
|
};
|
||||||
|
@ -344,7 +345,9 @@ impl Component for EnvelopeView {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() || self.mode == ViewMode::Subview => {
|
UIEventType::Input(Key::Char('r'))
|
||||||
|
if self.mode.is_attachment() || self.mode == ViewMode::Subview =>
|
||||||
|
{
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
self.subview.take();
|
self.subview.take();
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -358,12 +361,19 @@ impl Component for EnvelopeView {
|
||||||
|
|
||||||
{
|
{
|
||||||
let envelope: &Envelope = self.wrapper.envelope();
|
let envelope: &Envelope = self.wrapper.envelope();
|
||||||
if let Some(u) = envelope.body_bytes(self.wrapper.buffer()).attachments().get(lidx) {
|
if let Some(u) = envelope
|
||||||
|
.body_bytes(self.wrapper.buffer())
|
||||||
|
.attachments()
|
||||||
|
.get(lidx)
|
||||||
|
{
|
||||||
match u.content_type() {
|
match u.content_type() {
|
||||||
ContentType::MessageRfc822 => {
|
ContentType::MessageRfc822 => {
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
self.subview = Some(Box::new(Pager::from_str(&String::from_utf8_lossy(&decode_rec(u, None)).to_string(), None)));
|
self.subview = Some(Box::new(Pager::from_str(
|
||||||
},
|
&String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
|
||||||
|
None,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
ContentType::Text { .. } => {
|
ContentType::Text { .. } => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
|
@ -416,7 +426,7 @@ impl Component for EnvelopeView {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
UIEventType::Input(Key::Char('g'))
|
UIEventType::Input(Key::Char('g'))
|
||||||
if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url =>
|
if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url =>
|
||||||
{
|
{
|
||||||
|
@ -425,7 +435,10 @@ impl Component for EnvelopeView {
|
||||||
let url = {
|
let url = {
|
||||||
let envelope: &Envelope = self.wrapper.envelope();
|
let envelope: &Envelope = self.wrapper.envelope();
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let mut t = envelope.body_bytes(self.wrapper.buffer()).text().to_string();
|
let mut t = envelope
|
||||||
|
.body_bytes(self.wrapper.buffer())
|
||||||
|
.text()
|
||||||
|
.to_string();
|
||||||
let links: Vec<Link> = finder.links(&t).collect();
|
let links: Vec<Link> = finder.links(&t).collect();
|
||||||
if let Some(u) = links.get(lidx) {
|
if let Some(u) = links.get(lidx) {
|
||||||
u.as_str().to_string()
|
u.as_str().to_string()
|
||||||
|
|
|
@ -70,31 +70,28 @@ impl Component for HtmlView {
|
||||||
if self.pager.process_event(event, context) {
|
if self.pager.process_event(event, context) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
match event.event_type {
|
if let UIEventType::Input(Key::Char('v')) = event.event_type {
|
||||||
UIEventType::Input(Key::Char('v')) => {
|
// TODO: Optional filter that removes outgoing resource requests (images and
|
||||||
// TODO: Optional filter that removes outgoing resource requests (images and
|
// scripts)
|
||||||
// scripts)
|
let binary = query_default_app("text/html");
|
||||||
let binary = query_default_app("text/html");
|
if let Ok(binary) = binary {
|
||||||
if let Ok(binary) = binary {
|
let mut p = create_temp_file(&self.bytes, None);
|
||||||
let mut p = create_temp_file(&self.bytes, None);
|
Command::new(&binary)
|
||||||
Command::new(&binary)
|
.arg(p.path())
|
||||||
.arg(p.path())
|
.stdin(Stdio::piped())
|
||||||
.stdin(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.spawn()
|
||||||
.spawn()
|
.unwrap_or_else(|_| panic!("Failed to start {}", binary.display()));
|
||||||
.unwrap_or_else(|_| panic!("Failed to start {}", binary.display()));
|
context.temp_files.push(p);
|
||||||
context.temp_files.push(p);
|
} else {
|
||||||
} else {
|
context.replies.push_back(UIEvent {
|
||||||
context.replies.push_back(UIEvent {
|
id: 0,
|
||||||
id: 0,
|
event_type: UIEventType::StatusNotification(
|
||||||
event_type: UIEventType::StatusNotification(format!(
|
"Couldn't find a default application for html files.".to_string(),
|
||||||
"Couldn't find a default application for html files."
|
),
|
||||||
)),
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ impl MailView {
|
||||||
coordinates: (usize, usize, usize),
|
coordinates: (usize, usize, usize),
|
||||||
pager: Option<Pager>,
|
pager: Option<Pager>,
|
||||||
subview: Option<Box<Component>>,
|
subview: Option<Box<Component>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
MailView {
|
MailView {
|
||||||
coordinates,
|
coordinates,
|
||||||
pager,
|
pager,
|
||||||
|
@ -95,33 +95,34 @@ impl MailView {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the string to be displayed in the Viewer
|
/// Returns the string to be displayed in the Viewer
|
||||||
fn attachment_to_text(&self, body: Attachment) -> String {
|
fn attachment_to_text(&self, body: &Attachment) -> String {
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let body_text = String::from_utf8_lossy(&decode_rec(
|
let body_text = String::from_utf8_lossy(&decode_rec(
|
||||||
&body,
|
&body,
|
||||||
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
||||||
if a.content_type().is_text_html() {
|
if a.content_type().is_text_html() {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
let mut html_filter = Command::new("w3m")
|
let mut html_filter = Command::new("w3m")
|
||||||
.args(&["-I", "utf-8", "-T", "text/html"])
|
.args(&["-I", "utf-8", "-T", "text/html"])
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to start html filter process");
|
.expect("Failed to start html filter process");
|
||||||
|
|
||||||
html_filter
|
html_filter
|
||||||
.stdin
|
.stdin
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&v)
|
.write_all(&v)
|
||||||
.expect("Failed to write to w3m stdin");
|
.expect("Failed to write to w3m stdin");
|
||||||
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n".to_vec();
|
*v = b"Text piped through `w3m`. Press `v` to open in web browser. \n\n"
|
||||||
v.extend(html_filter.wait_with_output().unwrap().stdout);
|
.to_vec();
|
||||||
}
|
v.extend(html_filter.wait_with_output().unwrap().stdout);
|
||||||
})),
|
}
|
||||||
)).into_owned();
|
})),
|
||||||
|
)).into_owned();
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Normal | ViewMode::Subview => {
|
ViewMode::Normal | ViewMode::Subview => {
|
||||||
let mut t = body_text.to_string();
|
let mut t = body_text.to_string();
|
||||||
|
@ -172,7 +173,7 @@ impl MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn plain_text_to_buf(s: &String, highlight_urls: bool) -> CellBuffer {
|
pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
|
||||||
let mut buf = CellBuffer::from(s);
|
let mut buf = CellBuffer::from(s);
|
||||||
|
|
||||||
if highlight_urls {
|
if highlight_urls {
|
||||||
|
@ -237,7 +238,7 @@ impl Component for MailView {
|
||||||
grid[(x, y)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&format!("From: {}", envelope.from_to_string()),
|
&format!("From: {}", envelope.field_from_to_string()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
|
@ -250,7 +251,7 @@ impl Component for MailView {
|
||||||
grid[(x, y)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&format!("To: {}", envelope.to_to_string()),
|
&format!("To: {}", envelope.field_to_to_string()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
|
@ -302,7 +303,9 @@ impl Component for MailView {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope: &Envelope = &mailbox.collection[mailbox_idx.2];
|
let envelope: &Envelope = &mailbox.collection[mailbox_idx.2];
|
||||||
let op = context.accounts[mailbox_idx.0].backend.operation(envelope.hash());
|
let op = context.accounts[mailbox_idx.0]
|
||||||
|
.backend
|
||||||
|
.operation(envelope.hash());
|
||||||
let body = envelope.body(op);
|
let body = envelope.body(op);
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
||||||
|
@ -317,7 +320,7 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let buf = {
|
let buf = {
|
||||||
let text = self.attachment_to_text(body);
|
let text = self.attachment_to_text(&body);
|
||||||
// URL indexes must be colored (ugh..)
|
// URL indexes must be colored (ugh..)
|
||||||
MailView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
|
MailView::plain_text_to_buf(&text, self.mode == ViewMode::Url)
|
||||||
};
|
};
|
||||||
|
@ -365,7 +368,9 @@ impl Component for MailView {
|
||||||
};
|
};
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() || self.mode == ViewMode::Subview => {
|
UIEventType::Input(Key::Char('r'))
|
||||||
|
if self.mode.is_attachment() || self.mode == ViewMode::Subview =>
|
||||||
|
{
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
self.subview.take();
|
self.subview.take();
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -383,26 +388,30 @@ impl Component for MailView {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
|
let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
|
||||||
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash());
|
let op = context.accounts[self.coordinates.0]
|
||||||
|
.backend
|
||||||
|
.operation(envelope.hash());
|
||||||
if let Some(u) = envelope.body(op).attachments().get(lidx) {
|
if let Some(u) = envelope.body(op).attachments().get(lidx) {
|
||||||
match u.content_type() {
|
match u.content_type() {
|
||||||
ContentType::MessageRfc822 => {
|
ContentType::MessageRfc822 => {
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
match EnvelopeWrapper::new(u.bytes().to_vec()) {
|
match EnvelopeWrapper::new(u.bytes().to_vec()) {
|
||||||
Ok(wrapper) => {
|
Ok(wrapper) => {
|
||||||
self.subview = Some(Box::new(EnvelopeView::new(wrapper, None, None)));
|
self.subview =
|
||||||
},
|
Some(Box::new(EnvelopeView::new(wrapper, None, None)));
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
context.replies.push_back(UIEvent {
|
context.replies.push_back(UIEvent {
|
||||||
id: 0,
|
id: 0,
|
||||||
event_type: UIEventType::StatusNotification(
|
event_type: UIEventType::StatusNotification(format!(
|
||||||
format!("{}", e)
|
"{}",
|
||||||
),
|
e
|
||||||
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
ContentType::Text { .. } => {
|
ContentType::Text { .. } => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
|
@ -468,7 +477,9 @@ impl Component for MailView {
|
||||||
|
|
||||||
let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
|
let envelope: &Envelope = &mailbox.collection[self.coordinates.2];
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash());
|
let op = context.accounts[self.coordinates.0]
|
||||||
|
.backend
|
||||||
|
.operation(envelope.hash());
|
||||||
let mut t = envelope.body(op).text().to_string();
|
let mut t = envelope.body(op).text().to_string();
|
||||||
let links: Vec<Link> = finder.links(&t).collect();
|
let links: Vec<Link> = finder.links(&t).collect();
|
||||||
if let Some(u) = links.get(lidx) {
|
if let Some(u) = links.get(lidx) {
|
||||||
|
@ -500,7 +511,9 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
_ => { return false; }
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ struct ThreadEntry {
|
||||||
msg_idx: usize,
|
msg_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ThreadView {
|
pub struct ThreadView {
|
||||||
new_cursor_pos: usize,
|
new_cursor_pos: usize,
|
||||||
|
@ -49,7 +48,9 @@ pub struct ThreadView {
|
||||||
impl ThreadView {
|
impl ThreadView {
|
||||||
pub fn new(coordinates: (usize, usize, usize), context: &Context) -> Self {
|
pub fn new(coordinates: (usize, usize, usize), context: &Context) -> Self {
|
||||||
let mut stack: Vec<(usize, usize)> = Vec::with_capacity(32);
|
let mut stack: Vec<(usize, usize)> = Vec::with_capacity(32);
|
||||||
let mailbox = &context.accounts[coordinates.0][coordinates.1].as_ref().unwrap();
|
let mailbox = &context.accounts[coordinates.0][coordinates.1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap();
|
||||||
let threads = &mailbox.threads;
|
let threads = &mailbox.threads;
|
||||||
let container = &threads.containers()[threads.root_set()[coordinates.2]];
|
let container = &threads.containers()[threads.root_set()[coordinates.2]];
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ impl ThreadView {
|
||||||
entries: Vec::new(),
|
entries: Vec::new(),
|
||||||
cursor_pos: 1,
|
cursor_pos: 1,
|
||||||
new_cursor_pos: 0,
|
new_cursor_pos: 0,
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut line = 0;
|
let mut line = 0;
|
||||||
let mut max_ind = 0;
|
let mut max_ind = 0;
|
||||||
|
@ -87,22 +88,28 @@ impl ThreadView {
|
||||||
view.new_expanded_pos = view.entries.len() - 1;
|
view.new_expanded_pos = view.entries.len() - 1;
|
||||||
view.expanded_pos = view.new_expanded_pos + 1;
|
view.expanded_pos = view.new_expanded_pos + 1;
|
||||||
|
|
||||||
let height = 2*view.entries.len();
|
let height = 2 * view.entries.len();
|
||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
|
|
||||||
let mut strings: Vec<String> = Vec::with_capacity(view.entries.len());
|
let mut strings: Vec<String> = Vec::with_capacity(view.entries.len());
|
||||||
|
|
||||||
|
|
||||||
for e in &view.entries {
|
for e in &view.entries {
|
||||||
let envelope: &Envelope = &mailbox.collection[e.msg_idx];
|
let envelope: &Envelope = &mailbox.collection[e.msg_idx];
|
||||||
strings.push(format!(" {}{} - {}", " ".repeat(e.index.0 * 4), envelope.date_as_str(), envelope.from_to_string()));
|
strings.push(format!(
|
||||||
width = cmp::max(width, e.index.0 + strings.last().as_ref().unwrap().len() + 1);
|
" {}{} - {}",
|
||||||
|
" ".repeat(e.index.0 * 4),
|
||||||
|
envelope.date_as_str(),
|
||||||
|
envelope.field_from_to_string()
|
||||||
|
));
|
||||||
|
width = cmp::max(
|
||||||
|
width,
|
||||||
|
e.index.0 + strings.last().as_ref().unwrap().len() + 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let mut content = CellBuffer::new(width, height, Cell::default());
|
let mut content = CellBuffer::new(width, height, Cell::default());
|
||||||
for (y, e) in view.entries.iter().enumerate() {
|
for (y, e) in view.entries.iter().enumerate() {
|
||||||
if y > 0 && content.get_mut(e.index.0 * 4, 2*y - 1).is_some() {
|
if y > 0 && content.get_mut(e.index.0 * 4, 2 * y - 1).is_some() {
|
||||||
let index = (e.index.0 * 4, 2*y - 1);
|
let index = (e.index.0 * 4, 2 * y - 1);
|
||||||
if content[index].ch() == ' ' {
|
if content[index].ch() == ' ' {
|
||||||
let mut ctr = 1;
|
let mut ctr = 1;
|
||||||
while content[(e.index.0 * 4 + ctr, 2 * y - 1)].ch() == ' ' {
|
while content[(e.index.0 * 4 + ctr, 2 * y - 1)].ch() == ' ' {
|
||||||
|
@ -120,14 +127,14 @@ impl ThreadView {
|
||||||
&mut content,
|
&mut content,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((e.index.0 + 1, 2*y), (width - 1, height - 1)),
|
((e.index.0 + 1, 2 * y), (width - 1, height - 1)),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
content[(e.index.0 * 4, 2*y)].set_ch(VERT_BOUNDARY);
|
content[(e.index.0 * 4, 2 * y)].set_ch(VERT_BOUNDARY);
|
||||||
for i in (e.index.0 * 4)..width {
|
for i in (e.index.0 * 4)..width {
|
||||||
content[(i, 2*y + 1)].set_ch(HORZ_BOUNDARY);
|
content[(i, 2 * y + 1)].set_ch(HORZ_BOUNDARY);
|
||||||
}
|
}
|
||||||
content[(e.index.0 *4, 2*y + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
|
content[(e.index.0 * 4, 2 * y + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
|
||||||
}
|
}
|
||||||
//view.mailview = MailView::new((view.coordinates.0, view.coordinates.1, view.entries[view.expanded_pos].msg_idx), None, None);
|
//view.mailview = MailView::new((view.coordinates.0, view.coordinates.1, view.entries[view.expanded_pos].msg_idx), None, None);
|
||||||
view.content = content;
|
view.content = content;
|
||||||
|
@ -144,23 +151,28 @@ impl ThreadView {
|
||||||
let msg_idx = if let Some(i) = container.message() {
|
let msg_idx = if let Some(i) = container.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
mailbox.threads.containers()[
|
mailbox.threads.containers()[container.first_child().unwrap()]
|
||||||
container.first_child().unwrap()
|
.message()
|
||||||
].message().unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let envelope: &Envelope = &mailbox.collection[msg_idx];
|
let envelope: &Envelope = &mailbox.collection[msg_idx];
|
||||||
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash());
|
let op = context.accounts[self.coordinates.0]
|
||||||
|
.backend
|
||||||
|
.operation(envelope.hash());
|
||||||
let body = envelope.body(op);
|
let body = envelope.body(op);
|
||||||
|
|
||||||
let mut body_text: String = " \n".repeat(6);
|
let mut body_text: String = " \n".repeat(6);
|
||||||
body_text.push_str(&String::from_utf8_lossy(&decode_rec(&body, None)));
|
body_text.push_str(&String::from_utf8_lossy(&decode_rec(&body, None)));
|
||||||
let mut buf = CellBuffer::from(&body_text).split_newlines();
|
let mut buf = CellBuffer::from(body_text.as_str()).split_newlines();
|
||||||
|
|
||||||
|
|
||||||
let date = format!("Date: {}\n", envelope.date_as_str());
|
let date = format!("Date: {}\n", envelope.date_as_str());
|
||||||
let from = format!("From: {}\n", envelope.from_to_string());
|
let from = format!("From: {}\n", envelope.field_from_to_string());
|
||||||
let message_id = &format!("Message-ID: <{}>\n\n", envelope.message_id_raw());
|
let message_id = &format!("Message-ID: <{}>\n\n", envelope.message_id_raw());
|
||||||
let mut width = [date.len(), from.len(), message_id.len(), buf.size().0].iter().map(|&v| v).max().unwrap_or(1);
|
let mut width = [date.len(), from.len(), message_id.len(), buf.size().0]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.max()
|
||||||
|
.unwrap_or(1);
|
||||||
let height = buf.size().1;
|
let height = buf.size().1;
|
||||||
if width > buf.size().0 {
|
if width > buf.size().0 {
|
||||||
buf.resize(width, height, Cell::default());
|
buf.resize(width, height, Cell::default());
|
||||||
|
@ -176,7 +188,7 @@ impl ThreadView {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((ind, 0), (width, height)),
|
((ind, 0), (width, height)),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&from,
|
&from,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
|
@ -184,7 +196,7 @@ impl ThreadView {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((ind, 1), (width, height)),
|
((ind, 1), (width, height)),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&message_id,
|
&message_id,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
|
@ -192,7 +204,7 @@ impl ThreadView {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((ind, 2), (width, height)),
|
((ind, 2), (width, height)),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
ThreadEntry {
|
ThreadEntry {
|
||||||
index: (ind, idx, order),
|
index: (ind, idx, order),
|
||||||
|
@ -237,8 +249,8 @@ impl ThreadView {
|
||||||
self.cursor_pos = self.new_cursor_pos;
|
self.cursor_pos = self.new_cursor_pos;
|
||||||
for &idx in &[old_cursor_pos, self.new_cursor_pos] {
|
for &idx in &[old_cursor_pos, self.new_cursor_pos] {
|
||||||
let new_area = (
|
let new_area = (
|
||||||
set_y(upper_left, get_y(upper_left) + 2*(idx % rows)),
|
set_y(upper_left, get_y(upper_left) + 2 * (idx % rows)),
|
||||||
set_y(bottom_right, get_y(upper_left) + 2*(idx % rows)),
|
set_y(bottom_right, get_y(upper_left) + 2 * (idx % rows)),
|
||||||
);
|
);
|
||||||
self.highlight_line(grid, new_area, idx);
|
self.highlight_line(grid, new_area, idx);
|
||||||
context.dirty_areas.push_back(new_area);
|
context.dirty_areas.push_back(new_area);
|
||||||
|
@ -253,13 +265,16 @@ impl ThreadView {
|
||||||
grid,
|
grid,
|
||||||
&self.content,
|
&self.content,
|
||||||
area,
|
area,
|
||||||
((0, 2*top_idx), (width - 1, height - 1)),
|
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||||
);
|
);
|
||||||
self.highlight_line(
|
self.highlight_line(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
set_y(upper_left, get_y(upper_left) + 2*(self.cursor_pos % rows)),
|
set_y(upper_left, get_y(upper_left) + 2 * (self.cursor_pos % rows)),
|
||||||
set_y(bottom_right, get_y(upper_left) + 2*(self.cursor_pos % rows)),
|
set_y(
|
||||||
|
bottom_right,
|
||||||
|
get_y(upper_left) + 2 * (self.cursor_pos % rows),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
self.cursor_pos,
|
self.cursor_pos,
|
||||||
);
|
);
|
||||||
|
@ -292,22 +307,25 @@ impl Component for ThreadView {
|
||||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||||
|
|
||||||
if !self.dirty {
|
if !self.dirty {
|
||||||
self.mailview.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
self.mailview
|
||||||
|
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
|
||||||
let y = {
|
let y = {
|
||||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1].as_ref().unwrap();
|
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap();
|
||||||
let threads = &mailbox.threads;
|
let threads = &mailbox.threads;
|
||||||
let container = &threads.containers()[threads.root_set()[self.coordinates.2]];
|
let container = &threads.containers()[threads.root_set()[self.coordinates.2]];
|
||||||
let i = if let Some(i) = container.message() {
|
let i = if let Some(i) = container.message() {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
threads.containers()[
|
threads.containers()[container.first_child().unwrap()]
|
||||||
container.first_child().unwrap()
|
.message()
|
||||||
].message().unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let envelope: &Envelope = &mailbox.collection[i];
|
let envelope: &Envelope = &mailbox.collection[i];
|
||||||
|
|
||||||
|
@ -318,7 +336,7 @@ impl Component for ThreadView {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
area,
|
area,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, y)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, y)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
|
@ -341,7 +359,7 @@ impl Component for ThreadView {
|
||||||
&self.content,
|
&self.content,
|
||||||
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
||||||
((0, 0), (width - 1, height - 1)),
|
((0, 0), (width - 1, height - 1)),
|
||||||
);
|
);
|
||||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||||
grid[(x, mid)].set_ch(HORZ_BOUNDARY);
|
grid[(x, mid)].set_ch(HORZ_BOUNDARY);
|
||||||
}
|
}
|
||||||
|
@ -356,10 +374,23 @@ impl Component for ThreadView {
|
||||||
|
|
||||||
if self.new_expanded_pos != self.expanded_pos {
|
if self.new_expanded_pos != self.expanded_pos {
|
||||||
self.expanded_pos = self.new_expanded_pos;
|
self.expanded_pos = self.new_expanded_pos;
|
||||||
self.mailview = MailView::new((self.coordinates.0, self.coordinates.1, self.entries[self.expanded_pos].msg_idx), None, None);
|
self.mailview = MailView::new(
|
||||||
|
(
|
||||||
|
self.coordinates.0,
|
||||||
|
self.coordinates.1,
|
||||||
|
self.entries[self.expanded_pos].msg_idx,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.draw_list(grid, (set_y(upper_left, y), set_y(bottom_right, mid - 1)), context);
|
self.draw_list(
|
||||||
self.mailview.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
grid,
|
||||||
|
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
self.mailview
|
||||||
|
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||||
if self.mailview.process_event(event, context) {
|
if self.mailview.process_event(event, context) {
|
||||||
|
@ -372,7 +403,7 @@ impl Component for ThreadView {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
UIEventType::Input(Key::Down) => {
|
UIEventType::Input(Key::Down) => {
|
||||||
let height = self.entries.len();
|
let height = self.entries.len();
|
||||||
if height > 0 && self.cursor_pos + 1 < height {
|
if height > 0 && self.cursor_pos + 1 < height {
|
||||||
|
@ -385,10 +416,10 @@ impl Component for ThreadView {
|
||||||
self.new_expanded_pos = self.cursor_pos;
|
self.new_expanded_pos = self.cursor_pos;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
UIEventType::Resize => {
|
UIEventType::Resize => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub mod utilities;
|
||||||
pub use self::utilities::*;
|
pub use self::utilities::*;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Display, Debug};
|
use std::fmt::{Debug, Display};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use super::{Key, UIEvent, UIEventType};
|
use super::{Key, UIEvent, UIEventType};
|
||||||
|
@ -76,7 +76,6 @@ impl Display for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Deref for Entity {
|
impl Deref for Entity {
|
||||||
type Target = Box<Component>;
|
type Target = Box<Component>;
|
||||||
|
|
||||||
|
@ -227,22 +226,22 @@ fn write_string_to_grid(
|
||||||
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
||||||
return (x, y);
|
return (x, y);
|
||||||
}
|
}
|
||||||
'char: for c in s.chars() {
|
'char: for c in s.chars() {
|
||||||
grid[(x, y)].set_ch(c);
|
grid[(x, y)].set_ch(c);
|
||||||
grid[(x, y)].set_fg(fg_color);
|
grid[(x, y)].set_fg(fg_color);
|
||||||
grid[(x, y)].set_bg(bg_color);
|
grid[(x, y)].set_bg(bg_color);
|
||||||
x += 1;
|
x += 1;
|
||||||
|
|
||||||
if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) {
|
if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) {
|
||||||
x = get_x(upper_left);
|
x = get_x(upper_left);
|
||||||
y += 1;
|
y += 1;
|
||||||
if y > (get_y(bottom_right)) || y > get_y(bounds) {
|
if y > (get_y(bottom_right)) || y > get_y(bounds) {
|
||||||
return (x, y - 1);
|
return (x, y - 1);
|
||||||
}
|
|
||||||
if !line_break {
|
|
||||||
break 'char;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if !line_break {
|
||||||
|
break 'char;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(x, y)
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,9 @@ impl Component for HSplit {
|
||||||
for i in get_x(upper_left)..=get_x(bottom_right) {
|
for i in get_x(upper_left)..=get_x(bottom_right) {
|
||||||
grid[(i, mid)].set_ch('─');
|
grid[(i, mid)].set_ch('─');
|
||||||
}
|
}
|
||||||
context.dirty_areas.push_back(((get_x(upper_left), mid), (get_x(bottom_right), mid)));
|
context
|
||||||
|
.dirty_areas
|
||||||
|
.push_back(((get_x(upper_left), mid), (get_x(bottom_right), mid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.top.component.draw(
|
self.top.component.draw(
|
||||||
|
@ -154,14 +156,13 @@ impl Component for VSplit {
|
||||||
.get(mid, get_y(bottom_right) - 1)
|
.get(mid, get_y(bottom_right) - 1)
|
||||||
.map(|a| a.ch())
|
.map(|a| a.ch())
|
||||||
.unwrap_or_else(|| ' ');
|
.unwrap_or_else(|| ' ');
|
||||||
match c {
|
if let HORZ_BOUNDARY = c {
|
||||||
HORZ_BOUNDARY => {
|
grid[(mid, get_y(bottom_right) + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
|
||||||
grid[(mid, get_y(bottom_right) + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.dirty_areas.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
|
context
|
||||||
|
.dirty_areas
|
||||||
|
.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
|
||||||
}
|
}
|
||||||
self.left
|
self.left
|
||||||
.component
|
.component
|
||||||
|
@ -209,13 +210,13 @@ impl fmt::Display for Pager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pager {
|
impl Pager {
|
||||||
pub fn update_from_string(&mut self, text: String) -> () {
|
pub fn update_from_str(&mut self, text: &str) -> () {
|
||||||
let lines: Vec<&str> = text.trim().split('\n').collect();
|
let lines: Vec<&str> = text.trim().split('\n').collect();
|
||||||
let height = lines.len() + 1;
|
let height = lines.len() + 1;
|
||||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||||
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
||||||
//interpret_format_flowed(&text);
|
//interpret_format_flowed(&text);
|
||||||
Pager::print_string(&mut content, &text);
|
Pager::print_string(&mut content, text);
|
||||||
self.content = content;
|
self.content = content;
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self.width = width;
|
self.width = width;
|
||||||
|
@ -256,11 +257,11 @@ impl Pager {
|
||||||
Pager::print_string(&mut content, &text);
|
Pager::print_string(&mut content, &text);
|
||||||
Pager {
|
Pager {
|
||||||
cursor_pos: cursor_pos.unwrap_or(0),
|
cursor_pos: cursor_pos.unwrap_or(0),
|
||||||
height: height,
|
height,
|
||||||
width: width,
|
width,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
content: content,
|
content,
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_str(s: &str, cursor_pos: Option<usize>) -> Self {
|
pub fn from_str(s: &str, cursor_pos: Option<usize>) -> Self {
|
||||||
|
@ -275,7 +276,7 @@ impl Pager {
|
||||||
width,
|
width,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
content,
|
content,
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_buf(content: CellBuffer, cursor_pos: Option<usize>) -> Self {
|
pub fn from_buf(content: CellBuffer, cursor_pos: Option<usize>) -> Self {
|
||||||
|
@ -286,7 +287,7 @@ impl Pager {
|
||||||
width,
|
width,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
content,
|
content,
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn print_string(content: &mut CellBuffer, s: &str) {
|
pub fn print_string(content: &mut CellBuffer, s: &str) {
|
||||||
|
@ -327,14 +328,14 @@ impl Component for Pager {
|
||||||
match mvm {
|
match mvm {
|
||||||
PagerMovement::PageUp => {
|
PagerMovement::PageUp => {
|
||||||
self.cursor_pos = self.cursor_pos.saturating_sub(height);
|
self.cursor_pos = self.cursor_pos.saturating_sub(height);
|
||||||
},
|
}
|
||||||
PagerMovement::PageDown => {
|
PagerMovement::PageDown => {
|
||||||
if self.cursor_pos + 2*height + 1 < self.height {
|
if self.cursor_pos + 2 * height + 1 < self.height {
|
||||||
self.cursor_pos += height;
|
self.cursor_pos += height;
|
||||||
} else {
|
} else {
|
||||||
self.cursor_pos = self.height.saturating_sub(height).saturating_sub(1);
|
self.cursor_pos = self.height.saturating_sub(height).saturating_sub(1);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +395,9 @@ impl Component for Pager {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_ => { return false; }
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -586,7 +589,7 @@ impl Component for StatusBar {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
fn is_dirty(&self) -> bool {
|
fn is_dirty(&self) -> bool {
|
||||||
self.dirty || self.container.component.is_dirty()
|
self.dirty || self.container.component.is_dirty()
|
||||||
|
@ -617,7 +620,9 @@ impl fmt::Display for TextBox {
|
||||||
|
|
||||||
impl Component for TextBox {
|
impl Component for TextBox {
|
||||||
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
|
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
|
||||||
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool { false }
|
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
fn set_dirty(&mut self) {}
|
fn set_dirty(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +674,7 @@ impl Component for Progress {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
|
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
fn set_dirty(&mut self) {}
|
fn set_dirty(&mut self) {}
|
||||||
}
|
}
|
||||||
|
@ -751,13 +756,10 @@ impl Component for Tabbed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||||
match &event.event_type {
|
if let UIEventType::Input(Key::Char('T')) = event.event_type {
|
||||||
UIEventType::Input(Key::Char('T')) => {
|
self.cursor_pos = (self.cursor_pos + 1) % self.children.len();
|
||||||
self.cursor_pos = (self.cursor_pos + 1) % self.children.len();
|
self.children[self.cursor_pos].set_dirty();
|
||||||
self.children[self.cursor_pos].set_dirty();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
self.children[self.cursor_pos].process_event(event, context)
|
self.children[self.cursor_pos].process_event(event, context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,10 +117,13 @@ impl Settings {
|
||||||
sent_folder,
|
sent_folder,
|
||||||
};
|
};
|
||||||
|
|
||||||
s.insert(id, AccountConf {
|
s.insert(
|
||||||
account: acc,
|
id,
|
||||||
conf: x
|
AccountConf {
|
||||||
});
|
account: acc,
|
||||||
|
conf: x,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
|
|
|
@ -23,14 +23,13 @@
|
||||||
* User actions that need to be handled by the UI
|
* User actions that need to be handled by the UI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub use melib::mailbox::{SortOrder, SortField};
|
pub use melib::mailbox::{SortField, SortOrder};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MailListingAction {
|
pub enum MailListingAction {
|
||||||
ToggleThreaded,
|
ToggleThreaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
MailListing(MailListingAction),
|
MailListing(MailListingAction),
|
||||||
|
|
|
@ -32,8 +32,8 @@ use super::*;
|
||||||
use chan::{Receiver, Sender};
|
use chan::{Receiver, Sender};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::thread;
|
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
use termion::raw::IntoRawMode;
|
use termion::raw::IntoRawMode;
|
||||||
use termion::screen::AlternateScreen;
|
use termion::screen::AlternateScreen;
|
||||||
|
@ -60,9 +60,9 @@ impl InputHandler {
|
||||||
tx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(UIMode::Fork)));
|
tx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(UIMode::Fork)));
|
||||||
},
|
},
|
||||||
&rx,
|
&rx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
fn kill(&self) {
|
fn kill(&self) {
|
||||||
self.tx.send(false);
|
self.tx.send(false);
|
||||||
|
@ -100,23 +100,33 @@ impl Context {
|
||||||
}
|
}
|
||||||
pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<bool, usize> {
|
pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<bool, usize> {
|
||||||
let s = self.accounts[idx_a].status(idx_m)?;
|
let s = self.accounts[idx_a].status(idx_m)?;
|
||||||
if let Some(Some(event)) = s {
|
match s {
|
||||||
eprintln!("setting up notification");
|
LoadMailboxResult::New(event) => {
|
||||||
let (idx_a, idx_m) = self.mailbox_hashes[&event.folder];
|
eprintln!("setting up notification");
|
||||||
let subjects = {
|
let (idx_a, idx_m) = self.mailbox_hashes[&event.folder];
|
||||||
let mut ret = Vec::with_capacity(event.index.len());
|
let subjects = {
|
||||||
eprintln!("index is {:?}", &event.index);
|
let mut ret = Vec::with_capacity(event.index.len());
|
||||||
for &i in &event.index {
|
eprintln!("index is {:?}", &event.index);
|
||||||
ret.push(self.accounts[idx_a][idx_m].as_ref().unwrap().collection[i].subject());
|
for &i in &event.index {
|
||||||
}
|
ret.push(
|
||||||
ret
|
self.accounts[idx_a][idx_m].as_ref().unwrap().collection[i].subject(),
|
||||||
};
|
);
|
||||||
self.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Notification(format!("Update in {}/{}, indexes {:?}", self.accounts[idx_a].name(), self.accounts[idx_a][idx_m].as_ref().unwrap().folder.name(), subjects)) });
|
}
|
||||||
Ok(true)
|
ret
|
||||||
} else if let Some(None) = s {
|
};
|
||||||
Ok(true)
|
self.replies.push_back(UIEvent {
|
||||||
} else {
|
id: 0,
|
||||||
Ok(false)
|
event_type: UIEventType::Notification(format!(
|
||||||
|
"Update in {}/{}, indexes {:?}",
|
||||||
|
self.accounts[idx_a].name(),
|
||||||
|
self.accounts[idx_a][idx_m].as_ref().unwrap().folder.name(),
|
||||||
|
subjects
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
LoadMailboxResult::Loaded => Ok(true),
|
||||||
|
LoadMailboxResult::Refresh => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +164,12 @@ impl<W: Write> Drop for State<W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for State<std::io::Stdout> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl State<std::io::Stdout> {
|
impl State<std::io::Stdout> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
/* Create a channel to communicate with other threads. The main process is the sole receiver.
|
/* Create a channel to communicate with other threads. The main process is the sole receiver.
|
||||||
|
@ -236,8 +252,10 @@ impl State<std::io::Stdout> {
|
||||||
startup_thread: Some(startup_tx.clone()),
|
startup_thread: Some(startup_tx.clone()),
|
||||||
threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
||||||
};
|
};
|
||||||
s.threads
|
s.threads.insert(
|
||||||
.insert(startup_thread.thread().id(), (startup_tx.clone(), startup_thread));
|
startup_thread.thread().id(),
|
||||||
|
(startup_tx.clone(), startup_thread),
|
||||||
|
);
|
||||||
write!(
|
write!(
|
||||||
s.stdout(),
|
s.stdout(),
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
|
@ -290,13 +308,16 @@ impl State<std::io::Stdout> {
|
||||||
thread::sleep(dur);
|
thread::sleep(dur);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("Failed to spawn startup-thread in hash_to_folder()")
|
.expect("Failed to spawn startup-thread in hash_to_folder()")
|
||||||
};
|
};
|
||||||
self.startup_thread = Some(startup_tx.clone());
|
self.startup_thread = Some(startup_tx.clone());
|
||||||
self.threads
|
self.threads
|
||||||
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
|
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
|
||||||
} else {
|
} else {
|
||||||
eprintln!("BUG: mailbox with hash {} not found in mailbox_hashes.", hash);
|
eprintln!(
|
||||||
|
"BUG: mailbox with hash {} not found in mailbox_hashes.",
|
||||||
|
hash
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,18 @@
|
||||||
|
|
||||||
use async::*;
|
use async::*;
|
||||||
use conf::AccountConf;
|
use conf::AccountConf;
|
||||||
use melib::error::Result;
|
use mailbox::backends::{Backends, Folder, MailBackend, RefreshEventConsumer};
|
||||||
use mailbox::backends::{MailBackend, Folder, Backends, RefreshEventConsumer};
|
|
||||||
use mailbox::*;
|
use mailbox::*;
|
||||||
|
use melib::error::Result;
|
||||||
|
use std::mem;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::mem;
|
|
||||||
|
pub enum LoadMailboxResult {
|
||||||
|
New(NewMailEvent),
|
||||||
|
Refresh,
|
||||||
|
Loaded,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NewMailEvent {
|
pub struct NewMailEvent {
|
||||||
pub folder: u64,
|
pub folder: u64,
|
||||||
|
@ -68,15 +74,13 @@ impl Account {
|
||||||
workers.push(Some(handle));
|
workers.push(Some(handle));
|
||||||
}
|
}
|
||||||
Account {
|
Account {
|
||||||
name: name,
|
name,
|
||||||
folders: folders,
|
folders,
|
||||||
workers: workers,
|
workers,
|
||||||
|
sent_folder,
|
||||||
sent_folder: sent_folder,
|
|
||||||
|
|
||||||
settings: settings.clone(),
|
settings: settings.clone(),
|
||||||
runtime_settings: settings,
|
runtime_settings: settings,
|
||||||
backend: backend,
|
backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn reload(&mut self, idx: usize) {
|
pub fn reload(&mut self, idx: usize) {
|
||||||
|
@ -91,6 +95,9 @@ impl Account {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.folders.len()
|
self.folders.len()
|
||||||
}
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.folders.is_empty()
|
||||||
|
}
|
||||||
pub fn list_folders(&self) -> Vec<Folder> {
|
pub fn list_folders(&self) -> Vec<Folder> {
|
||||||
let mut folders = self.backend.folders();
|
let mut folders = self.backend.folders();
|
||||||
if let Some(folder_renames) = self.settings.conf().folders() {
|
if let Some(folder_renames) = self.settings.conf().folders() {
|
||||||
|
@ -109,8 +116,13 @@ impl Account {
|
||||||
pub fn workers(&mut self) -> &mut Vec<Worker> {
|
pub fn workers(&mut self) -> &mut Vec<Worker> {
|
||||||
&mut self.workers
|
&mut self.workers
|
||||||
}
|
}
|
||||||
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> Option<Option<NewMailEvent>> {
|
|
||||||
let mut ret: Option<Option<NewMailEvent>> = None;
|
fn load_mailbox(
|
||||||
|
&mut self,
|
||||||
|
index: usize,
|
||||||
|
envelopes: Result<Vec<Envelope>>,
|
||||||
|
) -> LoadMailboxResult {
|
||||||
|
let mut ret: LoadMailboxResult = LoadMailboxResult::Refresh;
|
||||||
|
|
||||||
// TODO: Cleanup this function
|
// TODO: Cleanup this function
|
||||||
let folders = self.backend.folders();
|
let folders = self.backend.folders();
|
||||||
|
@ -118,22 +130,50 @@ impl Account {
|
||||||
if self.sent_folder.is_some() {
|
if self.sent_folder.is_some() {
|
||||||
let id = self.sent_folder.unwrap();
|
let id = self.sent_folder.unwrap();
|
||||||
if id == index {
|
if id == index {
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
let old_m = mem::replace(&mut self.folders[index], Some(Mailbox::new(folder, &None, envelopes)));
|
let old_m = mem::replace(
|
||||||
|
&mut self.folders[index],
|
||||||
|
Some(Mailbox::new(folder, &None, envelopes)),
|
||||||
|
);
|
||||||
if let Some(old_m) = old_m {
|
if let Some(old_m) = old_m {
|
||||||
if self.folders[index].is_some() && old_m.is_ok() {
|
if self.folders[index].is_some() && old_m.is_ok() {
|
||||||
let diff = self.folders[index].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0));
|
let diff = self.folders[index]
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| v.as_ref().unwrap().collection.len())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.saturating_sub(
|
||||||
|
old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0),
|
||||||
|
);
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&self.folders[index].as_ref().unwrap().as_ref().unwrap().collection).count();
|
let mut index = old_m
|
||||||
ret = Some(Some(NewMailEvent {
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.iter()
|
||||||
|
.zip(
|
||||||
|
&self.folders[index]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection,
|
||||||
|
)
|
||||||
|
.count();
|
||||||
|
ret = LoadMailboxResult::New(NewMailEvent {
|
||||||
folder: folder.hash(),
|
folder: folder.hash(),
|
||||||
index: (index.saturating_sub(1)..(self.folders[index].as_ref().unwrap().as_ref().unwrap().collection.len().saturating_sub(1))).collect(),
|
index: (index.saturating_sub(1)
|
||||||
}));
|
..(self.folders[index]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.len()
|
||||||
|
.saturating_sub(1)))
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ret = Some(None);
|
|
||||||
}
|
}
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,54 +191,98 @@ impl Account {
|
||||||
if sent[0].is_none() {
|
if sent[0].is_none() {
|
||||||
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
|
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
|
||||||
}
|
}
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
let old_m = mem::replace(&mut cur[0], Some(Mailbox::new(folder, &sent[0], envelopes)));
|
let old_m =
|
||||||
|
mem::replace(&mut cur[0], Some(Mailbox::new(folder, &sent[0], envelopes)));
|
||||||
if let Some(old_m) = old_m {
|
if let Some(old_m) = old_m {
|
||||||
if cur[0].is_some() && old_m.is_ok() {
|
if cur[0].is_some() && old_m.is_ok() {
|
||||||
let diff = cur[0].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0));
|
let diff = cur[0]
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| v.as_ref().unwrap().collection.len())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.saturating_sub(
|
||||||
|
old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0),
|
||||||
|
);
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&cur[0].as_ref().unwrap().as_ref().unwrap().collection).count();
|
let mut index = old_m
|
||||||
ret = Some(Some(NewMailEvent {
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.iter()
|
||||||
|
.zip(&cur[0].as_ref().unwrap().as_ref().unwrap().collection)
|
||||||
|
.count();
|
||||||
|
ret = LoadMailboxResult::New(NewMailEvent {
|
||||||
folder: folder.hash(),
|
folder: folder.hash(),
|
||||||
index: (index.saturating_sub(1)..(cur[0].as_ref().unwrap().as_ref().unwrap().collection.len()).saturating_sub(1)).collect(),
|
index: (index.saturating_sub(1)
|
||||||
}));
|
..(cur[0]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.len())
|
||||||
|
.saturating_sub(1))
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ret = Some(None);
|
|
||||||
}
|
}
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
let old_m = mem::replace(&mut self.folders[index], Some(Mailbox::new(folder, &None, envelopes)));
|
let old_m = mem::replace(
|
||||||
if let Some(old_m) = old_m {
|
&mut self.folders[index],
|
||||||
if self.folders[index].is_some() && old_m.is_ok() {
|
Some(Mailbox::new(folder, &None, envelopes)),
|
||||||
let diff = self.folders[index].as_ref().map(|v| v.as_ref().unwrap().collection.len()).unwrap_or(0).saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0));
|
);
|
||||||
if diff > 0 {
|
if let Some(old_m) = old_m {
|
||||||
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&self.folders[index].as_ref().unwrap().as_ref().unwrap().collection).count();
|
if self.folders[index].is_some() && old_m.is_ok() {
|
||||||
ret = Some(Some(NewMailEvent {
|
let diff = self.folders[index]
|
||||||
folder: folder.hash(),
|
.as_ref()
|
||||||
index: (index.saturating_sub(1)..(self.folders[index].as_ref().unwrap().as_ref().unwrap().collection.len().saturating_sub(1))).collect(),
|
.map(|v| v.as_ref().unwrap().collection.len())
|
||||||
}));
|
.unwrap_or(0)
|
||||||
|
.saturating_sub(old_m.as_ref().map(|v| v.collection.len()).unwrap_or(0));
|
||||||
}
|
if diff > 0 {
|
||||||
|
let mut index = old_m
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.iter()
|
||||||
|
.zip(
|
||||||
|
&self.folders[index]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection,
|
||||||
|
)
|
||||||
|
.count();
|
||||||
|
ret = LoadMailboxResult::New(NewMailEvent {
|
||||||
|
folder: folder.hash(),
|
||||||
|
index: (index.saturating_sub(1)
|
||||||
|
..(self.folders[index]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.collection
|
||||||
|
.len()
|
||||||
|
.saturating_sub(1)))
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ret = Some(None);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* ======================== */
|
/* ======================== */
|
||||||
};
|
};
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&mut self, index: usize) -> result::Result<Option<Option<NewMailEvent>>, usize> {
|
pub fn status(&mut self, index: usize) -> result::Result<LoadMailboxResult, usize> {
|
||||||
match self.workers[index].as_mut() {
|
match self.workers[index].as_mut() {
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(LoadMailboxResult::Loaded);
|
||||||
}
|
}
|
||||||
Some(ref mut w) => match w.poll() {
|
Some(ref mut w) => match w.poll() {
|
||||||
Ok(AsyncStatus::NoUpdate) => {
|
Ok(AsyncStatus::NoUpdate) => {
|
||||||
|
|
|
@ -106,13 +106,22 @@ pub struct CellBuffer {
|
||||||
|
|
||||||
impl fmt::Debug for CellBuffer {
|
impl fmt::Debug for CellBuffer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "CellBuffer {{ cols: {}, rows: {}, buf: {} cells", self.cols, self.rows, self.buf.len())
|
write!(
|
||||||
|
f,
|
||||||
|
"CellBuffer {{ cols: {}, rows: {}, buf: {} cells",
|
||||||
|
self.cols,
|
||||||
|
self.rows,
|
||||||
|
self.buf.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CellBuffer {
|
impl CellBuffer {
|
||||||
pub fn area(&self) -> Area {
|
pub fn area(&self) -> Area {
|
||||||
((0, 0), (self.cols.saturating_sub(1), self.rows.saturating_sub(1)))
|
(
|
||||||
|
(0, 0),
|
||||||
|
(self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn set_cols(&mut self, new_cols: usize) {
|
pub fn set_cols(&mut self, new_cols: usize) {
|
||||||
self.cols = new_cols;
|
self.cols = new_cols;
|
||||||
|
@ -216,13 +225,13 @@ impl Default for CellBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a String> for CellBuffer {
|
impl<'a> From<&'a str> for CellBuffer {
|
||||||
fn from(s: &'a String) -> Self {
|
fn from(s: &'a str) -> Self {
|
||||||
let lines: Vec<&str> = s.lines().map(|l| l.trim_right()).collect();
|
let lines: Vec<&str> = s.lines().map(|l| l.trim_right()).collect();
|
||||||
let len = s.len() + lines.len();
|
let len = s.len() + lines.len();
|
||||||
let mut buf = CellBuffer::new(len, 1, Cell::default());
|
let mut buf = CellBuffer::new(len, 1, Cell::default());
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
for l in lines.iter() {
|
for l in &lines {
|
||||||
for (idx, c) in l.chars().enumerate() {
|
for (idx, c) in l.chars().enumerate() {
|
||||||
buf[(x + idx, 0)].set_ch(c);
|
buf[(x + idx, 0)].set_ch(c);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
use std;
|
use std;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Write, Read};
|
use std::io::{Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -52,10 +52,11 @@ impl File {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
pub fn read_to_string(&self) -> String {
|
pub fn read_to_string(&self) -> String {
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut f = fs::File::open(&self.path).expect(&format!("Can't open {}", &self.path.display()));
|
let mut f = fs::File::open(&self.path)
|
||||||
f.read_to_end(&mut buf).expect(&format!("Can't read {}", &self.path.display()));
|
.unwrap_or_else(|_| panic!("Can't open {}", &self.path.display()));
|
||||||
|
f.read_to_end(&mut buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Can't read {}", &self.path.display()));
|
||||||
String::from_utf8(buf).unwrap()
|
String::from_utf8(buf).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod accounts;
|
pub mod accounts;
|
||||||
pub use self::accounts::Account;
|
pub use self::accounts::{Account, LoadMailboxResult};
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod position;
|
mod position;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -87,7 +87,7 @@ macro_rules! height {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! width{
|
macro_rules! width {
|
||||||
($a:expr) => {
|
($a:expr) => {
|
||||||
(get_x(bottom_right!($a))).saturating_sub(get_x(upper_left!($a)))
|
(get_x(bottom_right!($a))).saturating_sub(get_x(upper_left!($a)))
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue