ui: Refer to child/parents with FolderHash in BackendFolder

- use a stack to build folder order list in conf/accounts.rs
- update side menu print
embed
Manos Pitsidianakis 2019-04-26 11:04:30 +03:00
parent 596194fa47
commit 1e44089d84
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
8 changed files with 233 additions and 159 deletions

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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()

View File

@ -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

View File

@ -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]
}
}
}