summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--melib/src/mailbox/backends.rs11
-rw-r--r--melib/src/mailbox/backends/maildir.rs18
-rw-r--r--melib/src/mailbox/backends/maildir/backend.rs66
-rw-r--r--ui/src/components.rs1
-rw-r--r--ui/src/components/mail.rs117
-rw-r--r--ui/src/components/mail/view/thread.rs62
-rw-r--r--ui/src/conf/accounts.rs54
-rw-r--r--ui/src/types.rs63
8 files changed, 233 insertions, 159 deletions
diff --git a/melib/src/mailbox/backends.rs b/melib/src/mailbox/backends.rs
index 738c910c..a8b79211 100644
--- a/melib/src/mailbox/backends.rs
+++ b/melib/src/mailbox/backends.rs
@@ -239,12 +239,13 @@ pub trait BackendFolder: Debug {
fn name(&self) -> &str;
fn change_name(&mut self, new_name: &str);
fn clone(&self) -> Folder;
- fn children(&self) -> &Vec<usize>;
+ fn children(&self) -> &Vec<FolderHash>;
+ fn parent(&self) -> Option<FolderHash>;
}
#[derive(Debug)]
struct DummyFolder {
- v: Vec<usize>,
+ v: Vec<FolderHash>,
}
impl BackendFolder for DummyFolder {
@@ -262,9 +263,13 @@ impl BackendFolder for DummyFolder {
folder_default()
}
- fn children(&self) -> &Vec<usize> {
+ fn children(&self) -> &Vec<FolderHash> {
&self.v
}
+
+ fn parent(&self) -> Option<FolderHash> {
+ None
+ }
}
pub fn folder_default() -> Folder {
diff --git a/melib/src/mailbox/backends/maildir.rs b/melib/src/mailbox/backends/maildir.rs
index 1f82dee4..8b9c210b 100644
--- a/melib/src/mailbox/backends/maildir.rs
+++ b/melib/src/mailbox/backends/maildir.rs
@@ -193,11 +193,17 @@ pub struct MaildirFolder {
hash: FolderHash,
name: String,
path: PathBuf,
- children: Vec<usize>,
+ parent: Option<FolderHash>,
+ children: Vec<FolderHash>,
}
impl MaildirFolder {
- pub fn new(path: String, file_name: String, children: Vec<usize>) -> Result<Self> {
+ pub fn new(
+ path: String,
+ file_name: String,
+ parent: Option<FolderHash>,
+ children: Vec<FolderHash>,
+ ) -> Result<Self> {
let pathbuf = PathBuf::from(path);
let mut h = DefaultHasher::new();
pathbuf.hash(&mut h);
@@ -206,6 +212,7 @@ impl MaildirFolder {
hash: h.finish(),
name: file_name,
path: pathbuf,
+ parent,
children,
};
ret.is_valid()?;
@@ -243,7 +250,7 @@ impl BackendFolder for MaildirFolder {
self.name = s.to_string();
}
- fn children(&self) -> &Vec<usize> {
+ fn children(&self) -> &Vec<FolderHash> {
&self.children
}
@@ -253,6 +260,11 @@ impl BackendFolder for MaildirFolder {
name: self.name.clone(),
path: self.path.clone(),
children: self.children.clone(),
+ parent: self.parent.clone(),
})
}
+
+ fn parent(&self) -> Option<FolderHash> {
+ self.parent.clone()
+ }
}
diff --git a/melib/src/mailbox/backends/maildir/backend.rs b/melib/src/mailbox/backends/maildir/backend.rs
index 9699a8bd..b007d660 100644
--- a/melib/src/mailbox/backends/maildir/backend.rs
+++ b/melib/src/mailbox/backends/maildir/backend.rs
@@ -73,7 +73,7 @@ pub type HashIndexes = Arc<Mutex<FnvHashMap<FolderHash, HashIndex>>>;
#[derive(Debug)]
pub struct MaildirType {
name: String,
- folders: Vec<MaildirFolder>,
+ folders: FnvHashMap<FolderHash, MaildirFolder>,
//folder_index: FnvHashMap<FolderHash, usize>,
hash_indexes: HashIndexes,
path: PathBuf,
@@ -144,7 +144,10 @@ fn move_to_cur(p: PathBuf) -> PathBuf {
impl MailBackend for MaildirType {
fn folders(&self) -> FnvHashMap<FolderHash, Folder> {
- self.folders.iter().map(|f| (f.hash(), f.clone())).collect()
+ self.folders
+ .iter()
+ .map(|(h, f)| (*h, f.clone() as Folder))
+ .collect()
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, folder)
@@ -417,7 +420,7 @@ eprintln!("removed but not contained in index");
}
fn save(&self, bytes: &[u8], folder: &str) -> Result<()> {
- for f in &self.folders {
+ for f in self.folders.values() {
if f.name == folder {
let mut path = f.path.clone();
path.push("cur");
@@ -464,25 +467,34 @@ eprintln!("removed but not contained in index");
impl MaildirType {
pub fn new(f: &AccountSettings) -> Self {
- let mut folders: Vec<MaildirFolder> = Vec::new();
- fn recurse_folders<P: AsRef<Path>>(folders: &mut Vec<MaildirFolder>, p: P) -> Vec<usize> {
+ let name = f.name.clone();
+ let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
+ fn recurse_folders<P: AsRef<Path>>(
+ folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
+ p: P,
+ ) -> Vec<FolderHash> {
let mut children = Vec::new();
for mut f in fs::read_dir(p).unwrap() {
- for f in f.iter_mut() {
+ 'entries: for f in f.iter_mut() {
{
let path = f.path();
if path.ends_with("cur") || path.ends_with("new") || path.ends_with("tmp") {
- continue;
+ continue 'entries;
}
if path.is_dir() {
- let path_children = recurse_folders(folders, &path);
+ let path_children = std::dbg!(recurse_folders(folders, &path));
if let Ok(f) = MaildirFolder::new(
path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(),
+ None,
path_children,
) {
- folders.push(f);
- children.push(folders.len() - 1);
+ f.children
+ .iter()
+ .map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
+ .count();
+ children.push(f.hash);
+ folders.insert(f.hash, f);
}
}
}
@@ -495,18 +507,28 @@ impl MaildirType {
if let Ok(f) = MaildirFolder::new(
path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(),
+ None,
Vec::with_capacity(0),
) {
- if f.is_valid().is_ok() {
- folders.push(f);
- }
+ let l: MaildirFolder = f;
+ folders.insert(l.hash, l);
}
}
if folders.is_empty() {
- recurse_folders(&mut folders, &path);
+ let children = recurse_folders(&mut folders, &path);
+ children
+ .iter()
+ .map(|c| folders.get_mut(c).map(|f| f.parent = None))
+ .count();
} else {
- folders[0].children = recurse_folders(&mut folders, &path);
+ let root_hash = *folders.keys().nth(0).unwrap();
+ let children = recurse_folders(&mut folders, &path);
+ children
+ .iter()
+ .map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
+ .count();
+ folders.get_mut(&root_hash).map(|f| f.children = children);
}
let hash_indexes = Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
@@ -515,12 +537,12 @@ impl MaildirType {
)));
{
let mut hash_indexes = hash_indexes.lock().unwrap();
- for f in &folders {
+ for &fh in folders.keys() {
hash_indexes.insert(
- f.hash(),
+ fh,
HashIndex {
index: FnvHashMap::with_capacity_and_hasher(0, Default::default()),
- hash: f.hash(),
+ hash: fh,
},
);
}
@@ -532,10 +554,10 @@ impl MaildirType {
path: PathBuf::from(f.root_folder()),
}
}
- fn owned_folder_idx(&self, folder: &Folder) -> usize {
- self.folders
+ fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
+ *self
+ .folders
.iter()
- .enumerate()
.find(|(_, f)| f.hash() == folder.hash())
.unwrap()
.0
@@ -548,7 +570,7 @@ impl MaildirType {
let handle = {
let tx = w.tx();
// TODO: Avoid clone
- let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)];
+ let folder: &MaildirFolder = &self.folders[&self.owned_folder_idx(folder)];
let folder_hash = folder.hash();
let tx_final = w.tx();
let path: PathBuf = folder.path().into();
diff --git a/ui/src/components.rs b/ui/src/components.rs
index 96a70b24..1dd9cf64 100644
--- a/ui/src/components.rs
+++ b/ui/src/components.rs
@@ -43,7 +43,6 @@ pub use contacts::*;
use std::fmt;
use std::fmt::{Debug, Display};
-use std::ops::{Deref, DerefMut};
use fnv::FnvHashMap;
use uuid::Uuid;
diff --git a/ui/src/components/mail.rs b/ui/src/components/mail.rs
index f57fcf50..be53b820 100644
--- a/ui/src/components/mail.rs
+++ b/ui/src/components/mail.rs
@@ -23,6 +23,7 @@
*/
use super::*;
use melib::backends::Folder;
+use melib::backends::FolderHash;
pub mod listing;
pub use listing::*;
@@ -91,35 +92,23 @@ impl AccountMenu {
eprintln!("BUG: invalid area in print_account");
}
// Each entry and its index in the account
- let entries: Vec<(usize, Folder)> = {
- let a = &context.accounts[a.index];
- let mut entries = Vec::with_capacity(a.len());
- for (idx, acc) in a.list_folders().into_iter().enumerate() {
- entries.push((idx, acc));
- }
- entries
- };
+ let entries: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
+ .list_folders()
+ .into_iter()
+ .map(|f| (f.hash(), f))
+ .collect();
+ let folders_order: FnvHashMap<FolderHash, usize> = context.accounts[a.index]
+ .folders_order()
+ .iter()
+ .enumerate()
+ .map(|(i, &fh)| (fh, i))
+ .collect();
+
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let highlight = self.cursor.map(|(x, _)| x == a.index).unwrap_or(false);
- let mut parents: Vec<Option<usize>> = vec![None; entries.len()];
-
- for (idx, e) in entries.iter().enumerate() {
- for &c in e.1.children() {
- if c < parents.len() {
- parents[c] = Some(idx);
- }
- }
- }
- let mut roots = Vec::new();
- for (idx, c) in parents.iter().enumerate() {
- if c.is_none() {
- roots.push(idx);
- }
- }
-
let mut inc = 0;
let mut depth = String::from("");
let mut s = format!("{}\n", a.name);
@@ -133,50 +122,72 @@ impl AccountMenu {
}
fn print(
- root: usize,
- parents: &[Option<usize>],
+ folder_idx: FolderHash,
depth: &mut String,
- entries: &[(usize, Folder)],
- s: &mut String,
inc: &mut usize,
+ entries: &FnvHashMap<FolderHash, Folder>,
+ folders_order: &FnvHashMap<FolderHash, usize>,
+ s: &mut String,
index: usize, //account index
context: &mut Context,
) {
- if root >= entries.len() {
- return;
- }
- let len = s.len();
- match context.accounts[index].status(entries[root].1.hash()) {
- Ok(_) => {}
+ match context.accounts[index].status(entries[&folder_idx].hash()) {
+ Ok(_) => {
+ let count = context.accounts[index][entries[&folder_idx].hash()]
+ .as_ref()
+ .unwrap()
+ .collection
+ .values()
+ .filter(|e| !e.is_seen())
+ .count();
+ let len = s.len();
+ s.insert_str(
+ len,
+ &format!("{} {} {}\n ", *inc, &entries[&folder_idx].name(), count),
+ );
+ }
Err(_) => {
- return;
- // TODO: Show progress visually
+ let len = s.len();
+ s.insert_str(
+ len,
+ &format!("{} {} ...\n ", *inc, &entries[&folder_idx].name()),
+ );
}
}
- let count = context.accounts[index][root]
- .as_ref()
- .unwrap()
- .collection
- .values()
- .filter(|e| !e.is_seen())
- .count();
- s.insert_str(
- len,
- &format!("{} {} {}\n ", *inc, &entries[root].1.name(), count),
- );
*inc += 1;
- for child in entries[root].1.children().iter() {
+ let mut children: Vec<FolderHash> = entries[&folder_idx].children().to_vec();
+ children
+ .sort_unstable_by(|a, b| folders_order[a].partial_cmp(&folders_order[b]).unwrap());
+ for child in entries[&folder_idx].children().iter() {
let len = s.len();
s.insert_str(len, &format!("{} ", depth));
push(depth, ' ');
- print(*child, parents, depth, entries, s, inc, index, context);
+ print(
+ *child,
+ depth,
+ inc,
+ entries,
+ folders_order,
+ s,
+ index,
+ context,
+ );
pop(depth);
}
}
- for r in roots {
- print(
- r, &parents, &mut depth, &entries, &mut s, &mut inc, a.index, context,
- );
+ for f in entries.keys() {
+ if entries[f].parent().is_none() {
+ print(
+ *f,
+ &mut depth,
+ &mut inc,
+ &entries,
+ &folders_order,
+ &mut s,
+ a.index,
+ context,
+ );
+ }
}
let lines: Vec<&str> = s.lines().collect();
diff --git a/ui/src/components/mail/view/thread.rs b/ui/src/components/mail/view/thread.rs
index 81e9ca66..2b591dfc 100644
--- a/ui/src/components/mail/view/thread.rs
+++ b/ui/src/components/mail/view/thread.rs
@@ -54,63 +54,6 @@ pub struct ThreadView {
id: ComponentId,
}
-#[derive(Debug)]
-struct StackVec {
- len: usize,
- array: [usize; 8],
- heap_vec: Vec<usize>,
-}
-
-impl StackVec {
- fn new() -> Self {
- StackVec {
- len: 0,
- array: [0, 0, 0, 0, 0, 0, 0, 0],
- heap_vec: Vec::new(),
- }
- }
- fn push(&mut self, ind: usize) {
- if self.len == self.array.len() {
- self.heap_vec.clear();
- self.heap_vec.reserve(16);
- self.heap_vec.copy_from_slice(&self.array);
- self.heap_vec.push(ind);
- } else if self.len > self.array.len() {
- self.heap_vec.push(ind);
- } else {
- self.array[self.len] = ind;
- }
- self.len += 1;
- }
- fn pop(&mut self) -> usize {
- if self.len >= self.array.len() {
- self.heap_vec.pop().unwrap()
- } else {
- let ret = self.array[self.len];
- self.len = self.len.saturating_sub(1);
- ret
- }
- }
- fn len(&self) -> usize {
- self.len
- }
- fn is_empty(&self) -> bool {
- self.len == 0
- }
-}
-
-impl Index<usize> for StackVec {
- type Output = usize;
-
- fn index(&self, idx: usize) -> &usize {
- if self.len >= self.array.len() {
- &self.heap_vec[idx]
- } else {
- &self.array[idx]
- }
- }
-}
-
impl ThreadView {
/*
* coordinates: (account index, mailbox index, root set thread_node index)
@@ -133,7 +76,8 @@ impl ThreadView {
cursor_pos: 1,
new_cursor_pos: 0,
dirty: true,
- id: ComponentId::new_v4(), ..Default::default()
+ id: ComponentId::new_v4(),
+ ..Default::default()
};
view.initiate(expanded_idx, context);
view.new_cursor_pos = view.new_expanded_pos;
@@ -875,7 +819,7 @@ impl Component for ThreadView {
.operation(envelope.hash(), mailbox.folder.hash());
if cfg!(debug_assertions) {
eprint!("{}:{}_{}: ", file!(), line!(), column!());
-eprintln!(
+ eprintln!(
"sending action edit for {}, {}",
envelope.message_id(),
op.description()
diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs
index b7d00dc1..b38bded1 100644
--- a/ui/src/conf/accounts.rs
+++ b/ui/src/conf/accounts.rs
@@ -24,6 +24,7 @@
*/
use super::AccountConf;
+use crate::StackVec;
use fnv::FnvHashMap;
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus};
use melib::backends::FolderHash;
@@ -69,7 +70,7 @@ pub struct Account {
pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf,
- pub(crate) backend: Box<MailBackend>,
+ pub(crate) backend: Box<dyn MailBackend>,
notify_fn: Arc<NotifyFn>,
}
@@ -128,19 +129,10 @@ impl Account {
let mut ref_folders: FnvHashMap<FolderHash, Folder> = backend.folders();
let mut folders: FnvHashMap<FolderHash, Option<Result<Mailbox>>> =
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
- let mut folders_order: Vec<FolderHash> = ref_folders.values().map(|f| f.hash()).collect();
+ let mut folders_order: Vec<FolderHash> = Vec::with_capacity(folders.len());
let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
let notify_fn = Arc::new(notify_fn);
- if let Some(pos) = ref_folders
- .values()
- .position(|f| f.name().eq_ignore_ascii_case("INBOX"))
- {
- folders_order.swap(pos, 0);
- }
- let sent_folder = ref_folders
- .values()
- .position(|x: &Folder| x.name() == settings.account().sent_folder);
if let Some(folder_confs) = settings.conf().folders() {
//if cfg!(debug_assertions) {
//eprint!("{}:{}_{}: ", file!(), line!(), column!());
@@ -154,10 +146,28 @@ impl Account {
}
}
}
- for (h, f) in ref_folders.into_iter() {
- folders.insert(h, None);
- workers.insert(h, Account::new_worker(f, &mut backend, notify_fn.clone()));
+ let mut stack: StackVec<FolderHash> = StackVec::new();
+ for (h, f) in ref_folders.iter() {
+ if f.parent().is_none() {
+ folders_order.push(f.hash());
+ for &c in f.children() {
+ stack.push(c);
+ }
+ while !stack.is_empty() {
+ let next = stack.pop();
+ folders_order.push(next);
+ for c in ref_folders[&next].children() {
+ stack.push(*c);
+ }
+ }
+ }
+ folders.insert(*h, None);
+ workers.insert(
+ *h,
+ Account::new_worker(f.clone(), &mut backend, notify_fn.clone()),
+ );
}
+
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() {
@@ -180,7 +190,7 @@ impl Account {
folders,
folders_order,
address_book,
- sent_folder,
+ sent_folder: None,
workers,
settings: settings.clone(),
runtime_settings: settings,
@@ -295,10 +305,18 @@ impl Account {
folders.swap(pos, 0);
}
*/
- self.folders_order
+ let order: FnvHashMap<FolderHash, usize> = self
+ .folders_order
.iter()
- .map(|ref h| folders.remove(&h).unwrap())
- .collect()
+ .enumerate()
+ .map(|(i, &fh)| (fh, i))
+ .collect();
+ let mut folders: Vec<Folder> = folders.drain().map(|(_, f)| f).collect();
+ folders.sort_unstable_by(|a, b| order[&a.hash()].partial_cmp(&order[&b.hash()]).unwrap());
+ folders
+ }
+ pub fn folders_order(&self) -> &Vec<FolderHash> {
+ &self.folders_order
}
pub fn name(&self) -> &str {
&self.name
diff --git a/ui/src/types.rs b/ui/src/types.rs
index 1cb8378e..2521c346 100644
--- a/ui/src/types.rs
+++ b/ui/src/types.rs
@@ -31,6 +31,7 @@ use melib::EnvelopeHash;
use melib::RefreshEvent;
use std;
use std::fmt;
+use std::ops::Index;
use std::thread;
use uuid::Uuid;
@@ -128,3 +129,65 @@ pub struct Notification {
_timestamp: std::time::Instant,
}
+
+#[derive(Debug)]
+pub(crate) struct StackVec<T: Default + Copy + std::fmt::Debug> {
+ len: usize,
+ array: [T; 8],
+ heap_vec: Vec<T>,
+}
+
+impl<T: Default + Copy + std::fmt::Debug> StackVec<T> {
+ pub(crate) fn new() -> Self {
+ StackVec {
+ len: 0,
+ array: [T::default(); 8],
+ heap_vec: Vec::new(),
+ }
+ }
+ pub(crate) fn push(&mut self, ind: T) {
+ if self.len == self.array.len() {
+ if self.heap_vec.is_empty() {
+ self.heap_vec.reserve(16);
+ for _ in 0..8 {
+ self.heap_vec.push(T::default());
+ }
+ }
+ self.heap_vec[0..8].copy_from_slice(&self.array);
+ self.heap_vec.push(ind);
+ } else if self.len > self.array.len() {
+ self.heap_vec.push(ind);
+ } else {
+ self.array[self.len] = ind;
+ }
+ self.len += 1;
+ }
+ pub(crate) fn pop(&mut self) -> T {
+ if self.len >= self.array.len() {
+ self.len -= 1;
+ self.heap_vec.pop().unwrap()
+ } else {
+ let ret = self.array[self.len - 1];
+ self.len = self.len - 1;
+ ret
+ }
+ }
+ pub(crate) fn len(&self) -> usize {
+ self.len
+ }
+ pub(crate) fn is_empty(&self) -> bool {
+ self.len == 0
+ }
+}
+
+impl<T: Default + Copy + std::fmt::Debug> Index<usize> for StackVec<T> {
+ type Output = T;
+
+ fn index(&self, idx: usize) -> &T {
+ if self.len >= self.array.len() {
+ &self.heap_vec[idx]
+ } else {
+ &self.array[idx]
+ }
+ }
+}