melib: remove Mailbox

Refactor Collection from melib to hold what folders have what envelopes.

Frontend accounts will now have a FolderEntry for each logical folder
and will unify many Account fields into one and eliminate a lot of
duplicate/dead code.
async
Manos Pitsidianakis 2020-02-10 02:11:07 +02:00
parent b50e770b5a
commit b6efb14824
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
12 changed files with 306 additions and 440 deletions

View File

@ -21,12 +21,13 @@
use super::*;
use crate::backends::FolderHash;
use core::ops::{Index, IndexMut};
use smallvec::SmallVec;
use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
use fnv::FnvHashMap;
use fnv::{FnvHashMap, FnvHashSet};
pub struct EnvelopeRef<'g> {
guard: RwLockReadGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
@ -68,6 +69,7 @@ pub struct Collection {
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
pub threads: FnvHashMap<FolderHash, Threads>,
sent_folder: Option<FolderHash>,
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
}
impl Drop for Collection {
@ -101,6 +103,7 @@ impl Collection {
* /cur, or it was deleted).
*/
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
let mailboxes = FnvHashMap::with_capacity_and_hasher(16, Default::default());
Collection {
envelopes: Arc::new(RwLock::new(envelopes)),
@ -108,6 +111,7 @@ impl Collection {
message_ids,
subject_index,
threads,
mailboxes,
sent_folder: None,
}
}
@ -123,6 +127,9 @@ impl Collection {
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
debug!("DEBUG: Removing {}", envelope_hash);
self.envelopes.write().unwrap().remove(&envelope_hash);
self.mailboxes.entry(folder_hash).and_modify(|m| {
m.remove(&envelope_hash);
});
self.threads
.entry(folder_hash)
.or_default()
@ -144,11 +151,15 @@ impl Collection {
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
return;
}
let mut env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
env.set_hash(new_hash);
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
self.mailboxes.entry(folder_hash).and_modify(|m| {
m.remove(&old_hash);
m.insert(new_hash);
});
envelope.set_hash(new_hash);
self.message_ids
.insert(env.message_id().raw().to_vec(), new_hash);
self.envelopes.write().unwrap().insert(new_hash, env);
.insert(envelope.message_id().raw().to_vec(), new_hash);
self.envelopes.write().unwrap().insert(new_hash, envelope);
{
if self
.threads
@ -175,7 +186,7 @@ impl Collection {
}
}
/// Merge new Mailbox to collection and update threads.
/// Merge new mailbox to collection and update threads.
/// Returns a list of already existing folders whose threads were updated
pub fn merge(
&mut self,
@ -191,16 +202,21 @@ impl Collection {
let &mut Collection {
ref mut threads,
ref mut envelopes,
ref mut mailboxes,
ref sent_folder,
..
} = self;
if !threads.contains_key(&folder_hash) {
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
for (h, e) in new_envelopes {
envelopes.write().unwrap().insert(h, e);
}
} else {
mailboxes.entry(folder_hash).and_modify(|m| {
m.extend(new_envelopes.keys().cloned());
});
threads.entry(folder_hash).and_modify(|t| {
let mut ordered_hash_set =
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
@ -291,6 +307,10 @@ impl Collection {
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
envelope.set_thread(old_env.thread());
let new_hash = envelope.hash();
self.mailboxes.entry(folder_hash).and_modify(|m| {
m.remove(&old_hash);
m.insert(new_hash);
});
self.message_ids
.insert(envelope.message_id().raw().to_vec(), new_hash);
self.envelopes.write().unwrap().insert(new_hash, envelope);
@ -328,6 +348,9 @@ impl Collection {
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
let hash = envelope.hash();
self.mailboxes.entry(folder_hash).and_modify(|m| {
m.insert(hash);
});
self.message_ids
.insert(envelope.message_id().raw().to_vec(), hash);
self.envelopes.write().unwrap().insert(hash, envelope);
@ -365,3 +388,16 @@ impl Collection {
self.envelopes.read().unwrap().contains_key(env_hash)
}
}
impl Index<&FolderHash> for Collection {
type Output = FnvHashSet<EnvelopeHash>;
fn index(&self, index: &FolderHash) -> &FnvHashSet<EnvelopeHash> {
&self.mailboxes[index]
}
}
impl IndexMut<&FolderHash> for Collection {
fn index_mut(&mut self, index: &FolderHash) -> &mut FnvHashSet<EnvelopeHash> {
self.mailboxes.get_mut(index).unwrap()
}
}

View File

@ -119,7 +119,6 @@ mod collection;
pub mod conf;
pub mod email;
pub mod error;
pub mod mailbox;
pub mod thread;
pub use crate::email::*;
pub use crate::thread::*;
@ -138,11 +137,10 @@ extern crate bitflags;
extern crate fnv;
extern crate uuid;
pub use crate::conf::*;
pub use crate::mailbox::*;
pub use crate::backends::{Backends, RefreshEvent, RefreshEventConsumer, SpecialUsageMailbox};
pub use crate::email::{Envelope, Flag};
pub use crate::collection::*;
pub use crate::conf::*;
pub use crate::email::{Envelope, EnvelopeHash, Flag};
pub use crate::error::{MeliError, Result};
pub use crate::addressbook::*;

View File

@ -1,80 +0,0 @@
/*
* meli - mailbox module.
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* Mail related code.
*
* This module handles reading emails from various backends, handling account data etc
*/
use crate::backends::Folder;
pub use crate::collection::*;
pub use crate::email::*;
use fnv::{FnvHashMap, FnvHashSet};
/// `Mailbox` represents a folder of mail.
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Mailbox {
#[serde(skip_serializing, skip_deserializing)]
pub folder: Folder,
name: String,
pub envelopes: FnvHashSet<EnvelopeHash>,
has_sent: bool,
}
impl Mailbox {
pub fn new(folder: Folder, envelopes: &FnvHashMap<EnvelopeHash, Envelope>) -> Mailbox {
let name = folder.name().into();
let envelopes = envelopes.keys().cloned().collect();
Mailbox {
folder,
name,
envelopes,
..Default::default()
}
}
pub fn merge(&mut self, envelopes: &FnvHashMap<EnvelopeHash, Envelope>) {
self.envelopes.extend(envelopes.keys().cloned());
}
pub fn name(&self) -> &str {
&self.name
}
pub fn is_empty(&self) -> bool {
self.envelopes.is_empty()
}
pub fn len(&self) -> usize {
self.envelopes.len()
}
pub fn insert(&mut self, h: EnvelopeHash) {
self.envelopes.insert(h);
}
pub fn rename(&mut self, old_hash: EnvelopeHash, new_hash: EnvelopeHash) {
self.envelopes.remove(&old_hash);
self.envelopes.insert(new_hash);
}
pub fn remove(&mut self, h: EnvelopeHash) {
self.envelopes.remove(&h);
}
}

View File

@ -137,7 +137,7 @@ pub trait MailListingTrait: ListingTrait {
) {
let account = &mut context.accounts[self.coordinates().0];
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
let folder_hash = account[self.coordinates().1].unwrap().folder.hash();
let folder_hash = self.coordinates().1;
for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) {
envs_to_set.push(
account.collection.threads[&folder_hash].thread_nodes()[&h]
@ -449,7 +449,8 @@ impl Component for Listing {
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index].ref_folders()[&folder_node.hash]
context.accounts[*account_index][&folder_node.hash]
.ref_folder
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
@ -464,7 +465,8 @@ impl Component for Listing {
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index].ref_folders()[&folder_node.hash]
context.accounts[*account_index][&folder_node.hash]
.ref_folder
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
@ -550,10 +552,10 @@ impl Component for Listing {
{
/* Account might have no folders yet if it's offline */
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) =
context.accounts.get(self.cursor_pos.0).and_then(|account| {
account.folder_confs(*folder_hash).conf_override.index_style
})
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
@ -870,15 +872,20 @@ impl Component for Listing {
};
let account = &context.accounts[self.cursor_pos.0];
if let Ok(m) = account[folder_hash].as_result() {
format!(
use crate::conf::accounts::MailboxStatus;
match account[&folder_hash].status {
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
"Mailbox: {}, Messages: {}, New: {}",
m.folder.name(),
m.envelopes.len(),
m.folder.count().ok().map(|(v, _)| v).unwrap_or(0),
)
} else {
account[folder_hash].to_string()
account[&folder_hash].ref_folder.name(),
account.collection[&folder_hash].len(),
account[&folder_hash]
.ref_folder
.count()
.ok()
.map(|(v, _)| v)
.unwrap_or(0),
),
MailboxStatus::Failed(_) | MailboxStatus::None => account[&folder_hash].status(),
}
}
}
@ -905,7 +912,7 @@ impl Listing {
let entries: SmallVec<[(usize, FolderHash); 16]> = a
.list_folders()
.into_iter()
.filter(|folder_node| a.ref_folders()[&folder_node.hash].is_subscribed())
.filter(|folder_node| a[&folder_node.hash].ref_folder.is_subscribed())
.map(|f| (f.depth, f.hash))
.collect::<_>();
@ -974,8 +981,11 @@ impl Listing {
debug!("BUG: invalid area in print_account");
}
// Each entry and its index in the account
let folders: FnvHashMap<FolderHash, Folder> =
context.accounts[a.index].ref_folders().clone();
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
.folder_entries
.iter()
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
.collect();
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
@ -1160,7 +1170,9 @@ impl Listing {
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[self.cursor_pos.0].ref_folders()[&folder_node.hash].is_subscribed()
context.accounts[self.cursor_pos.0][&folder_node.hash]
.ref_folder
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
.collect::<_>();
@ -1175,7 +1187,7 @@ impl Listing {
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account.folder_confs(*folder_hash).conf_override.index_style)
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context

View File

@ -142,7 +142,7 @@ impl MailListingTrait for CompactListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -650,7 +650,7 @@ impl CompactListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -716,9 +716,8 @@ impl CompactListing {
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.cursor_pos.1];
self.order.clear();
self.selection.clear();
self.length = 0;
@ -752,7 +751,7 @@ impl CompactListing {
debug!("key = {}", root_env_hash);
debug!(
"name = {} {}",
mailbox.name(),
account[&self.cursor_pos.1].name(),
context.accounts[self.cursor_pos.0].name()
);
debug!("{:#?}", context.accounts);
@ -837,7 +836,7 @@ impl CompactListing {
//debug!("key = {}", root_env_hash);
//debug!(
// "name = {} {}",
// mailbox.name(),
// account[&self.cursor_pos.1].name(),
// context.accounts[self.cursor_pos.0].name()
//);
//debug!("{:#?}", context.accounts);
@ -968,8 +967,7 @@ impl CompactListing {
}
}
if self.length == 0 && self.filter_term.is_empty() {
let mailbox = &account[self.cursor_pos.1];
let message = mailbox.to_string();
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
write_string_to_grid(

View File

@ -128,7 +128,7 @@ impl MailListingTrait for ConversationsListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -592,7 +592,7 @@ impl ConversationsListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -658,9 +658,8 @@ impl ConversationsListing {
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.cursor_pos.1];
self.order.clear();
self.selection.clear();
self.length = 0;
@ -684,7 +683,7 @@ impl ConversationsListing {
debug!("key = {}", root_env_hash);
debug!(
"name = {} {}",
mailbox.name(),
account[&self.cursor_pos.1].name(),
context.accounts[self.cursor_pos.0].name()
);
debug!("{:#?}", context.accounts);
@ -852,8 +851,7 @@ impl ConversationsListing {
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let mailbox = &account[self.cursor_pos.1];
let message = mailbox.to_string();
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,

View File

@ -142,7 +142,7 @@ impl MailListingTrait for PlainListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -158,9 +158,7 @@ impl MailListingTrait for PlainListing {
return;
}
}
self.local_collection = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.unwrap()
.envelopes
self.local_collection = context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1]
.iter()
.cloned()
.collect();
@ -169,9 +167,8 @@ impl MailListingTrait for PlainListing {
.envelopes
.read()
.unwrap();
self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.unwrap()
.envelopes
self.thread_node_hashes = context.accounts[self.cursor_pos.0].collection
[&self.cursor_pos.1]
.iter()
.map(|h| (*h, env_lck[h].thread()))
.collect();
@ -616,7 +613,7 @@ impl PlainListing {
}
}
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -664,7 +661,7 @@ impl PlainListing {
fn redraw_list(&mut self, context: &Context) {
let account = &context.accounts[self.cursor_pos.0];
let mailbox = &account[self.cursor_pos.1].unwrap();
let mailbox = &account[&self.cursor_pos.1];
self.order.clear();
self.selection.clear();
@ -890,8 +887,7 @@ impl PlainListing {
}
}
if self.length == 0 && self.filter_term.is_empty() {
let mailbox = &account[self.cursor_pos.1];
let message = mailbox.to_string();
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
write_string_to_grid(
@ -1139,10 +1135,7 @@ impl Component for PlainListing {
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[self.cursor_pos.0];
if !account.collection.contains_key(new_hash)
|| !account[self.cursor_pos.1]
.unwrap()
.envelopes
.contains(new_hash)
|| !account.collection[&self.cursor_pos.1].contains(new_hash)
{
return false;
}

View File

@ -99,7 +99,7 @@ impl MailListingTrait for ThreadListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -116,9 +116,7 @@ impl MailListingTrait for ThreadListing {
}
}
let account = &context.accounts[self.cursor_pos.0];
let mailbox = account[self.cursor_pos.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.cursor_pos.1];
self.length = threads.len();
self.locations.clear();
let default_cell = {
@ -129,7 +127,7 @@ impl MailListingTrait for ThreadListing {
ret
};
if self.length == 0 {
let message = format!("Folder `{}` is empty.", mailbox.folder.name());
let message = format!("Folder `{}` is empty.", account[&self.cursor_pos.1].name());
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
@ -348,12 +346,7 @@ impl ListingTrait for ThreadListing {
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
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 {
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
return;
}
@ -414,12 +407,7 @@ impl ThreadListing {
}
fn highlight_line_self(&mut self, idx: usize, context: &Context) {
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() {
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
return;
}
if self.locations[idx] != 0 {

View File

@ -307,10 +307,14 @@ impl StatusPanel {
self.content[(2, h + y + 1)].set_ch(' ');
}
}
let count = a.ref_folders.values().fold((0, 0), |acc, f| {
let count = f.count().unwrap_or((0, 0));
(acc.0 + count.0, acc.1 + count.1)
});
let count = a
.folder_entries
.values()
.map(|entry| &entry.ref_folder)
.fold((0, 0), |acc, f| {
let count = f.count().unwrap_or((0, 0));
(acc.0 + count.0, acc.1 + count.1)
});
let (mut column_width, _) = write_string_to_grid(
&format!("Messages total {}, unseen {}", count.1, count.0),
&mut self.content,
@ -357,8 +361,9 @@ impl StatusPanel {
None,
);
for (i, f) in a
.ref_folders
.folder_entries
.values()
.map(|entry| &entry.ref_folder)
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
.enumerate()
{
@ -474,8 +479,9 @@ impl Component for AccountStatus {
None,
);
for f in a
.ref_folders
.folder_entries
.values()
.map(|entry| &entry.ref_folder)
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
{
line += 1;
@ -501,7 +507,7 @@ impl Component for AccountStatus {
);
line += 2;
for folder_node in a.list_folders() {
let f: &Folder = &a.ref_folders()[&folder_node.hash];
let f: &Folder = &a[&folder_node.hash].ref_folder;
if f.is_subscribed() {
write_string_to_grid(
f.path(),

View File

@ -69,7 +69,7 @@ pub struct ThreadView {
impl ThreadView {
const DESCRIPTION: &'static str = "thread view";
/*
* coordinates: (account index, mailbox index, root set thread_node index)
* coordinates: (account index, folder_hash, root set thread_node index)
* expanded_hash: optional position of expanded entry when we render the threadview. Default
* expanded message is the last one.
* context: current context
@ -163,8 +163,7 @@ impl ThreadView {
fn initiate(&mut self, expanded_hash: Option<ThreadNodeHash>, context: &Context) {
let account = &context.accounts[self.coordinates.0];
let mailbox = &account[self.coordinates.1].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.coordinates.1];
if !threads.groups.contains_key(&self.thread_group) {
return;
@ -649,8 +648,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].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.coordinates.1];
let thread_root = threads
.thread_group_iter(self.thread_group)
.next()
@ -752,8 +750,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].unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &account.collection.threads[&self.coordinates.1];
let thread_root = threads
.thread_group_iter(self.thread_group)
.next()

View File

@ -30,11 +30,12 @@ use melib::backends::{
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
};
use melib::email::*;
use melib::error::{MeliError, Result};
use melib::mailbox::*;
use melib::text_processing::GlobMatch;
use melib::thread::{SortField, SortOrder, ThreadNode, ThreadNodeHash, Threads};
use melib::AddressBook;
use melib::Collection;
use smallvec::SmallVec;
use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
@ -50,97 +51,61 @@ use std::sync::{Arc, RwLock};
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
macro_rules! mailbox {
($idx:expr, $folders:expr) => {
$folders.get_mut(&$idx).unwrap().unwrap_mut()
};
}
#[derive(Serialize, Debug)]
pub enum MailboxEntry {
Available(Mailbox),
pub enum MailboxStatus {
Available,
Failed(MeliError),
/// first argument is done work, and second is total work
Parsing(Mailbox, usize, usize),
Parsing(usize, usize),
None,
}
impl Default for MailboxEntry {
impl Default for MailboxStatus {
fn default() -> Self {
MailboxEntry::Parsing(Mailbox::default(), 0, 0)
MailboxStatus::Parsing(0, 0)
}
}
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::None => "Not subscribed, is this a bug?".to_string(),
MailboxEntry::Parsing(_, done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
}
)
}
}
impl MailboxEntry {
impl MailboxStatus {
pub fn is_available(&self) -> bool {
if let MailboxEntry::Available(_) = self {
if let MailboxStatus::Available = self {
true
} else {
false
}
}
pub fn is_parsing(&self) -> bool {
if let MailboxEntry::Parsing(_, _, _) = self {
if let MailboxStatus::Parsing(_, _) = self {
true
} else {
false
}
}
}
pub fn as_result(&self) -> Result<&Mailbox> {
match self {
MailboxEntry::Available(ref m) => Ok(m),
MailboxEntry::Parsing(ref m, _, _) => Ok(m),
MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
"Mailbox is not available: {}",
e.to_string()
))),
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
#[derive(Debug)]
pub struct FolderEntry {
pub status: MailboxStatus,
pub name: String,
pub ref_folder: Folder,
pub conf: FileFolderConf,
pub worker: Worker,
}
impl FolderEntry {
pub fn status(&self) -> String {
match self.status {
MailboxStatus::Available => self.name().to_string(),
MailboxStatus::Failed(ref e) => e.to_string(),
MailboxStatus::None => "Not subscribed, is this a bug?".to_string(),
MailboxStatus::Parsing(done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
}
}
pub fn as_mut_result(&mut self) -> Result<&mut Mailbox> {
match self {
MailboxEntry::Available(ref mut m) => Ok(m),
MailboxEntry::Parsing(ref mut m, _, _) => Ok(m),
MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
"Mailbox is not available: {}",
e.to_string()
))),
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
}
}
pub fn unwrap_mut(&mut self) -> &mut Mailbox {
match self {
MailboxEntry::Available(ref mut m) => m,
MailboxEntry::Parsing(ref mut m, _, _) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
}
pub fn unwrap(&self) -> &Mailbox {
match self {
MailboxEntry::Available(ref m) => m,
MailboxEntry::Parsing(ref m, _, _) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
pub fn name(&self) -> &str {
&self.name
}
}
@ -149,20 +114,13 @@ pub struct Account {
pub index: usize,
name: String,
pub is_online: bool,
pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
pub(crate) ref_folders: FnvHashMap<FolderHash, Folder>,
pub(crate) folder_confs: FnvHashMap<FolderHash, FileFolderConf>,
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
pub(crate) folders_order: Vec<FolderHash>,
pub(crate) folder_names: FnvHashMap<FolderHash, String>,
tree: Vec<FolderNode>,
sent_folder: Option<FolderHash>,
pub(crate) collection: Collection,
pub(crate) address_book: AddressBook,
pub(crate) workers: FnvHashMap<FolderHash, Worker>,
pub(crate) work_context: WorkContext,
pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf,
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
@ -210,7 +168,7 @@ impl Drop for Account {
permissions.set_mode(0o600); // Read/write for owner only.
f.set_permissions(permissions).unwrap();
let writer = io::BufWriter::new(f);
if let Err(err) = bincode::serialize_into(writer, &self.folders) {
if let Err(err) = bincode::serialize_into(writer, &self.collection) {
eprintln!("{}", err);
};
};
@ -218,26 +176,6 @@ impl Drop for Account {
}
}
pub struct MailboxIterator<'a> {
folders_order: &'a [FolderHash],
folders: &'a FnvHashMap<FolderHash, MailboxEntry>,
pos: usize,
}
impl<'a> Iterator for MailboxIterator<'a> {
type Item = &'a MailboxEntry;
fn next(&mut self) -> Option<&'a MailboxEntry> {
if self.pos == self.folders.len() {
return None;
}
let fh = &self.folders_order[self.pos];
self.pos += 1;
Some(&self.folders[&fh])
}
}
#[derive(Serialize, Debug, Clone, Default)]
pub struct FolderNode {
pub hash: FolderHash,
@ -295,16 +233,12 @@ impl Account {
index,
name,
is_online: false,
folders: Default::default(),
ref_folders: Default::default(),
folder_confs: Default::default(),
folder_entries: Default::default(),
folders_order: Default::default(),
folder_names: Default::default(),
tree: Default::default(),
address_book,
sent_folder: Default::default(),
collection: Default::default(),
workers: Default::default(),
work_context,
runtime_settings: settings.clone(),
settings,
@ -325,12 +259,9 @@ impl Account {
return;
}
};
let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
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();
let mut folder_names = FnvHashMap::default();
let mut folder_confs = FnvHashMap::default();
let mut sent_folder = None;
for f in ref_folders.values_mut() {
@ -355,7 +286,16 @@ impl Account {
}
_ => {}
}
folder_confs.insert(f.hash(), conf.clone());
folder_entries.insert(
f.hash(),
FolderEntry {
ref_folder: f.clone(),
name: f.path().to_string(),
status: MailboxStatus::None,
conf: conf.clone(),
worker: None,
},
);
} else {
let mut new = FileFolderConf::default();
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
@ -368,9 +308,17 @@ impl Account {
tmp
};
folder_confs.insert(f.hash(), new);
folder_entries.insert(
f.hash(),
FolderEntry {
ref_folder: f.clone(),
name: f.path().to_string(),
status: MailboxStatus::None,
conf: new,
worker: None,
},
);
}
folder_names.insert(f.hash(), f.path().to_string());
}
let mut tree: Vec<FolderNode> = Vec::new();
@ -378,36 +326,27 @@ impl Account {
for (h, f) in ref_folders.iter() {
if !f.is_subscribed() {
/* Skip unsubscribed folder */
folders.insert(*h, MailboxEntry::None);
workers.insert(*h, None);
continue;
}
folders.insert(
*h,
MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
);
workers.insert(
*h,
Account::new_worker(
folder_entries.entry(*h).and_modify(|entry| {
entry.status = MailboxStatus::Parsing(0, 0);
entry.worker = Account::new_worker(
f.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
),
);
);
});
collection.mailboxes.insert(*h, Default::default());
collection.threads.insert(*h, Threads::default());
}
build_folders_order(&mut tree, &ref_folders, &mut folders_order);
self.folders = folders;
self.ref_folders = ref_folders;
self.folder_confs = folder_confs;
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
self.folders_order = folders_order;
self.folder_names = folder_names;
self.folder_entries = folder_entries;
self.tree = tree;
self.sent_folder = sent_folder;
self.collection = collection;
self.workers = workers;
}
fn new_worker(
@ -483,7 +422,7 @@ impl Account {
Some(w)
}
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
if !self.folders[&folder_hash].is_available() {
if !self.folder_entries[&folder_hash].status.is_available() {
self.event_queue.push_back((folder_hash, event));
return None;
}
@ -493,7 +432,6 @@ impl Account {
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
match kind {
RefreshEventKind::Update(old_hash, envelope) => {
mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
#[cfg(feature = "sqlite3")]
{
if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
@ -514,7 +452,6 @@ impl Account {
}
RefreshEventKind::Rename(old_hash, new_hash) => {
debug!("rename {} to {}", old_hash, new_hash);
mailbox!(&folder_hash, self.folders).rename(old_hash, new_hash);
self.collection.rename(old_hash, new_hash, folder_hash);
#[cfg(feature = "sqlite3")]
{
@ -538,13 +475,10 @@ impl Account {
RefreshEventKind::Create(envelope) => {
let env_hash = envelope.hash();
if self.collection.contains_key(&env_hash)
&& mailbox!(&folder_hash, self.folders)
.envelopes
.contains(&env_hash)
&& self.collection[&folder_hash].contains(&env_hash)
{
return None;
}
mailbox!(&folder_hash, self.folders).insert(env_hash);
let (is_seen, is_draft) =
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
let (subject, from) = {
@ -578,10 +512,12 @@ impl Account {
self.collection.insert_reply(env_hash);
}
let ref_folders: FnvHashMap<FolderHash, Folder> =
self.backend.read().unwrap().folders().unwrap();
let folder_conf = &self.folder_confs[&folder_hash];
if folder_conf.folder_conf().ignore.is_true() {
if self.folder_entries[&folder_hash]
.conf
.folder_conf
.ignore
.is_true()
{
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
}
@ -606,13 +542,12 @@ impl Account {
"{}\n{} {}",
subject,
self.name,
ref_folders[&folder_hash].name(),
self.folder_entries[&folder_hash].name()
),
Some(crate::types::NotificationType::NewMail),
));
}
RefreshEventKind::Remove(envelope_hash) => {
mailbox!(&folder_hash, self.folders).remove(envelope_hash);
#[cfg(feature = "sqlite3")]
{
let envelopes = self.collection.envelopes.read();
@ -633,15 +568,15 @@ impl Account {
return Some(EnvelopeRemove(envelope_hash));
}
RefreshEventKind::Rescan => {
let ref_folders: FnvHashMap<FolderHash, Folder> =
self.backend.read().unwrap().folders().unwrap();
let handle = Account::new_worker(
ref_folders[&folder_hash].clone(),
self.folder_entries[&folder_hash].ref_folder.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
);
self.workers.insert(folder_hash, handle);
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.worker = handle;
});
}
RefreshEventKind::Failure(e) => {
debug!("RefreshEvent Failure: {}", e.to_string());
@ -717,18 +652,14 @@ impl Account {
}
pub fn len(&self) -> usize {
self.folders.len()
self.tree.len()
}
pub fn is_empty(&self) -> bool {
self.folders.is_empty()
}
pub fn ref_folders(&self) -> &FnvHashMap<FolderHash, Folder> {
&self.ref_folders
self.tree.is_empty()
}
pub fn list_folders(&self) -> Vec<FolderNode> {
let mut ret = Vec::with_capacity(self.folders.len());
let mut ret = Vec::with_capacity(self.folder_entries.len());
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
ret.push(node.clone());
for c in node.children.iter() {
@ -747,14 +678,12 @@ impl Account {
pub fn name(&self) -> &str {
&self.name
}
pub fn workers(&mut self) -> &mut FnvHashMap<FolderHash, Worker> {
&mut self.workers
}
fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
if payload.is_err() {
self.folders
.insert(folder_hash, MailboxEntry::Failed(payload.unwrap_err()));
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.status = MailboxStatus::Failed(payload.unwrap_err());
});
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
.unwrap();
@ -765,20 +694,14 @@ impl Account {
.into_iter()
.map(|e| (e.hash(), e))
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
match self.folders.entry(folder_hash).or_default() {
MailboxEntry::Failed(_) | MailboxEntry::None => {}
MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => {
m.merge(&envelopes);
if let Some(updated_folders) =
self.collection
.merge(envelopes, folder_hash, self.sent_folder)
{
for f in updated_folders {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
.unwrap();
}
}
if let Some(updated_folders) =
self.collection
.merge(envelopes, folder_hash, self.sent_folder)
{
for f in updated_folders {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
.unwrap();
}
}
self.sender
@ -791,11 +714,17 @@ impl Account {
return Err(0);
}
loop {
match self.workers.get_mut(&folder_hash).unwrap() {
match self
.folder_entries
.get_mut(&folder_hash)
.unwrap()
.worker
.as_mut()
{
None => {
return if self.folders[&folder_hash].is_available()
|| (self.folders[&folder_hash].is_parsing()
&& self.collection.threads.contains_key(&folder_hash))
return if self.folder_entries[&folder_hash].status.is_available()
|| (self.folder_entries[&folder_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&folder_hash))
{
Ok(())
} else {
@ -812,13 +741,9 @@ impl Account {
}
Ok(AsyncStatus::Finished) => {
debug!("got finished in status for {}", folder_hash);
self.folders.entry(folder_hash).and_modify(|f| {
let m = if let MailboxEntry::Parsing(m, _, _) = f {
std::mem::replace(m, Mailbox::default())
} else {
return;
};
*f = MailboxEntry::Available(m);
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.status = MailboxStatus::Available;
entry.worker = None;
});
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
@ -826,13 +751,14 @@ impl Account {
folder_hash,
))))
.unwrap();
self.workers.insert(folder_hash, None);
}
Ok(AsyncStatus::ProgressReport(n)) => {
self.folders.entry(folder_hash).and_modify(|f| {
if let MailboxEntry::Parsing(_, ref mut d, _) = f {
*d += n;
self.folder_entries.entry(folder_hash).and_modify(|entry| {
match entry.status {
MailboxStatus::Parsing(ref mut d, _) => {
*d += n;
}
_ => {}
}
});
//return Err(n);
@ -843,9 +769,9 @@ impl Account {
},
};
}
if self.folders[&folder_hash].is_available()
|| (self.folders[&folder_hash].is_parsing()
&& self.collection.threads.contains_key(&folder_hash))
if self.folder_entries[&folder_hash].status.is_available()
|| (self.folder_entries[&folder_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&folder_hash))
{
Ok(())
} else {
@ -909,13 +835,6 @@ impl Account {
}
self.backend.write().unwrap().save(bytes, folder, flags)
}
pub fn iter_mailboxes(&self) -> MailboxIterator {
MailboxIterator {
folders_order: &self.folders_order,
folders: &self.folders,
pos: 0,
}
}
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
self.collection.contains_key(&h)
@ -966,39 +885,40 @@ impl Account {
tmp
};
self.folder_confs.insert(folder.hash(), new);
self.folder_names
.insert(folder.hash(), folder.path().to_string());
self.folders.insert(
folder.hash(),
MailboxEntry::Parsing(
Mailbox::new(folder.clone(), &FnvHashMap::default()),
0,
0,
),
);
self.workers.insert(
folder.hash(),
Account::new_worker(
folder.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
),
let folder_hash = folder.hash();
self.folder_entries.insert(
folder_hash,
FolderEntry {
name: folder.path().to_string(),
status: MailboxStatus::Parsing(0, 0),
conf: new,
worker: Account::new_worker(
folder.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
),
ref_folder: folder,
},
);
self.collection
.threads
.insert(folder.hash(), Threads::default());
self.ref_folders = self.backend.read().unwrap().folders()?;
build_folders_order(&mut self.tree, &self.ref_folders, &mut self.folders_order);
.insert(folder_hash, Threads::default());
build_folders_order(
&mut self.tree,
&self.folder_entries,
&mut self.folders_order,
);
Ok(format!("`{}` successfully created.", &path))
}
FolderOperation::Delete(path) => {
if self.ref_folders.len() == 1 {
if self.folder_entries.len() == 1 {
return Err(MeliError::new("Cannot delete only mailbox."));
}
let folder_hash = if let Some((folder_hash, _)) =
self.ref_folders.iter().find(|(_, f)| f.path() == path)
let folder_hash = if let Some((folder_hash, _)) = self
.folder_entries
.iter()
.find(|(_, f)| f.ref_folder.path() == path)
{
*folder_hash
} else {
@ -1011,13 +931,9 @@ impl Account {
folder_hash,
))))
.unwrap();
self.folders.remove(&folder_hash);
self.ref_folders = self.backend.read().unwrap().folders()?;
self.folder_confs.remove(&folder_hash);
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
self.folders_order.remove(pos);
}
self.folder_names.remove(&folder_hash);
if let Some(pos) = self.tree.iter().position(|n| n.hash == folder_hash) {
self.tree.remove(pos);
}
@ -1025,7 +941,9 @@ impl Account {
self.sent_folder = None;
}
self.collection.threads.remove(&folder_hash);
self.workers.remove(&folder_hash); // FIXME Kill worker as well
self.collection.mailboxes.remove(&folder_hash);
self.folder_entries.remove(&folder_hash);
// FIXME Kill worker as well
// FIXME remove from settings as well
@ -1038,29 +956,13 @@ impl Account {
}
}
pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf {
&self.folder_confs[&folder_hash]
}
pub fn sent_folder(&self) -> &str {
let sent_folder = self
.folder_confs
.iter()
.find(|(_, f)| f.folder_conf().usage == Some(SpecialUsageMailbox::Sent));
if let Some(sent_folder) = sent_folder.as_ref() {
&self.folder_names[&sent_folder.0]
} else {
""
}
}
pub fn special_use_folder(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
let ret = self
.folder_confs
.folder_entries
.iter()
.find(|(_, f)| f.folder_conf().usage == Some(special_use));
.find(|(_, f)| f.conf.folder_conf().usage == Some(special_use));
if let Some(ret) = ret.as_ref() {
Some(&self.folder_names[&ret.0])
Some(ret.1.name())
} else {
None
}
@ -1117,7 +1019,7 @@ impl Account {
let mut ret = SmallVec::new();
let envelopes = self.collection.envelopes.read().unwrap();
for &env_hash in &self.folders[&folder_hash].as_result()?.envelopes {
for &env_hash in &self.collection[&folder_hash].iter() {
let envelope = &envelopes[&env_hash];
if envelope.subject().contains(&search_term) {
ret.push(env_hash);
@ -1140,32 +1042,32 @@ impl Account {
}
}
impl Index<FolderHash> for Account {
type Output = MailboxEntry;
fn index(&self, index: FolderHash) -> &MailboxEntry {
&self.folders[&index]
impl Index<&FolderHash> for Account {
type Output = FolderEntry;
fn index(&self, index: &FolderHash) -> &FolderEntry {
&self.folder_entries[index]
}
}
impl IndexMut<FolderHash> for Account {
fn index_mut(&mut self, index: FolderHash) -> &mut MailboxEntry {
self.folders.get_mut(&index).unwrap()
impl IndexMut<&FolderHash> for Account {
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
self.folder_entries.get_mut(index).unwrap()
}
}
fn build_folders_order(
tree: &mut Vec<FolderNode>,
ref_folders: &FnvHashMap<FolderHash, Folder>,
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
folders_order: &mut Vec<FolderHash>,
) {
let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
tree.clear();
folders_order.clear();
for (h, f) in ref_folders.iter() {
if f.parent().is_none() {
for (h, f) in folder_entries.iter() {
if f.ref_folder.parent().is_none() {
fn rec(
h: FolderHash,
ref_folders: &FnvHashMap<FolderHash, Folder>,
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
depth: usize,
) -> FolderNode {
let mut node = FolderNode {
@ -1173,18 +1075,18 @@ fn build_folders_order(
children: Vec::new(),
depth,
};
for &c in ref_folders[&h].children() {
node.children.push(rec(c, ref_folders, depth + 1));
for &c in folder_entries[&h].ref_folder.children() {
node.children.push(rec(c, folder_entries, depth + 1));
}
node
};
tree.push(rec(*h, &ref_folders, 0));
for &c in f.children() {
tree.push(rec(*h, &folder_entries, 0));
for &c in f.ref_folder.children() {
stack.push(c);
}
while let Some(next) = stack.pop() {
for c in ref_folders[&next].children() {
for c in folder_entries[&next].ref_folder.children() {
stack.push(*c);
}
}
@ -1192,14 +1094,23 @@ fn build_folders_order(
}
tree.sort_unstable_by(|a, b| {
if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
if folder_entries[&b.hash]
.ref_folder
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
} else if folder_entries[&a.hash]
.ref_folder
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
ref_folders[&a.hash]
folder_entries[&a.hash]
.ref_folder
.path()
.cmp(&ref_folders[&b.hash].path())
.cmp(&folder_entries[&b.hash].ref_folder.path())
}
});
@ -1207,14 +1118,23 @@ fn build_folders_order(
for n in tree.iter_mut() {
folders_order.push(n.hash);
n.children.sort_unstable_by(|a, b| {
if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
if folder_entries[&b.hash]
.ref_folder
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
} else if folder_entries[&a.hash]
.ref_folder
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
ref_folders[&a.hash]
folder_entries[&a.hash]
.ref_folder
.path()
.cmp(&ref_folders[&b.hash].path())
.cmp(&folder_entries[&b.hash].ref_folder.path())
}
});
stack.extend(n.children.iter().rev().map(Some));

View File

@ -153,7 +153,7 @@ impl Context {
debug!(
"hash & folder: {:?} {}",
folder_node.hash,
accounts[account_pos].ref_folders()[&folder_node.hash].name()
accounts[account_pos][&folder_node.hash].name()
);
mailbox_hashes.insert(folder_node.hash, account_pos);
}