Async loading of mailboxes on startup
parent
4e5721563e
commit
b21d30c2ef
|
@ -20,3 +20,4 @@ data-encoding = "2.1.1"
|
||||||
encoding = "0.2.33"
|
encoding = "0.2.33"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
termion = "1.5.1"
|
termion = "1.5.1"
|
||||||
|
chan = "0.1.21"
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
use chan;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AsyncStatus {
|
||||||
|
NoUpdate,
|
||||||
|
Finished,
|
||||||
|
ProgressReport(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AsyncBuilder {
|
||||||
|
tx: chan::Sender<AsyncStatus>,
|
||||||
|
rx: chan::Receiver<AsyncStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Async<T> {
|
||||||
|
value: Option<T>,
|
||||||
|
worker: Option<thread::JoinHandle<T>>,
|
||||||
|
tx: chan::Sender<AsyncStatus>,
|
||||||
|
rx: chan::Receiver<AsyncStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl AsyncBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (sender, receiver) = chan::sync(::std::mem::size_of::<AsyncStatus>());
|
||||||
|
AsyncBuilder {
|
||||||
|
tx: sender,
|
||||||
|
rx: receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn tx(&mut self) -> chan::Sender<AsyncStatus> {
|
||||||
|
self.tx.clone()
|
||||||
|
}
|
||||||
|
pub fn rx(&mut self) -> chan::Receiver<AsyncStatus> {
|
||||||
|
self.rx.clone()
|
||||||
|
}
|
||||||
|
pub fn build<T: Clone>(self, worker: thread::JoinHandle<T>) -> Async<T> {
|
||||||
|
Async {
|
||||||
|
worker: Some(worker),
|
||||||
|
value: None,
|
||||||
|
tx: self.tx,
|
||||||
|
rx: self.rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Async<T> {
|
||||||
|
pub fn extract(self) -> T {
|
||||||
|
self.value.unwrap()
|
||||||
|
}
|
||||||
|
pub fn poll(&mut self) -> Result<AsyncStatus, ()> {
|
||||||
|
if self.value.is_some() {
|
||||||
|
return Ok(AsyncStatus::Finished);
|
||||||
|
}
|
||||||
|
//self.tx.send(true);
|
||||||
|
let rx = &self.rx;
|
||||||
|
chan_select! {
|
||||||
|
default => {
|
||||||
|
return Ok(AsyncStatus::NoUpdate);
|
||||||
|
},
|
||||||
|
rx.recv() -> r => {
|
||||||
|
match r {
|
||||||
|
Some(AsyncStatus::Finished) => {
|
||||||
|
},
|
||||||
|
Some(a) => {
|
||||||
|
eprintln!("async got {:?}", a);
|
||||||
|
return Ok(a);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
let v = self.worker.take().unwrap().join().unwrap();
|
||||||
|
self.value = Some(v);
|
||||||
|
eprintln!("worker joined");
|
||||||
|
return Ok(AsyncStatus::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -111,7 +111,8 @@ impl FileSettings {
|
||||||
let mut s = Config::new();
|
let mut s = Config::new();
|
||||||
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
|
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
|
||||||
|
|
||||||
// TODO: Return result
|
// No point in returning without a config file.
|
||||||
|
// TODO: Error and exit instead of panic.
|
||||||
s.unwrap().deserialize().unwrap()
|
s.unwrap().deserialize().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
pub mod conf;
|
pub mod conf;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod mailbox;
|
pub mod mailbox;
|
||||||
|
pub mod utilities;
|
||||||
|
pub mod async;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
@ -31,6 +33,8 @@ extern crate chrono;
|
||||||
extern crate data_encoding;
|
extern crate data_encoding;
|
||||||
extern crate encoding;
|
extern crate encoding;
|
||||||
extern crate memmap;
|
extern crate memmap;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate chan;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
@ -41,3 +45,4 @@ pub use mailbox::*;
|
||||||
pub use error::{MeliError, Result};
|
pub use error::{MeliError, Result};
|
||||||
pub use mailbox::backends::{Backends, RefreshEvent, RefreshEventConsumer};
|
pub use mailbox::backends::{Backends, RefreshEvent, RefreshEventConsumer};
|
||||||
pub use mailbox::email::{Envelope, Flag};
|
pub use mailbox::email::{Envelope, Flag};
|
||||||
|
pub use utilities::*;
|
||||||
|
|
|
@ -22,13 +22,19 @@
|
||||||
use conf::{AccountSettings, Folder};
|
use conf::{AccountSettings, Folder};
|
||||||
use mailbox::backends::{Backends, RefreshEventConsumer};
|
use mailbox::backends::{Backends, RefreshEventConsumer};
|
||||||
use mailbox::*;
|
use mailbox::*;
|
||||||
|
use async::*;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
use std::result;
|
||||||
|
|
||||||
|
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
name: String,
|
name: String,
|
||||||
folders: Vec<Option<Result<Mailbox>>>,
|
folders: Vec<Option<Result<Mailbox>>>,
|
||||||
|
|
||||||
|
workers: Vec<Worker>,
|
||||||
|
|
||||||
sent_folder: Option<usize>,
|
sent_folder: Option<usize>,
|
||||||
|
|
||||||
pub settings: AccountSettings,
|
pub settings: AccountSettings,
|
||||||
|
@ -43,13 +49,17 @@ impl Account {
|
||||||
.iter()
|
.iter()
|
||||||
.position(|x| *x.path() == settings.sent_folder);
|
.position(|x| *x.path() == settings.sent_folder);
|
||||||
let mut folders = Vec::with_capacity(settings.folders.len());
|
let mut folders = Vec::with_capacity(settings.folders.len());
|
||||||
for _ in 0..settings.folders.len() {
|
let mut workers = Vec::new();
|
||||||
folders.push(None);
|
|
||||||
}
|
|
||||||
let backend = backends.get(settings.format());
|
let backend = backends.get(settings.format());
|
||||||
|
for f in &settings.folders {
|
||||||
|
folders.push(None);
|
||||||
|
let mut handle = backend.get(&f);
|
||||||
|
workers.push(Some(handle));
|
||||||
|
}
|
||||||
Account {
|
Account {
|
||||||
name: name,
|
name: name,
|
||||||
folders: folders,
|
folders: folders,
|
||||||
|
workers: workers,
|
||||||
|
|
||||||
sent_folder: sent_folder,
|
sent_folder: sent_folder,
|
||||||
|
|
||||||
|
@ -71,24 +81,16 @@ impl Account {
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
}
|
pub fn workers(&mut self) -> &mut Vec<Worker> {
|
||||||
|
&mut self.workers
|
||||||
impl Index<usize> for Account {
|
|
||||||
type Output = Option<Result<Mailbox>>;
|
|
||||||
fn index(&self, index: usize) -> &Option<Result<Mailbox>> {
|
|
||||||
&self.folders[index]
|
|
||||||
}
|
}
|
||||||
}
|
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> () {
|
||||||
|
|
||||||
impl IndexMut<usize> for Account {
|
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Option<Result<Mailbox>> {
|
|
||||||
if self.folders[index].is_none() {
|
|
||||||
let folder = &self.settings.folders[index];
|
let folder = &self.settings.folders[index];
|
||||||
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 {
|
||||||
self.folders[index] =
|
self.folders[index] =
|
||||||
Some(Mailbox::new(folder, &None, self.backend.get(&folder)));
|
Some(Mailbox::new(folder, &None, envelopes));
|
||||||
} else {
|
} else {
|
||||||
let (sent, cur) = {
|
let (sent, cur) = {
|
||||||
let ptr = self.folders.as_mut_ptr();
|
let ptr = self.folders.as_mut_ptr();
|
||||||
|
@ -102,14 +104,54 @@ impl IndexMut<usize> for Account {
|
||||||
};
|
};
|
||||||
let sent_path = &self.settings.folders[id];
|
let sent_path = &self.settings.folders[id];
|
||||||
if sent[0].is_none() {
|
if sent[0].is_none() {
|
||||||
sent[0] = Some(Mailbox::new(sent_path, &None, self.backend.get(&folder)));
|
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
|
||||||
}
|
}
|
||||||
cur[0] = Some(Mailbox::new(folder, &sent[0], self.backend.get(folder)));
|
cur[0] = Some(Mailbox::new(folder, &sent[0], envelopes));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.folders[index] = Some(Mailbox::new(folder, &None, self.backend.get(&folder)));
|
self.folders[index] = Some(Mailbox::new(folder, &None, envelopes));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
&mut self.folders[index]
|
|
||||||
|
pub fn status(&mut self, index: usize) -> result::Result<(), usize> {
|
||||||
|
match self.workers[index].as_mut() {
|
||||||
|
None => { return Ok(()); },
|
||||||
|
Some(ref mut w) => {
|
||||||
|
match w.poll() {
|
||||||
|
Ok(AsyncStatus::NoUpdate) => {
|
||||||
|
return Err(0);
|
||||||
|
},
|
||||||
|
Ok(AsyncStatus::Finished) => {
|
||||||
|
},
|
||||||
|
Ok(AsyncStatus::ProgressReport(n)) => {
|
||||||
|
return Err(n);
|
||||||
|
},
|
||||||
|
a => {
|
||||||
|
eprintln!("{:?}", a);
|
||||||
|
return Err(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let m = self.workers[index].take().unwrap().extract();
|
||||||
|
self.load_mailbox(index, m);
|
||||||
|
self.workers[index] = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Account {
|
||||||
|
type Output = Result<Mailbox>;
|
||||||
|
fn index(&self, index: usize) -> &Result<Mailbox> {
|
||||||
|
&self.folders[index].as_ref().expect("BUG: Requested mailbox that is not yet available.")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will panic if mailbox hasn't loaded, ask `status()` first.
|
||||||
|
impl IndexMut<usize> for Account {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Result<Mailbox> {
|
||||||
|
self.folders[index].as_mut().expect("BUG: Requested mailbox that is not yet available.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
use conf::Folder;
|
use conf::Folder;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
use async::*;
|
||||||
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
||||||
use mailbox::email::{Envelope, Flag};
|
use mailbox::email::{Envelope, Flag};
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ impl BackendOp for ImapOp {
|
||||||
pub struct ImapType {}
|
pub struct ImapType {}
|
||||||
|
|
||||||
impl MailBackend for ImapType {
|
impl MailBackend for ImapType {
|
||||||
fn get(&self, _folder: &Folder) -> Result<Vec<Envelope>> {
|
fn get(&self, _folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use async::*;
|
||||||
use conf::Folder;
|
use conf::Folder;
|
||||||
use error::{MeliError, Result};
|
use error::{MeliError, Result};
|
||||||
use mailbox::backends::{
|
use mailbox::backends::{
|
||||||
|
@ -40,6 +41,8 @@ use std::thread;
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
/// `BackendOp` implementor for Maildir
|
/// `BackendOp` implementor for Maildir
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -116,10 +119,11 @@ impl BackendOp for MaildirOp {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MaildirType {
|
pub struct MaildirType {
|
||||||
path: String,
|
path: String,
|
||||||
|
idx: (usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailBackend for MaildirType {
|
impl MailBackend for MaildirType {
|
||||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
|
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||||
self.multicore(4, folder)
|
self.multicore(4, folder)
|
||||||
}
|
}
|
||||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||||
|
@ -145,11 +149,15 @@ impl MailBackend for MaildirType {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(event) => match event {
|
Ok(event) => match event {
|
||||||
DebouncedEvent::Create(pathbuf) => {
|
DebouncedEvent::Create(pathbuf) => {
|
||||||
|
let path = pathbuf.parent().unwrap().to_str().unwrap();
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
hasher.write(path.as_bytes());
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
folder: format!(
|
folder: format!(
|
||||||
"{}",
|
"{}", path
|
||||||
pathbuf.parent().unwrap().to_str().unwrap()
|
|
||||||
),
|
),
|
||||||
|
hash: hasher.finish(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -163,9 +171,10 @@ impl MailBackend for MaildirType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaildirType {
|
impl MaildirType {
|
||||||
pub fn new(path: &str) -> Self {
|
pub fn new(path: &str, idx: (usize, usize)) -> Self {
|
||||||
MaildirType {
|
MaildirType {
|
||||||
path: path.to_string(),
|
path: path.to_string(),
|
||||||
|
idx: idx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn is_valid(f: &Folder) -> Result<()> {
|
fn is_valid(f: &Folder) -> Result<()> {
|
||||||
|
@ -183,8 +192,19 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn multicore(&self, cores: usize, folder: &Folder) -> Result<Vec<Envelope>> {
|
pub fn multicore(&self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||||
MaildirType::is_valid(folder)?;
|
|
||||||
|
let mut w = AsyncBuilder::new();
|
||||||
|
let handle = {
|
||||||
|
let tx = w.tx();
|
||||||
|
// TODO: Avoid clone
|
||||||
|
let folder = folder.clone();
|
||||||
|
|
||||||
|
|
||||||
|
thread::Builder::new()
|
||||||
|
.name(format!("parsing {:?}", folder))
|
||||||
|
.spawn(move || {
|
||||||
|
MaildirType::is_valid(&folder)?;
|
||||||
let path = folder.path();
|
let path = folder.path();
|
||||||
let mut path = PathBuf::from(path);
|
let mut path = PathBuf::from(path);
|
||||||
path.push("cur");
|
path.push("cur");
|
||||||
|
@ -208,19 +228,26 @@ impl MaildirType {
|
||||||
count
|
count
|
||||||
};
|
};
|
||||||
for chunk in files.chunks(chunk_size) {
|
for chunk in files.chunks(chunk_size) {
|
||||||
|
let mut tx = tx.clone();
|
||||||
let s = scope.spawn(move || {
|
let s = scope.spawn(move || {
|
||||||
|
let len = chunk.len();
|
||||||
|
let size = if len <= 100 { 100 } else { (len / 100) * 100};
|
||||||
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
||||||
for e in chunk {
|
for c in chunk.chunks(size) {
|
||||||
let e_copy = e.to_string();
|
let len = c.len();
|
||||||
if let Some(mut e) =
|
for e in c {
|
||||||
Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new(
|
let e_copy = e.to_string();
|
||||||
move || Box::new(MaildirOp::new(e_copy.clone())),
|
if let Some(mut e) =
|
||||||
)))) {
|
Envelope::from_token(Box::new(BackendOpGenerator::new(Box::new(
|
||||||
if e.populate_headers().is_err() {
|
move || Box::new(MaildirOp::new(e_copy.clone())),
|
||||||
continue;
|
)))) {
|
||||||
}
|
if e.populate_headers().is_err() {
|
||||||
local_r.push(e);
|
continue;
|
||||||
|
}
|
||||||
|
local_r.push(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
tx.send(AsyncStatus::ProgressReport(len));
|
||||||
}
|
}
|
||||||
local_r
|
local_r
|
||||||
});
|
});
|
||||||
|
@ -232,6 +259,10 @@ impl MaildirType {
|
||||||
let mut result = t.join();
|
let mut result = t.join();
|
||||||
r.append(&mut result);
|
r.append(&mut result);
|
||||||
}
|
}
|
||||||
|
tx.send(AsyncStatus::Finished);
|
||||||
Ok(r)
|
Ok(r)
|
||||||
|
}).unwrap()
|
||||||
|
};
|
||||||
|
w.build(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
use conf::Folder;
|
use conf::Folder;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
use async::*;
|
||||||
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
||||||
use mailbox::email::{Envelope, Flag};
|
use mailbox::email::{Envelope, Flag};
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ impl BackendOp for MboxOp {
|
||||||
pub struct MboxType {}
|
pub struct MboxType {}
|
||||||
|
|
||||||
impl MailBackend for MboxType {
|
impl MailBackend for MboxType {
|
||||||
fn get(&self, _folder: &Folder) -> Result<Vec<Envelope>> {
|
fn get(&self, _folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub mod mbox;
|
||||||
|
|
||||||
use conf::Folder;
|
use conf::Folder;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
use async::*;
|
||||||
use mailbox::backends::imap::ImapType;
|
use mailbox::backends::imap::ImapType;
|
||||||
use mailbox::backends::maildir::MaildirType;
|
use mailbox::backends::maildir::MaildirType;
|
||||||
use mailbox::backends::mbox::MboxType;
|
use mailbox::backends::mbox::MboxType;
|
||||||
|
@ -47,7 +48,7 @@ impl Backends {
|
||||||
};
|
};
|
||||||
b.register(
|
b.register(
|
||||||
"maildir".to_string(),
|
"maildir".to_string(),
|
||||||
Box::new(|| Box::new(MaildirType::new(""))),
|
Box::new(|| Box::new(MaildirType::new("", (0, 0)))),
|
||||||
);
|
);
|
||||||
b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
|
b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
|
||||||
b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
|
b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
|
||||||
|
@ -69,6 +70,7 @@ impl Backends {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RefreshEvent {
|
pub struct RefreshEvent {
|
||||||
|
pub hash: u64,
|
||||||
pub folder: String,
|
pub folder: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +89,7 @@ impl RefreshEventConsumer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait MailBackend: ::std::fmt::Debug {
|
pub trait MailBackend: ::std::fmt::Debug {
|
||||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>>;
|
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> ();
|
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> ();
|
||||||
//fn new(folders: &Vec<String>) -> Box<Self>;
|
//fn new(folders: &Vec<String>) -> Box<Self>;
|
||||||
//login function
|
//login function
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
pub trait ProgressTracker {
|
||||||
|
fn new(s: String, total_work: usize) -> Self;
|
||||||
|
|
||||||
|
fn add_work(&mut self, n: usize) -> ();
|
||||||
|
fn set_work(&mut self, n: usize) -> ();
|
||||||
|
fn work(&mut self, n: usize) -> ();
|
||||||
|
|
||||||
|
fn percentage(&self) -> usize;
|
||||||
|
fn description(&self) -> &str;
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ fn make_input_thread(
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
/* Lock all stdio outs */
|
/* Lock all stdio outs */
|
||||||
//let _stdout = stdout();
|
//let _stdout = stdout();
|
||||||
|
@ -182,8 +183,9 @@ fn main() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ThreadEvent::RefreshMailbox { name : n } => {
|
ThreadEvent::RefreshMailbox { hash : h } => {
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())});
|
eprintln!("got refresh mailbox hash {:x}", h);
|
||||||
|
//state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())});
|
||||||
state.redraw();
|
state.redraw();
|
||||||
/* Don't handle this yet. */
|
/* Don't handle this yet. */
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const MAX_COLS: usize = 500;
|
const MAX_COLS: usize = 500;
|
||||||
|
|
||||||
|
|
||||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||||
/// `Pager`.
|
/// `MailView`.
|
||||||
pub struct MailListing {
|
pub struct MailListing {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||||
cursor_pos: (usize, usize, usize),
|
cursor_pos: (usize, usize, usize),
|
||||||
|
@ -24,7 +25,7 @@ impl MailListing {
|
||||||
/* TODO: Make this configurable */
|
/* TODO: Make this configurable */
|
||||||
fn make_entry_string(e: &Envelope, idx: usize) -> String {
|
fn make_entry_string(e: &Envelope, idx: usize) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{} {} {:.85}",
|
"{} {} {}",
|
||||||
idx,
|
idx,
|
||||||
&e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
&e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||||
e.subject()
|
e.subject()
|
||||||
|
@ -55,17 +56,25 @@ impl MailListing {
|
||||||
let threaded = context.accounts[self.cursor_pos.0]
|
let threaded = context.accounts[self.cursor_pos.0]
|
||||||
.runtime_settings
|
.runtime_settings
|
||||||
.threaded;
|
.threaded;
|
||||||
// Get mailbox as a reference.
|
|
||||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap();
|
|
||||||
// Inform State that we changed the current folder view.
|
// Inform State that we changed the current folder view.
|
||||||
context.replies.push_back(UIEvent {
|
context.replies.push_back(UIEvent {
|
||||||
id: 0,
|
id: 0,
|
||||||
event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)),
|
event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)),
|
||||||
});
|
});
|
||||||
|
// Get mailbox as a reference.
|
||||||
|
//
|
||||||
|
loop {
|
||||||
|
eprintln!("loop round");
|
||||||
|
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
|
||||||
|
Ok(()) => { break; },
|
||||||
|
Err(a) => {
|
||||||
|
eprintln!("status returned {:?}", a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
self.length = if threaded {
|
self.length = if threaded {
|
||||||
mailbox.threaded_collection.len()
|
mailbox.threaded_collection.len()
|
||||||
|
@ -81,7 +90,7 @@ impl MailListing {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((0, 0), (MAX_COLS - 1, 0)),
|
((0, 0), (MAX_COLS - 1, 0)),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
self.content = content;
|
self.content = content;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +99,7 @@ impl MailListing {
|
||||||
if threaded {
|
if threaded {
|
||||||
let mut indentations: Vec<bool> = Vec::with_capacity(6);
|
let mut indentations: Vec<bool> = Vec::with_capacity(6);
|
||||||
let mut thread_idx = 0; // needed for alternate thread colors
|
let mut thread_idx = 0; // needed for alternate thread colors
|
||||||
/* Draw threaded view. */
|
/* Draw threaded view. */
|
||||||
let mut iter = mailbox.threaded_collection.iter().enumerate().peekable();
|
let mut iter = mailbox.threaded_collection.iter().enumerate().peekable();
|
||||||
let len = mailbox
|
let len = mailbox
|
||||||
.threaded_collection
|
.threaded_collection
|
||||||
|
@ -143,13 +152,13 @@ impl MailListing {
|
||||||
container,
|
container,
|
||||||
&indentations,
|
&indentations,
|
||||||
len,
|
len,
|
||||||
),
|
),
|
||||||
&mut content,
|
&mut content,
|
||||||
fg_color,
|
fg_color,
|
||||||
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 {
|
||||||
content[(x, idx)].set_ch(' ');
|
content[(x, idx)].set_ch(' ');
|
||||||
content[(x, idx)].set_bg(bg_color);
|
content[(x, idx)].set_bg(bg_color);
|
||||||
|
@ -199,7 +208,7 @@ impl MailListing {
|
||||||
bg_color,
|
bg_color,
|
||||||
((0, y), (MAX_COLS - 1, y)),
|
((0, y), (MAX_COLS - 1, y)),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
for x in x..MAX_COLS {
|
for x in x..MAX_COLS {
|
||||||
content[(x, y)].set_ch(' ');
|
content[(x, y)].set_ch(' ');
|
||||||
|
@ -217,8 +226,6 @@ impl MailListing {
|
||||||
.runtime_settings
|
.runtime_settings
|
||||||
.threaded;
|
.threaded;
|
||||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope: &Envelope = if threaded {
|
let envelope: &Envelope = if threaded {
|
||||||
|
@ -563,6 +570,12 @@ impl Component for MailListing {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.view = None;
|
self.view = None;
|
||||||
}
|
}
|
||||||
|
UIEventType::MailboxUpdate((ref idxa, ref idxf)) => {
|
||||||
|
if *idxa == self.new_cursor_pos.1 && *idxf == self.new_cursor_pos.0 {
|
||||||
|
self.refresh_mailbox(context);
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
UIEventType::ChangeMode(UIMode::Normal) => {
|
UIEventType::ChangeMode(UIMode::Normal) => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,6 @@ impl Component for MailView {
|
||||||
.runtime_settings
|
.runtime_settings
|
||||||
.threaded;
|
.threaded;
|
||||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope_idx: usize = if threaded {
|
let envelope_idx: usize = if threaded {
|
||||||
|
@ -157,8 +155,6 @@ impl Component for MailView {
|
||||||
let buf = {
|
let buf = {
|
||||||
let mailbox_idx = self.coordinates; // coordinates are mailbox idxs
|
let mailbox_idx = self.coordinates; // coordinates are mailbox idxs
|
||||||
let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1]
|
let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope: &Envelope = &mailbox.collection[envelope_idx];
|
let envelope: &Envelope = &mailbox.collection[envelope_idx];
|
||||||
|
@ -268,8 +264,6 @@ impl Component for MailView {
|
||||||
.runtime_settings
|
.runtime_settings
|
||||||
.threaded;
|
.threaded;
|
||||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope_idx: usize = if threaded {
|
let envelope_idx: usize = if threaded {
|
||||||
|
@ -339,8 +333,6 @@ impl Component for MailView {
|
||||||
.runtime_settings
|
.runtime_settings
|
||||||
.threaded;
|
.threaded;
|
||||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let envelope_idx: usize = if threaded {
|
let envelope_idx: usize = if threaded {
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub mod notifications;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
|
|
||||||
pub use mail::*;
|
pub use mail::*;
|
||||||
pub use utilities::*;
|
pub use self::utilities::*;
|
||||||
|
|
||||||
use super::cells::{CellBuffer, Color};
|
use super::cells::{CellBuffer, Color};
|
||||||
use super::position::Area;
|
use super::position::Area;
|
||||||
|
@ -138,6 +138,14 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area,
|
||||||
}
|
}
|
||||||
let mut src_x = get_x(upper_left!(src));
|
let mut src_x = get_x(upper_left!(src));
|
||||||
let mut src_y = get_y(upper_left!(src));
|
let mut src_y = get_y(upper_left!(src));
|
||||||
|
let (cols, rows) = grid_src.size();
|
||||||
|
if src_x >= cols || src_y >= rows {
|
||||||
|
eprintln!(
|
||||||
|
"DEBUG: src area outside of grid_src in copy_area",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||||
'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||||
|
|
|
@ -413,9 +413,13 @@ impl Component for StatusBar {
|
||||||
self.container.rcv_event(event, context);
|
self.container.rcv_event(event, context);
|
||||||
match &event.event_type {
|
match &event.event_type {
|
||||||
UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => {
|
UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => {
|
||||||
|
match context.accounts[*idx_a].status(*idx_f) {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(_) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let m = &context.accounts[*idx_a][*idx_f]
|
let m = &context.accounts[*idx_a][*idx_f]
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.status = format!(
|
self.status = format!(
|
||||||
|
@ -483,3 +487,47 @@ impl Component for TextBox {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Progress {
|
||||||
|
description: String,
|
||||||
|
total_work: usize,
|
||||||
|
finished: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Progress {
|
||||||
|
pub fn new(s: String, total_work: usize) -> Self {
|
||||||
|
Progress {
|
||||||
|
description: s,
|
||||||
|
total_work: total_work,
|
||||||
|
finished: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_work(&mut self, n: usize) -> () {
|
||||||
|
if self.finished >= self.total_work {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.finished += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn percentage(&self) -> usize {
|
||||||
|
if self.total_work > 0 {
|
||||||
|
100 * self.finished / self.total_work
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
&self.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Progress {
|
||||||
|
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ pub enum ThreadEvent {
|
||||||
Input(Key),
|
Input(Key),
|
||||||
/// A watched folder has been refreshed.
|
/// A watched folder has been refreshed.
|
||||||
RefreshMailbox {
|
RefreshMailbox {
|
||||||
name: String,
|
hash: u64,
|
||||||
},
|
},
|
||||||
UIEventType(UIEventType),
|
UIEventType(UIEventType),
|
||||||
//Decode { _ }, // For gpg2 signature check
|
//Decode { _ }, // For gpg2 signature check
|
||||||
|
@ -83,7 +83,7 @@ pub enum ThreadEvent {
|
||||||
|
|
||||||
impl From<RefreshEvent> for ThreadEvent {
|
impl From<RefreshEvent> for ThreadEvent {
|
||||||
fn from(event: RefreshEvent) -> Self {
|
fn from(event: RefreshEvent) -> Self {
|
||||||
ThreadEvent::RefreshMailbox { name: event.folder }
|
ThreadEvent::RefreshMailbox { hash: event.hash }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,7 @@ pub enum UIEventType {
|
||||||
EditDraft(File),
|
EditDraft(File),
|
||||||
Action(Action),
|
Action(Action),
|
||||||
StatusNotification(String),
|
StatusNotification(String),
|
||||||
|
MailboxUpdate((usize, usize)),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event passed from `State` to its Entities.
|
/// An event passed from `State` to its Entities.
|
||||||
|
@ -248,7 +249,7 @@ impl State<std::io::Stdout> {
|
||||||
cursor::Hide,
|
cursor::Hide,
|
||||||
clear::All,
|
clear::All,
|
||||||
cursor::Goto(1, 1)
|
cursor::Goto(1, 1)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
s.flush();
|
s.flush();
|
||||||
for account in &mut s.context.accounts {
|
for account in &mut s.context.accounts {
|
||||||
let sender = s.sender.clone();
|
let sender = s.sender.clone();
|
||||||
|
@ -455,15 +456,11 @@ impl<W: Write> State<W> {
|
||||||
/// Tries to load a mailbox's content
|
/// Tries to load a mailbox's content
|
||||||
pub fn refresh_mailbox(&mut self, account_idx: usize, folder_idx: usize) {
|
pub fn refresh_mailbox(&mut self, account_idx: usize, folder_idx: usize) {
|
||||||
let flag = match &mut self.context.accounts[account_idx][folder_idx] {
|
let flag = match &mut self.context.accounts[account_idx][folder_idx] {
|
||||||
Some(Ok(_)) => true,
|
Ok(_) => true,
|
||||||
Some(Err(e)) => {
|
Err(e) => {
|
||||||
eprintln!("error {:?}", e);
|
eprintln!("error {:?}", e);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
eprintln!("None");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if flag {
|
if flag {
|
||||||
self.rcv_event(UIEvent {
|
self.rcv_event(UIEvent {
|
||||||
|
|
Loading…
Reference in New Issue