diff options
-rw-r--r-- | melib/src/mailbox/backends.rs | 11 | ||||
-rw-r--r-- | melib/src/mailbox/backends/maildir.rs | 18 | ||||
-rw-r--r-- | melib/src/mailbox/backends/maildir/backend.rs | 66 | ||||
-rw-r--r-- | ui/src/components.rs | 1 | ||||
-rw-r--r-- | ui/src/components/mail.rs | 117 | ||||
-rw-r--r-- | ui/src/components/mail/view/thread.rs | 62 | ||||
-rw-r--r-- | ui/src/conf/accounts.rs | 54 | ||||
-rw-r--r-- | ui/src/types.rs | 63 |
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] + } + } +} |