melib,ui: add MailboxEntry enum

Use an enum to describe a mailbox's state in ui::conf::Account instead
of Result.
embed
Manos Pitsidianakis 2019-07-28 18:52:45 +03:00
parent 5b679be782
commit 8a0e702127
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
12 changed files with 176 additions and 144 deletions

View File

@ -106,13 +106,10 @@ impl Collection {
}
}
/* envelope is not in threads, so insert it */
let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope;
unsafe {
self.threads
.entry(folder_hash)
.or_default()
.insert(&mut (*env), &self.envelopes);
}
self.threads
.entry(folder_hash)
.or_default()
.insert(&mut self.envelopes, new_hash);
for (h, t) in self.threads.iter_mut() {
if *h == folder_hash {
continue;
@ -127,7 +124,7 @@ impl Collection {
&mut self,
mut envelopes: FnvHashMap<EnvelopeHash, Envelope>,
folder_hash: FolderHash,
mailbox: &mut Result<Mailbox>,
mailbox: &mut Mailbox,
sent_folder: Option<FolderHash>,
) {
self.sent_folder = sent_folder;
@ -135,9 +132,7 @@ impl Collection {
if self.message_ids.contains_key(e.message_id().raw()) {
/* skip duplicates until a better way to handle them is found. */
//FIXME
if let Ok(mailbox) = mailbox.as_mut() {
mailbox.remove(h);
}
mailbox.remove(h);
false
} else {
self.message_ids.insert(e.message_id().raw().to_vec(), h);
@ -214,13 +209,10 @@ impl Collection {
}
}
/* envelope is not in threads, so insert it */
let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope;
unsafe {
self.threads
.entry(folder_hash)
.or_default()
.insert(&mut (*env), &self.envelopes);
}
self.threads
.entry(folder_hash)
.or_default()
.insert(&mut self.envelopes, new_hash);
for (h, t) in self.threads.iter_mut() {
if *h == folder_hash {
continue;
@ -236,10 +228,17 @@ impl Collection {
self.message_ids
.insert(envelope.message_id().raw().to_vec(), hash);
self.envelopes.insert(hash, envelope);
self.threads
if !self
.threads
.entry(folder_hash)
.or_default()
.insert_reply(&mut self.envelopes, hash);
.insert_reply(&mut self.envelopes, hash)
{
self.threads
.entry(folder_hash)
.or_default()
.insert(&mut self.envelopes, hash);
}
&self.envelopes[&hash]
}
pub fn insert_reply(&mut self, env_hash: EnvelopeHash) {

View File

@ -27,7 +27,6 @@
use crate::backends::Folder;
pub use crate::email::*;
use crate::error::Result;
use crate::thread::ThreadHash;
pub use crate::thread::{SortField, SortOrder, ThreadNode, Threads};
@ -47,19 +46,15 @@ pub struct Mailbox {
}
impl Mailbox {
pub fn new(
folder: Folder,
envelopes: Result<&FnvHashMap<EnvelopeHash, Envelope>>,
) -> Result<Mailbox> {
let envelopes = envelopes?;
pub fn new(folder: Folder, envelopes: &FnvHashMap<EnvelopeHash, Envelope>) -> Mailbox {
let name = folder.name().into();
let envelopes = envelopes.keys().cloned().collect();
Ok(Mailbox {
Mailbox {
folder,
name,
envelopes,
..Default::default()
})
}
}
pub fn name(&self) -> &str {

View File

@ -938,12 +938,7 @@ impl Threads {
new_hash_set.difference(&self.hash_set).cloned().collect();
for h in difference {
debug!("inserting {}", envelopes[&h].subject());
let env = envelopes.entry(h).or_default() as *mut Envelope;
unsafe {
// `envelopes` is borrowed immutably and `insert` only changes the envelope's
// `thread` field.
self.insert(&mut (*env), envelopes);
}
self.insert(envelopes, h);
}
self.create_root_set(envelopes);
@ -953,10 +948,10 @@ impl Threads {
tree.retain(|t| root_set.contains(&t.id));
}
pub fn insert(&mut self, envelope: &mut Envelope, envelopes: &Envelopes) {
self.link_envelope(envelope);
pub fn insert(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) {
self.link_envelope(envelopes.get_mut(&env_hash).unwrap());
{
let id = self.message_ids[envelope.message_id().raw()];
let id = self.message_ids[envelopes[&env_hash].message_id().raw()];
self.rebuild_thread(id, envelopes);
}
}

View File

@ -143,7 +143,7 @@ impl Composer {
context: &Context,
) -> Self {
let account = &context.accounts[coordinates.0];
let mailbox = &account[coordinates.1].as_ref().unwrap();
let mailbox = &account[coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_nodes = &threads.thread_nodes();
let mut ret = Composer::default();

View File

@ -532,12 +532,16 @@ impl Listing {
Ok(_) => {
let account = &context.accounts[index];
let count = account[entries[&folder_idx].hash()]
.as_ref()
.unwrap()
.envelopes
.iter()
.map(|h| &account.collection[&h])
.filter(|e| !e.is_seen())
.filter_map(|h| {
if account.collection[&h].is_seen() {
None
} else {
Some(())
}
})
.count();
let len = s.len();
s.insert_str(

View File

@ -539,13 +539,14 @@ impl CompactListing {
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(folder_hash) {
Ok(_) => {}
Ok(()) => {}
Err(_) => {
let message: String = context.accounts[self.cursor_pos.0][folder_hash].to_string();
self.data_columns.columns[0] =
CellBuffer::new("Loading.".len(), 1, Cell::with_char(' '));
CellBuffer::new(message.len(), 1, Cell::with_char(' '));
self.length = 0;
write_string_to_grid(
"Loading.",
message.as_str(),
&mut self.data_columns.columns[0],
Color::Default,
Color::Default,
@ -562,7 +563,7 @@ impl CompactListing {
}
let account = &context.accounts[self.cursor_pos.0];
let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
let mailbox = &account[self.cursor_pos.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
self.order.clear();
@ -778,7 +779,11 @@ impl CompactListing {
return;
}
let account = &context.accounts[self.cursor_pos.0];
let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
let mailbox = if account[self.cursor_pos.1].is_available() {
account[self.cursor_pos.1].unwrap()
} else {
return;
};
let threads = &account.collection.threads[&mailbox.folder.hash()];
self.length = 0;
@ -913,10 +918,7 @@ impl CompactListing {
}
fn get_envelope_under_cursor(&self, cursor: usize, context: &Context) -> EnvelopeHash {
let account = &context.accounts[self.cursor_pos.0];
let folder_hash = account[self.cursor_pos.1]
.as_ref()
.map(|m| m.folder.hash())
.unwrap();
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
let threads = &account.collection.threads[&folder_hash];
if self.filtered_selection.is_empty() {
let thread_node = threads.root_set(cursor);
@ -1035,10 +1037,7 @@ impl Component for CompactListing {
.thread()
.clone()
};
let folder_hash = account[self.cursor_pos.1]
.as_ref()
.map(|m| m.folder.hash())
.unwrap();
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
let threads = &account.collection.threads[&folder_hash];
let root_thread_index = threads.root_iter().position(|t| t == thread_hash);
if let Some(pos) = root_thread_index {
@ -1168,10 +1167,7 @@ impl Component for CompactListing {
let i = self.get_envelope_under_cursor(self.cursor_pos.2, context);
let account = &mut context.accounts[self.cursor_pos.0];
let thread_hash = account.get_env(&i).thread();
let folder_hash = account[self.cursor_pos.1]
.as_ref()
.map(|m| m.folder.hash())
.unwrap();
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
let threads = account.collection.threads.entry(folder_hash).or_default();
let thread_group = threads.thread_nodes()[&thread_hash].thread_group();
let thread_group = threads.find(thread_group);

View File

@ -266,10 +266,11 @@ impl PlainListing {
match context.accounts[self.cursor_pos.0].status(folder_hash) {
Ok(_) => {}
Err(_) => {
self.content = CellBuffer::new(MAX_COLS, 1, Cell::with_char(' '));
let message: String = context.accounts[self.cursor_pos.0][folder_hash].to_string();
self.content = CellBuffer::new(message.len(), 1, Cell::with_char(' '));
self.length = 0;
write_string_to_grid(
"Loading.",
message.as_str(),
&mut self.content,
Color::Default,
Color::Default,
@ -280,7 +281,7 @@ impl PlainListing {
}
}
let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
let mailbox = &account[self.cursor_pos.1].unwrap();
self.length = mailbox.len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));

View File

@ -137,9 +137,11 @@ impl ListingTrait for ThreadListing {
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
let mailbox = if context.accounts[self.cursor_pos.0][self.cursor_pos.1].is_available() {
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
} else {
return;
};
if mailbox.is_empty() || mailbox.len() <= idx {
return;
}
@ -222,10 +224,11 @@ impl ThreadListing {
match context.accounts[self.cursor_pos.0].status(folder_hash) {
Ok(_) => {}
Err(_) => {
self.content = CellBuffer::new(MAX_COLS, 1, Cell::with_char(' '));
let message: String = context.accounts[self.cursor_pos.0][folder_hash].to_string();
self.content = CellBuffer::new(message.len(), 1, Cell::with_char(' '));
self.length = 0;
write_string_to_grid(
"Loading.",
message.as_str(),
&mut self.content,
Color::Default,
Color::Default,
@ -236,7 +239,7 @@ impl ThreadListing {
}
}
let account = &context.accounts[self.cursor_pos.0];
let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
let mailbox = account[self.cursor_pos.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
self.length = threads.len();
@ -328,9 +331,11 @@ impl ThreadListing {
}
fn highlight_line_self(&mut self, idx: usize, context: &Context) {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
let mailbox = if context.accounts[self.cursor_pos.0][self.cursor_pos.1].is_available() {
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
} else {
return;
};
if mailbox.is_empty() {
return;
}

View File

@ -158,7 +158,7 @@ impl ThreadView {
fn initiate(&mut self, expanded_hash: Option<ThreadHash>, context: &Context) {
/* stack to push thread messages in order in order to pop and print them later */
let account = &context.accounts[self.coordinates.0];
let mailbox = &account[self.coordinates.1].as_ref().unwrap();
let mailbox = &account[self.coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_iter = threads.thread_iter(self.coordinates.2);
@ -607,7 +607,7 @@ impl ThreadView {
/* First draw the thread subject on the first row */
let y = if self.dirty {
let account = &context.accounts[self.coordinates.0];
let mailbox = &account[self.coordinates.1].as_ref().unwrap();
let mailbox = &account[self.coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)];
let i = if let Some(i) = thread_node.message() {
@ -688,7 +688,7 @@ impl ThreadView {
/* First draw the thread subject on the first row */
let y = {
let account = &context.accounts[self.coordinates.0];
let mailbox = &account[self.coordinates.1].as_ref().unwrap();
let mailbox = &account[self.coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)];
let i = if let Some(i) = thread_node.message() {
@ -904,7 +904,7 @@ impl Component for ThreadView {
UIEvent::Input(Key::Char('e')) => {
{
let account = &context.accounts[self.coordinates.0];
let mailbox = &account[self.coordinates.1].as_ref().unwrap();
let mailbox = &account[self.coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_node =
&threads.thread_nodes()[&threads.root_set(self.coordinates.2)];

View File

@ -921,7 +921,7 @@ impl Component for StatusBar {
}
}
let account = &context.accounts[*idx_a];
let m = &account[*idx_f].as_ref().unwrap();
let m = &account[*idx_f].unwrap();
self.status = format!(
"{} | Mailbox: {}, Messages: {}, New: {}",
self.mode,

View File

@ -46,24 +46,75 @@ use std::ops::{Index, IndexMut};
use std::result;
use std::sync::Arc;
pub type Worker = Option<Async<(Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>)>>;
pub type Worker = Option<Async<Result<(FnvHashMap<EnvelopeHash, Envelope>, Mailbox)>>>;
macro_rules! mailbox {
($idx:expr, $folders:expr) => {
$folders
.get_mut(&$idx)
.unwrap()
.as_mut()
.unwrap()
.as_mut()
.unwrap()
$folders.get_mut(&$idx).unwrap().unwrap_mut()
};
}
#[derive(Serialize, Debug)]
pub enum MailboxEntry {
Available(Mailbox),
Failed(MeliError),
/// first argument is done work, and second is total work
Parsing(usize, usize),
/// first argument is done work, and second is total work
Threading(usize, usize),
}
impl std::fmt::Display for MailboxEntry {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
MailboxEntry::Available(ref m) => m.name().to_string(),
MailboxEntry::Failed(ref e) => e.to_string(),
MailboxEntry::Parsing(done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
MailboxEntry::Threading(done, total) => {
format!("Calculating threads. [{}/{}]", done, total)
}
}
)
}
}
impl MailboxEntry {
pub fn is_available(&self) -> bool {
if let MailboxEntry::Available(_) = self {
true
} else {
false
}
}
pub fn is_parsing(&self) -> bool {
if let MailboxEntry::Parsing(_, _) = self {
true
} else {
false
}
}
pub fn unwrap_mut(&mut self) -> &mut Mailbox {
match self {
MailboxEntry::Available(ref mut m) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
}
pub fn unwrap(&self) -> &Mailbox {
match self {
MailboxEntry::Available(ref m) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
}
}
#[derive(Debug)]
pub struct Account {
name: String,
pub(crate) folders: FnvHashMap<FolderHash, Option<Result<Mailbox>>>,
pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
pub(crate) folders_order: Vec<FolderHash>,
folder_names: FnvHashMap<FolderHash, String>,
tree: Vec<FolderNode>,
@ -113,31 +164,21 @@ impl Drop for Account {
pub struct MailboxIterator<'a> {
folders_order: &'a [FolderHash],
folders: &'a FnvHashMap<FolderHash, Option<Result<Mailbox>>>,
folders: &'a FnvHashMap<FolderHash, MailboxEntry>,
pos: usize,
}
impl<'a> Iterator for MailboxIterator<'a> {
type Item = Option<&'a Mailbox>;
type Item = &'a MailboxEntry;
fn next(&mut self) -> Option<Option<&'a Mailbox>> {
fn next(&mut self) -> Option<&'a MailboxEntry> {
if self.pos == self.folders.len() {
return None;
}
let fh = &self.folders_order[self.pos];
if self.pos == self.folders.len() {
return None;
}
self.pos += 1;
if self.folders[&fh].is_none() {
return Some(None);
}
if let Some(Err(_)) = self.folders[&fh] {
return Some(None);
}
Some(Some(self.folders[&fh].as_ref().unwrap().as_ref().unwrap()))
Some(&self.folders[&fh])
}
}
@ -156,7 +197,7 @@ impl Account {
) -> Self {
let mut backend = map.get(settings.account().format())(settings.account());
let mut ref_folders: FnvHashMap<FolderHash, Folder> = backend.folders();
let mut folders: FnvHashMap<FolderHash, Option<Result<Mailbox>>> =
let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
@ -208,7 +249,7 @@ impl Account {
}
}
}
folders.insert(*h, None);
folders.insert(*h, MailboxEntry::Parsing(0, 0));
workers.insert(
*h,
Account::new_worker(f.clone(), &mut backend, notify_fn.clone()),
@ -282,15 +323,19 @@ impl Account {
.collect::<FnvHashMap<EnvelopeHash, Envelope>>()
});
let hash = folder.hash();
let m = Mailbox::new(folder, envelopes.as_ref().map_err(Clone::clone));
tx.send(AsyncStatus::Payload((envelopes, m)));
if envelopes.is_err() {
tx.send(AsyncStatus::Payload(Err(envelopes.unwrap_err())));
notify_fn.notify(hash);
return;
}
let envelopes = envelopes.unwrap();
let m = Mailbox::new(folder, &envelopes);
tx.send(AsyncStatus::Payload(Ok((envelopes, m))));
notify_fn.notify(hash);
})))
}
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
if self.folders[&folder_hash].is_none()
|| self.folders[&folder_hash].as_ref().unwrap().is_err()
{
if !self.folders[&folder_hash].is_available() {
self.event_queue.push_back((folder_hash, event));
return None;
}
@ -306,15 +351,13 @@ impl Account {
}
RefreshEventKind::Rename(old_hash, new_hash) => {
debug!("rename {} to {}", old_hash, new_hash);
let mailbox = mailbox!(&folder_hash, self.folders);
mailbox.rename(old_hash, new_hash);
mailbox!(&folder_hash, self.folders).rename(old_hash, new_hash);
self.collection.rename(old_hash, new_hash, folder_hash);
return Some(EnvelopeRename(old_hash, new_hash));
}
RefreshEventKind::Create(envelope) => {
let env_hash = envelope.hash();
let mailbox = mailbox!(&folder_hash, self.folders);
mailbox.insert(env_hash);
mailbox!(&folder_hash, self.folders).insert(env_hash);
self.collection.insert(*envelope, folder_hash);
if self
.sent_folder
@ -367,7 +410,7 @@ impl Account {
pub fn watch(&self, r: RefreshEventConsumer) {
self.backend.watch(r).unwrap();
}
/* This doesn't represent the number of correctly parsed mailboxes though */
pub fn len(&self) -> usize {
self.folders.len()
}
@ -417,17 +460,18 @@ impl Account {
fn load_mailbox(
&mut self,
folder_hash: FolderHash,
mailbox: (Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>),
payload: (Result<(FnvHashMap<EnvelopeHash, Envelope>, Mailbox)>),
) {
let (envs, mut mailbox) = mailbox;
if envs.is_err() {
self.folders.insert(folder_hash, None);
if payload.is_err() {
self.folders
.insert(folder_hash, MailboxEntry::Failed(payload.unwrap_err()));
return;
}
let envs = envs.unwrap();
let (envelopes, mut mailbox) = payload.unwrap();
self.collection
.merge(envs, folder_hash, &mut mailbox, self.sent_folder);
self.folders.insert(folder_hash, Some(mailbox));
.merge(envelopes, folder_hash, &mut mailbox, self.sent_folder);
self.folders
.insert(folder_hash, MailboxEntry::Available(mailbox));
}
pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
@ -435,12 +479,17 @@ impl Account {
None => {
return Ok(());
}
Some(ref mut w) if self.folders[&folder_hash].is_none() => match w.poll() {
Some(ref mut w) if self.folders[&folder_hash].is_parsing() => match w.poll() {
Ok(AsyncStatus::NoUpdate) => {
return Err(0);
}
Ok(AsyncStatus::Finished) => {}
Ok(AsyncStatus::ProgressReport(n)) => {
self.folders.entry(folder_hash).and_modify(|f| {
if let MailboxEntry::Parsing(ref mut d, _) = f {
*d += n;
}
});
return Err(n);
}
_ => {
@ -496,7 +545,7 @@ impl Account {
}
pub fn operation(&self, h: EnvelopeHash) -> Box<BackendOp> {
for mailbox in self.folders.values() {
if let Some(Ok(m)) = mailbox {
if let MailboxEntry::Available(ref m) = mailbox {
if m.envelopes.contains(&h) {
let operation = self.backend.operation(h, m.folder.hash());
if self.settings.account.read_only() {
@ -542,41 +591,28 @@ impl Account {
}
impl Index<FolderHash> for Account {
type Output = Result<Mailbox>;
fn index(&self, index: FolderHash) -> &Result<Mailbox> {
type Output = MailboxEntry;
fn index(&self, index: FolderHash) -> &MailboxEntry {
&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<FolderHash> for Account {
fn index_mut(&mut self, index: FolderHash) -> &mut Result<Mailbox> {
self.folders
.get_mut(&index)
.unwrap()
.as_mut()
.expect("BUG: Requested mailbox that is not yet available.")
fn index_mut(&mut self, index: FolderHash) -> &mut MailboxEntry {
self.folders.get_mut(&index).unwrap()
}
}
impl Index<usize> for Account {
type Output = Result<Mailbox>;
fn index(&self, index: usize) -> &Result<Mailbox> {
type Output = MailboxEntry;
fn index(&self, index: usize) -> &MailboxEntry {
&self.folders[&self.folders_order[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
.get_mut(&self.folders_order[index])
.unwrap()
.as_mut()
.expect("BUG: Requested mailbox that is not yet available.")
fn index_mut(&mut self, index: usize) -> &mut MailboxEntry {
self.folders.get_mut(&self.folders_order[index]).unwrap()
}
}

View File

@ -276,11 +276,12 @@ impl State {
return;
}
if let Some(notification) = self.context.accounts[idxa].reload(event, hash) {
if let UIEvent::Notification(_, _) = notification {
self.context
.replies
.push_back(UIEvent::MailboxUpdate((idxa, hash)));
}
self.rcv_event(notification);
} else {
self.context
.replies
.push_back(UIEvent::MailboxUpdate((idxa, hash)));
}
} else {
debug!(