Detect new mail events and pass them as notifications to State

concerns #26
embed
Manos Pitsidianakis 2018-08-16 21:20:53 +03:00
parent b2c7430907
commit 710920c67b
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 138 additions and 58 deletions

View File

@ -29,6 +29,12 @@ use mailbox::backends::{Backends, RefreshEventConsumer};
use mailbox::*;
use std::ops::{Index, IndexMut};
use std::result;
use std::mem;
pub struct NewMailEvent {
pub folder: u64,
pub index: Vec<usize>,
}
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
@ -93,13 +99,32 @@ impl Account {
pub fn workers(&mut self) -> &mut Vec<Worker> {
&mut self.workers
}
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> () {
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> Option<NewMailEvent> {
let mut ret: Option<NewMailEvent> = None;
// TODO: Cleanup this function
let folders = self.backend.folders();
let folder = &folders[index];
if self.sent_folder.is_some() {
let id = self.sent_folder.unwrap();
if id == index {
self.folders[index] = Some(Mailbox::new(folder, &None, envelopes));
/* ======================== */
let old_m = mem::replace(&mut self.folders[index], Some(Mailbox::new(folder, &None, envelopes)));
if let Some(old_m) = old_m {
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));
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 = Some(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 {
let (sent, cur) = {
let ptr = self.folders.as_mut_ptr();
@ -115,17 +140,51 @@ impl Account {
if sent[0].is_none() {
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
}
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 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));
if diff > 0 {
let mut index = old_m.as_ref().unwrap().collection.iter().zip(&cur[0].as_ref().unwrap().as_ref().unwrap().collection).count();
ret = Some(NewMailEvent {
folder: folder.hash(),
index: (index.saturating_sub(1)..(cur[0].as_ref().unwrap().as_ref().unwrap().collection.len()).saturating_sub(1)).collect(),
});
}
}
}
/* ======================== */
}
} else {
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 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));
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 = Some(NewMailEvent {
folder: folder.hash(),
index: (index.saturating_sub(1)..(self.folders[index].as_ref().unwrap().as_ref().unwrap().collection.len().saturating_sub(1))).collect(),
});
}
}
}
/* ======================== */
};
ret
}
pub fn status(&mut self, index: usize) -> result::Result<bool, usize> {
pub fn status(&mut self, index: usize) -> result::Result<Option<NewMailEvent>, usize> {
match self.workers[index].as_mut() {
None => {
return Ok(false);
return Ok(None);
}
Some(ref mut w) => match w.poll() {
Ok(AsyncStatus::NoUpdate) => {
@ -142,9 +201,8 @@ impl Account {
},
};
let m = self.workers[index].take().unwrap().extract();
self.load_mailbox(index, m);
self.workers[index] = None;
Ok(true)
Ok(self.load_mailbox(index, m))
}
}

View File

@ -49,7 +49,7 @@ use std::fs;
use std::io;
use std::io::Read;
use std::sync::{Mutex, Arc};
use std::hash::Hasher;
use std::hash::{Hasher, Hash};
use std::path::{Path, PathBuf};
extern crate fnv;
use self::fnv::FnvHashMap;
@ -213,21 +213,20 @@ impl MailBackend for MaildirType {
Ok(event) => match event {
DebouncedEvent::Create(mut pathbuf)
| DebouncedEvent::Remove(mut pathbuf) => {
let path = if pathbuf.is_dir() {
if pathbuf.is_dir() {
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
pathbuf.pop();
}
pathbuf.to_str().unwrap()
} else {
pathbuf.pop();
pathbuf.parent().unwrap().to_str().unwrap()
pathbuf.pop();
};
eprintln!(" got event in {}", path);
eprintln!(" got event in {}", pathbuf.display());
let mut hasher = DefaultHasher::new();
hasher.write(path.as_bytes());
pathbuf.hash(&mut hasher);
sender.send(RefreshEvent {
folder: format!("{}", path),
folder: format!("{}", pathbuf.display()),
hash: hasher.finish(),
});
}
@ -306,16 +305,15 @@ impl MaildirType {
let tx = w.tx();
// TODO: Avoid clone
let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)];
let path = folder.path().to_string();
let mut path: PathBuf = folder.path().into();
let name = format!("parsing {:?}", folder.name());
let map = self.hash_index.clone();
let map2 = self.hash_index.clone();
thread::Builder::new()
.name(name)
.name(name.clone())
.spawn(move || {
let cache_dir = cache_dir.clone();
let mut path = PathBuf::from(path);
path.push("cur");
let iter = path.read_dir()?;
let count = path.read_dir()?.count();
@ -341,7 +339,7 @@ impl MaildirType {
let cache_dir = cache_dir.clone();
let mut tx = tx.clone();
let map = map.clone();
let s = scope.spawn(move || {
let s = scope.builder().name(name.clone()).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());
@ -419,7 +417,7 @@ impl MaildirType {
}
local_r
});
threads.push(s);
threads.push(s.unwrap());
}
});
}
@ -447,26 +445,27 @@ impl MaildirType {
pub struct MaildirFolder {
hash: u64,
name: String,
path: String,
path: PathBuf,
children: Vec<usize>,
}
impl MaildirFolder {
pub fn new(path: String, file_name: String, children: Vec<usize>) -> Result<Self> {
let pathbuf = PathBuf::from(path);
let mut h = DefaultHasher::new();
h.write(&path.as_bytes());
pathbuf.hash(&mut h);
let ret = MaildirFolder {
hash: h.finish(),
name: file_name,
path: path,
path: pathbuf,
children: children,
};
ret.is_valid()?;
Ok(ret)
}
pub fn path(&self) -> &str {
&self.path
pub fn path(&self) -> &Path {
self.path.as_path()
}
fn is_valid(&self) -> Result<()> {
let path = self.path();
@ -476,7 +475,7 @@ impl MaildirFolder {
if !p.is_dir() {
return Err(MeliError::new(format!(
"{} is not a valid maildir folder",
path
path.display()
)));
}
p.pop();

View File

@ -66,7 +66,7 @@ fn main() {
let menu = Entity {
component: Box::new(AccountMenu::new(&state.context.accounts)),
};
let listing = CompactListing::new();
let listing = MailListing::new();
let b = Entity {
component: Box::new(listing),
};
@ -150,7 +150,6 @@ fn main() {
},
ThreadEvent::RefreshMailbox { hash : h } => {
state.hash_to_folder(h);
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(String::from("Update in mailbox"))});
state.redraw();
},
ThreadEvent::UIEvent(UIEventType::ChangeMode(f)) => {
@ -160,10 +159,10 @@ fn main() {
ThreadEvent::UIEvent(UIEventType::StartupCheck) => {
let mut flag = false;
let mut render_flag = false;
for account in &mut state.context.accounts {
let len = account.len();
for i in 0..len {
match account.status(i) {
for idx_a in 0..state.context.accounts.len() {
let len = state.context.accounts[idx_a].len();
for idx_m in 0..len {
match state.context.account_status(idx_a, idx_m) {
Ok(true) => {
render_flag = true;
},

View File

@ -33,6 +33,7 @@ use chan::{Receiver, Sender};
use fnv::FnvHashMap;
use std::io::Write;
use std::thread;
use std::result;
use std::time;
use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
@ -97,6 +98,24 @@ impl Context {
pub fn restore_input(&self) {
self.input.restore(self.sender.clone());
}
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)?;
if let Some(event) = s {
eprintln!("setting up notification");
let (idx_a, idx_m) = self.mailbox_hashes[&event.folder];
let subjects = {
let mut ret = Vec::with_capacity(event.index.len());
eprintln!("index is {:?}", &event.index);
for &i in &event.index {
ret.push(self.accounts[idx_a][idx_m].as_ref().unwrap().collection[i].subject());
}
ret
};
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)) });
return Ok(true);
}
Ok(false)
}
}
/// A State object to manage and own components and entities of the UI. `State` is responsible for
@ -224,8 +243,10 @@ impl State<std::io::Stdout> {
cursor::Goto(1, 1)
).unwrap();
s.flush();
eprintln!("DEBUG: inserting mailbox hashes:");
for (x, account) in s.context.accounts.iter_mut().enumerate() {
for (y, folder) in account.backend.folders().iter().enumerate() {
eprintln!("{:?}", folder);
s.context.mailbox_hashes.insert(folder.hash(), (x, y));
}
let sender = s.context.sender.clone();
@ -242,35 +263,38 @@ impl State<std::io::Stdout> {
* and startup a thread to remind us to poll it every now and then till it's finished.
*/
pub fn hash_to_folder(&mut self, hash: u64) {
let (idxa, idxm) = self.context.mailbox_hashes[&hash];
self.context.accounts[idxa].reload(idxm);
let (startup_tx, startup_rx) = chan::async();
let startup_thread = {
let sender = self.context.sender.clone();
let startup_rx = startup_rx.clone();
if let Some(&(idxa, idxm)) = self.context.mailbox_hashes.get(&hash) {
self.context.accounts[idxa].reload(idxm);
let (startup_tx, startup_rx) = chan::async();
let startup_thread = {
let sender = self.context.sender.clone();
let startup_rx = startup_rx.clone();
thread::Builder::new()
.name("startup-thread".to_string())
.spawn(move || {
let dur = time::Duration::from_millis(100);
loop {
chan_select! {
default => {},
startup_rx.recv() -> _ => {
sender.send(ThreadEvent::UIEvent(UIEventType::MailboxUpdate((idxa,idxm))));
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
return;
thread::Builder::new()
.name("startup-thread".to_string())
.spawn(move || {
let dur = time::Duration::from_millis(100);
loop {
chan_select! {
default => {},
startup_rx.recv() -> _ => {
sender.send(ThreadEvent::UIEvent(UIEventType::MailboxUpdate((idxa,idxm))));
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
return;
}
}
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
thread::sleep(dur);
}
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
thread::sleep(dur);
}
})
.unwrap()
};
self.startup_thread = Some(startup_tx.clone());
self.threads
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
})
.expect("Failed to spawn startup-thread in hash_to_folder()")
};
self.startup_thread = Some(startup_tx.clone());
self.threads
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
} else {
eprintln!("BUG: mailbox with hash {} not found in mailbox_hashes.", hash);
}
}
/// If an owned thread returns a `ThreadEvent::ThreadJoin` event to `State` then it must remove