Browse Source

melib: add per-folder hash indexes in maildir backend

embed
Manos Pitsidianakis 4 years ago
parent
commit
9b58908f6f
Signed by: epilys GPG Key ID: 73627C2F690DF710
  1. 199
      melib/src/mailbox/backends/maildir/backend.rs
  2. 17
      melib/src/mailbox/backends/maildir/mod.rs
  3. 17
      melib/src/mailbox/backends/mod.rs
  4. 54
      melib/src/mailbox/collection.rs
  5. 2
      melib/src/mailbox/email/attachment_types.rs
  6. 8
      melib/src/mailbox/email/mod.rs
  7. 25
      melib/src/mailbox/mod.rs
  8. 98
      melib/src/mailbox/thread.rs
  9. 13
      ui/src/components/mail/compose.rs
  10. 19
      ui/src/components/mail/listing/compact.rs
  11. 6
      ui/src/components/mail/listing/plain.rs
  12. 6
      ui/src/components/mail/listing/thread.rs
  13. 6
      ui/src/components/mail/view/mod.rs
  14. 10
      ui/src/state.rs
  15. 87
      ui/src/types/accounts.rs

199
melib/src/mailbox/backends/maildir/backend.rs

@ -29,7 +29,7 @@ use async::*;
use conf::AccountSettings;
use error::Result;
use mailbox::backends::{
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent, RefreshEventConsumer,
BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
RefreshEventKind::*,
};
use mailbox::email::{Envelope, EnvelopeHash};
@ -49,19 +49,39 @@ use std::ffi::OsStr;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::Read;
use std::ops::{Deref, DerefMut};
use std::path::{Component, Path, PathBuf};
use std::result;
use std::sync::{Arc, Mutex};
type HashIndex = Arc<Mutex<FnvHashMap<EnvelopeHash, (usize, PathBuf)>>>;
#[derive(Debug, Default)]
pub struct HashIndex {
index: FnvHashMap<EnvelopeHash, (usize, PathBuf)>,
hash: FolderHash,
}
impl Deref for HashIndex {
type Target = FnvHashMap<EnvelopeHash, (usize, PathBuf)>;
fn deref(&self) -> &FnvHashMap<EnvelopeHash, (usize, PathBuf)> {
&self.index
}
}
impl DerefMut for HashIndex {
fn deref_mut(&mut self) -> &mut FnvHashMap<EnvelopeHash, (usize, PathBuf)> {
&mut self.index
}
}
pub type HashIndexes = Arc<Mutex<FnvHashMap<FolderHash, HashIndex>>>;
/// Maildir backend https://cr.yp.to/proto/maildir.html
#[derive(Debug)]
pub struct MaildirType {
name: String,
folders: Vec<MaildirFolder>,
hash_index: HashIndex,
//folder_index: FnvHashMap<FolderHash, usize>,
hash_indexes: HashIndexes,
path: PathBuf,
}
@ -80,18 +100,18 @@ macro_rules! path_is_new {
}
macro_rules! get_path_hash {
($path:expr) => {{
if $path.is_dir() {
if $path.ends_with("cur") | $path.ends_with("new") {
$path.pop();
let mut path = $path.clone();
if path.is_dir() {
if path.ends_with("cur") | path.ends_with("new") {
path.pop();
}
} else {
$path.pop();
$path.pop();
path.pop();
path.pop();
};
eprintln!(" got event in {}", $path.display());
let mut hasher = DefaultHasher::new();
$path.hash(&mut hasher);
path.hash(&mut hasher);
hasher.finish()
}};
}
@ -111,7 +131,7 @@ fn get_file_hash(file: &Path) -> EnvelopeHash {
hasher.finish()
}
fn move_to_cur(p: PathBuf) {
fn move_to_cur(p: PathBuf) -> PathBuf {
let mut new = p.clone();
{
let file_name = p.file_name().unwrap();
@ -121,7 +141,8 @@ fn move_to_cur(p: PathBuf) {
new.push("cur");
new.push(file_name);
}
fs::rename(p, new).unwrap();
fs::rename(p, &new).unwrap();
new
}
impl MailBackend for MaildirType {
@ -148,7 +169,7 @@ impl MailBackend for MaildirType {
p.push("new");
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
}
let hash_index = self.hash_index.clone();
let hash_indexes = self.hash_indexes.clone();
thread::Builder::new()
.name("folder watch".to_string())
.spawn(move || {
@ -169,25 +190,26 @@ impl MailBackend for MaildirType {
Ok(event) => match event {
/* Create */
DebouncedEvent::Create(mut pathbuf) => {
if path_is_new!(pathbuf) {
move_to_cur(pathbuf);
continue;
}
let folder_hash = get_path_hash!(pathbuf);
let file_name = pathbuf
.as_path()
.strip_prefix(&root_path)
.unwrap()
.to_path_buf();
if let Some(env) = add_path_to_index(
&hash_index,
&hash_indexes,
folder_hash,
pathbuf.as_path(),
&cache_dir,
file_name,
) {
sender.send(RefreshEvent {
hash: get_path_hash!(pathbuf),
hash: folder_hash,
kind: Create(Box::new(env)),
});
if path_is_new!(pathbuf) {
move_to_cur(pathbuf);
}
} else {
continue;
}
@ -195,6 +217,9 @@ impl MailBackend for MaildirType {
/* Update */
DebouncedEvent::NoticeWrite(mut pathbuf)
| DebouncedEvent::Write(mut pathbuf) => {
let folder_hash = get_path_hash!(pathbuf);
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
let index_lock = &mut hash_indexes_lock.entry(folder_hash).or_default();
let file_name = pathbuf
.as_path()
.strip_prefix(&root_path)
@ -202,7 +227,6 @@ impl MailBackend for MaildirType {
.to_path_buf();
/* Linear search in hash_index to find old hash */
let old_hash: EnvelopeHash = {
let mut index_lock = hash_index.lock().unwrap();
if let Some((k, v)) =
index_lock.iter_mut().find(|(_, v)| v.1 == pathbuf)
{
@ -212,13 +236,14 @@ impl MailBackend for MaildirType {
/* Did we just miss a Create event? In any case, create
* envelope. */
if let Some(env) = add_path_to_index(
&hash_index,
&hash_indexes,
folder_hash,
pathbuf.as_path(),
&cache_dir,
file_name,
) {
sender.send(RefreshEvent {
hash: get_path_hash!(pathbuf),
hash: folder_hash,
kind: Create(Box::new(env)),
});
}
@ -226,25 +251,28 @@ impl MailBackend for MaildirType {
}
};
let new_hash: EnvelopeHash = get_file_hash(pathbuf.as_path());
let mut index_lock = hash_index.lock().unwrap();
if index_lock.get_mut(&new_hash).is_none() {
let op = Box::new(MaildirOp::new(new_hash, hash_index.clone()));
let op = Box::new(MaildirOp::new(new_hash, hash_indexes.clone(), folder_hash));
if let Some(env) = Envelope::from_token(op, new_hash) {
index_lock.insert(new_hash, (0, pathbuf.clone()));
/* Send Write notice */
sender.send(RefreshEvent {
hash: get_path_hash!(pathbuf),
hash: folder_hash,
kind: Update(old_hash, Box::new(env)),
});
} else {
eprintln!("DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`", new_hash, pathbuf.as_path().display());
}
}
}
/* Remove */
DebouncedEvent::NoticeRemove(mut pathbuf)
| DebouncedEvent::Remove(mut pathbuf) => {
let index_lock = hash_index.lock().unwrap();
let folder_hash = get_path_hash!(pathbuf);
let hash_indexes_lock = hash_indexes.lock().unwrap();
let index_lock = &hash_indexes_lock[&folder_hash];
let hash: EnvelopeHash = if let Some((k, _)) =
index_lock.iter().find(|(_, v)| v.1 == pathbuf)
{
@ -254,14 +282,16 @@ impl MailBackend for MaildirType {
};
sender.send(RefreshEvent {
hash: get_path_hash!(pathbuf),
hash: folder_hash,
kind: Remove(hash),
});
}
/* Envelope hasn't changed, so handle this here */
DebouncedEvent::Rename(src, mut dest) => {
DebouncedEvent::Rename(mut src, mut dest) => {
let folder_hash = get_path_hash!(src);
let old_hash: EnvelopeHash = get_file_hash(src.as_path());
let mut index_lock = hash_index.lock().unwrap();
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
let mut index_lock = hash_indexes_lock.entry(folder_hash).or_default();
if let Some(v) = index_lock.get_mut(&old_hash) {
v.1 = dest;
} else {
@ -285,8 +315,8 @@ impl MailBackend for MaildirType {
})?;
Ok(())
}
fn operation(&self, hash: EnvelopeHash) -> Box<BackendOp> {
Box::new(MaildirOp::new(hash, self.hash_index.clone()))
fn operation(&self, hash: EnvelopeHash, folder_hash: FolderHash) -> Box<BackendOp> {
Box::new(MaildirOp::new(hash, self.hash_indexes.clone(), folder_hash))
}
}
@ -329,13 +359,26 @@ impl MaildirType {
}
}
folders[0].children = recurse_folders(&mut folders, &path);
let hash_indexes = Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
folders.len(),
Default::default(),
)));
{
let mut hash_indexes = hash_indexes.lock().unwrap();
for f in &folders {
hash_indexes.insert(
f.hash(),
HashIndex {
index: FnvHashMap::with_capacity_and_hasher(0, Default::default()),
hash: f.hash(),
},
);
}
}
MaildirType {
name: f.name().to_string(),
folders,
hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
0,
Default::default(),
))),
hash_indexes,
path: PathBuf::from(f.root_folder()),
}
}
@ -357,14 +400,20 @@ impl MaildirType {
let mut w = AsyncBuilder::new();
let root_path = self.path.to_path_buf();
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
{
let mut hash_index = self.hash_indexes.lock().unwrap();
let index_lock = hash_index.entry(folder.hash()).or_default();
index_lock.clear();
}
let handle = {
let tx = w.tx();
// TODO: Avoid clone
let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)];
let folder_hash = folder.hash();
let mut path: PathBuf = folder.path().into();
let name = format!("parsing {:?}", folder.name());
let map = self.hash_index.clone();
let map2 = self.hash_index.clone();
let map = self.hash_indexes.clone();
let map2 = self.hash_indexes.clone();
thread::Builder::new()
.name(name.clone())
@ -412,10 +461,10 @@ impl MaildirType {
Envelope,
> = Vec::with_capacity(chunk.len());
for c in chunk.chunks(size) {
//thread::yield_now();
let map = map.clone();
let len = c.len();
for file in c {
//thread::yield_now();
/* Check if we have a cache file with this email's
* filename */
@ -423,7 +472,7 @@ impl MaildirType {
.strip_prefix(&root_path)
.unwrap()
.to_path_buf();
let hash = if let Some(cached) =
if let Some(cached) =
cache_dir.find_cache_file(&file_name)
{
/* Cached struct exists, try to load it */
@ -433,43 +482,25 @@ impl MaildirType {
let result: result::Result<Envelope, _> = bincode::deserialize_from(reader);
if let Ok(env) = result {
let mut map = map.lock().unwrap();
let mut map = map.entry(folder_hash).or_default();;
let hash = env.hash();
if (*map).contains_key(&hash) {
continue;
}
(*map).insert(hash, (0, file.clone()));
map.insert(hash, (0, file.clone()));
local_r.push(env);
continue;
}
let mut reader = io::BufReader::new(
fs::File::open(cached).unwrap(),
);
let mut buf = Vec::with_capacity(2048);
reader.read_to_end(&mut buf).unwrap_or_else(|_| {
panic!("Can't read {}", file.display())
});
let mut hasher = FnvHasher::default();
hasher.write(&buf);
hasher.finish()
} else {
get_file_hash(file)
};
let hash = get_file_hash(file);
{
{
let mut map = map.lock().unwrap();
if (*map).contains_key(&hash) {
continue;
}
(*map).insert(hash, (0, PathBuf::from(file)));
}
let op =
Box::new(MaildirOp::new(hash, map.clone()));
if let Some(mut e) = Envelope::from_token(op, hash)
{
if let Ok(cached) =
cache_dir.place_cache_file(file_name)
let mut map = map.lock().unwrap();
let mut map = map.entry(folder_hash).or_default();
(*map).insert(hash, (0, PathBuf::from(file)));
}
let op =
Box::new(MaildirOp::new(hash, map.clone(), folder_hash));
if let Some(mut e) = Envelope::from_token(op, hash)
{
if let Ok(cached) =
cache_dir.place_cache_file(file_name)
{
/* place result in cache directory */
let f = match fs::File::create(cached) {
@ -482,10 +513,10 @@ impl MaildirType {
bincode::serialize_into(writer, &e)
.unwrap();
}
local_r.push(e);
} else {
continue;
}
local_r.push(e);
} else {
eprintln!("DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`", hash, file.as_path().display());
continue;
}
}
tx.send(AsyncStatus::ProgressReport(len));
@ -501,6 +532,7 @@ impl MaildirType {
r.append(&mut result);
}
let mut map = map2.lock().unwrap();
let map = map.entry(folder_hash).or_default();
for (idx, e) in r.iter().enumerate() {
let mut y = (*map)[&e.hash()].clone();
y.0 = idx;
@ -517,7 +549,8 @@ impl MaildirType {
}
fn add_path_to_index(
hash_index: &HashIndex,
hash_index: &HashIndexes,
folder_hash: FolderHash,
path: &Path,
cache_dir: &xdg::BaseDirectories,
file_name: PathBuf,
@ -525,13 +558,14 @@ fn add_path_to_index(
let env: Envelope;
let hash = get_file_hash(path);
{
let mut index_lock = hash_index.lock().unwrap();
if (*index_lock).contains_key(&hash) {
let mut hash_index = hash_index.lock().unwrap();
let index_lock = hash_index.entry(folder_hash).or_default();
if index_lock.contains_key(&hash) {
return None;
}
(*index_lock).insert(hash, (0, path.to_path_buf()));
index_lock.insert(hash, (0, path.to_path_buf()));
}
let op = Box::new(MaildirOp::new(hash, hash_index.clone()));
let op = Box::new(MaildirOp::new(hash, hash_index.clone(), folder_hash));
if let Some(e) = Envelope::from_token(op, hash) {
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
/* place result in cache directory */
@ -546,6 +580,11 @@ fn add_path_to_index(
}
env = e;
} else {
eprintln!(
"DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`",
hash,
path.display()
);
return None;
}
Some(env)

17
melib/src/mailbox/backends/maildir/mod.rs

@ -20,7 +20,6 @@
*/
extern crate fnv;
use self::fnv::FnvHashMap;
mod backend;
pub use self::backend::*;
@ -35,12 +34,12 @@ use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
/// `BackendOp` implementor for Maildir
#[derive(Debug)]
pub struct MaildirOp {
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (usize, PathBuf)>>>,
hash_index: HashIndexes,
folder_hash: FolderHash,
hash: EnvelopeHash,
slice: Option<Mmap>,
}
@ -49,6 +48,7 @@ impl Clone for MaildirOp {
fn clone(&self) -> Self {
MaildirOp {
hash_index: self.hash_index.clone(),
folder_hash: self.folder_hash,
hash: self.hash,
slice: None,
}
@ -56,19 +56,17 @@ impl Clone for MaildirOp {
}
impl MaildirOp {
pub fn new(
hash: EnvelopeHash,
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (usize, PathBuf)>>>,
) -> Self {
pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, folder_hash: FolderHash) -> Self {
MaildirOp {
hash_index,
folder_hash,
hash,
slice: None,
}
}
fn path(&self) -> PathBuf {
let hash_index = self.hash_index.clone();
let map = hash_index.lock().unwrap();
let map = self.hash_index.lock().unwrap();
let map = &map[&self.folder_hash];
map.get(&self.hash).unwrap().1.clone()
}
}
@ -154,6 +152,7 @@ impl<'a> BackendOp for MaildirOp {
let hash = envelope.hash();
let hash_index = self.hash_index.clone();
let mut map = hash_index.lock().unwrap();
let map = map.entry(self.folder_hash).or_default();
map.get_mut(&hash).unwrap().1 = PathBuf::from(new_name);
Ok(())
}

17
melib/src/mailbox/backends/mod.rs

@ -31,6 +31,7 @@ use mailbox::backends::maildir::MaildirType;
use mailbox::email::{Envelope, EnvelopeHash, Flag};
use std::fmt;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Arc;
extern crate fnv;
@ -147,7 +148,7 @@ pub trait MailBackend: ::std::fmt::Debug {
fn get(&mut self, folder: &Folder, notify_fn: Arc<NotifyFn>) -> Async<Result<Vec<Envelope>>>;
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
fn folders(&self) -> Vec<Folder>;
fn operation(&self, hash: EnvelopeHash) -> Box<BackendOp>;
fn operation(&self, hash: EnvelopeHash, folder_hash: FolderHash) -> Box<BackendOp>;
//login function
}
@ -265,4 +266,16 @@ pub fn folder_default() -> Folder {
}
pub type FolderHash = u64;
pub type Folder = Box<BackendFolder + Send>;
pub type Folder = Box<dyn BackendFolder + Send>;
impl Clone for Folder {
fn clone(&self) -> Self {
BackendFolder::clone(self.deref())
}
}
impl Default for Folder {
fn default() -> Self {
folder_default()
}
}

54
melib/src/mailbox/collection.rs

@ -14,12 +14,32 @@ use self::fnv::FnvHashMap;
/// `Mailbox` represents a folder of mail.
#[derive(Debug, Clone, Default)]
pub struct Collection {
folder: Folder,
pub envelopes: FnvHashMap<EnvelopeHash, Envelope>,
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
pub threads: Threads,
}
impl Drop for Collection {
fn drop(&mut self) {
let cache_dir =
xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", self.folder.hash()))
.unwrap();
if let Ok(cached) = cache_dir.place_cache_file("threads") {
/* place result in cache directory */
let f = match fs::File::create(cached) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &self.threads).unwrap();
}
}
}
impl Collection {
pub fn new(vec: Vec<Envelope>, folder: &Folder) -> Collection {
let mut envelopes: FnvHashMap<EnvelopeHash, Envelope> =
@ -36,26 +56,26 @@ impl Collection {
let threads = if let Some(cached) = cache_dir.find_cache_file("threads") {
let reader = io::BufReader::new(fs::File::open(cached).unwrap());
let result: result::Result<Threads, _> = bincode::deserialize_from(reader);
if let Ok(mut cached_t) = result {
let ret = if let Ok(mut cached_t) = result {
cached_t.update(&mut envelopes);
cached_t
} else {
let ret = Threads::new(&mut envelopes); // sent_folder);
if let Ok(cached) = cache_dir.place_cache_file("threads") {
/* place result in cache directory */
let f = match fs::File::create(cached) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &ret).unwrap();
}
ret
Threads::new(&mut envelopes)
};
if let Ok(cached) = cache_dir.place_cache_file("threads") {
/* place result in cache directory */
let f = match fs::File::create(cached) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &ret).unwrap();
}
ret
} else {
let ret = Threads::new(&mut envelopes); // sent_folder);
let ret = Threads::new(&mut envelopes);
if let Ok(cached) = cache_dir.place_cache_file("threads") {
/* place result in cache directory */
let f = match fs::File::create(cached) {
@ -70,6 +90,7 @@ impl Collection {
ret
};
Collection {
folder: folder.clone(),
envelopes,
date_index,
subject_index,
@ -94,7 +115,8 @@ impl Collection {
}
}
pub(crate) fn insert_reply(&mut self, envelope: Envelope) {
self.threads.insert_reply(envelope, &mut self.envelopes);
self.insert(envelope);
//self.threads.insert_reply(envelope, &mut self.envelopes);
}
}

2
melib/src/mailbox/email/attachment_types.rs

@ -58,7 +58,7 @@ impl<'a> From<&'a [u8]> for Charset {
b"utf-16" | b"UTF-16" => Charset::UTF16,
b"iso-8859-1" | b"ISO-8859-1" => Charset::ISO8859_1,
b"iso-8859-2" | b"ISO-8859-2" => Charset::ISO8859_2,
b"iso-8859-7" | b"ISO-8859-7" => Charset::ISO8859_7,
b"iso-8859-7" | b"ISO-8859-7" | b"iso8859-7" => Charset::ISO8859_7,
b"iso-8859-15" | b"ISO-8859-15" => Charset::ISO8859_15,
b"windows-1251" | b"Windows-1251" => Charset::Windows1251,
b"windows-1252" | b"Windows-1252" => Charset::Windows1252,

8
melib/src/mailbox/email/mod.rs

@ -209,12 +209,18 @@ impl fmt::Debug for MessageID {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
struct References {
raw: Vec<u8>,
refs: Vec<MessageID>,
}
impl fmt::Debug for References {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#?}", self.refs)
}
}
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct Flag: u8 {

25
melib/src/mailbox/mod.rs

@ -30,7 +30,7 @@ pub use self::email::*;
/* Mail backends. Currently only maildir is supported */
pub mod backends;
use error::Result;
use mailbox::backends::{folder_default, Folder};
use mailbox::backends::Folder;
pub mod thread;
pub use mailbox::thread::{SortField, SortOrder, ThreadNode, Threads};
@ -40,7 +40,7 @@ pub use self::collection::*;
use std::option::Option;
/// `Mailbox` represents a folder of mail.
#[derive(Debug)]
#[derive(Debug, Clone, Default)]
pub struct Mailbox {
pub folder: Folder,
name: String,
@ -48,27 +48,6 @@ pub struct Mailbox {
has_sent: bool,
}
impl Clone for Mailbox {
fn clone(&self) -> Self {
Mailbox {
folder: self.folder.clone(),
collection: self.collection.clone(),
has_sent: self.has_sent,
name: self.name.clone(),
}
}
}
impl Default for Mailbox {
fn default() -> Self {
Mailbox {
folder: folder_default(),
collection: Collection::default(),
has_sent: false,
name: String::new(),
}
}
}
impl Mailbox {
pub fn new(folder: Folder, envelopes: Result<Vec<Envelope>>) -> Result<Mailbox> {
let mut envelopes: Vec<Envelope> = envelopes?;

98
melib/src/mailbox/thread.rs

@ -719,6 +719,21 @@ impl Threads {
}
}
pub fn update_envelope(&mut self, old_hash: EnvelopeHash, envelope: &Envelope) {
/* must update:
* - hash_set
* - message fields in thread_nodes
*/
self.hash_set.remove(&old_hash);
self.hash_set.insert(envelope.hash());
let node = self
.thread_nodes
.iter_mut()
.find(|n| n.message.map(|n| n == old_hash).unwrap_or(false))
.unwrap();
node.message = Some(envelope.hash());
}
pub fn update(&mut self, collection: &mut FnvHashMap<EnvelopeHash, Envelope>) {
let new_hash_set = FnvHashSet::from_iter(collection.keys().cloned());
@ -732,6 +747,18 @@ impl Threads {
self.insert(&mut (*env), collection);
}
}
let difference: Vec<EnvelopeHash> =
self.hash_set.difference(&new_hash_set).cloned().collect();
for h in difference {
self.hash_set.remove(&h);
let node = self
.thread_nodes
.iter_mut()
.find(|n| n.message.map(|n| n == h).unwrap_or(false))
.unwrap();
node.message = None;
}
}
pub fn insert(
@ -787,29 +814,34 @@ impl Threads {
stack.push(node_idx);
}
/* Trace path from root ThreadTree to the envelope's parent */
let mut tree = self.tree.get_mut();
for &s in stack.iter().rev() {
/* Borrow checker is being a tad silly here, so the following
* is basically this:
*
* let tree = &mut tree[s].children;
*/
let temp_tree = tree;
if let Some(pos) = temp_tree.iter().position(|v| v.id == s) {
tree = &mut temp_tree[pos].children;
} else {
let tree_node = ThreadTree::new(s);
temp_tree.push(tree_node);
let new_id = temp_tree.len() - 1;
tree = &mut temp_tree[new_id].children;
{
/* Trace path from root ThreadTree to the envelope's parent */
let mut tree = self.tree.get_mut();
for &s in stack.iter().rev() {
/* Borrow checker is being a tad silly here, so the following
* is basically this:
*
* let tree = &mut tree[s].children;
*/
let temp_tree = tree;
if let Some(pos) = temp_tree.iter().position(|v| v.id == s) {
tree = &mut temp_tree[pos].children;
} else {
let tree_node = ThreadTree::new(s);
temp_tree.push(tree_node);
let new_id = temp_tree.len() - 1;
tree = &mut temp_tree[new_id].children;
}
}
/* Add new child */
let tree_node = ThreadTree::new(id);
tree.push(tree_node);
let new_id = tree.len() - 1;
node_build(&mut tree[new_id], id, &mut self.thread_nodes, 1, collection);
}
/* Add new child */
let tree_node = ThreadTree::new(id);
tree.push(tree_node);
let new_id = tree.len() - 1;
node_build(&mut tree[new_id], id, &mut self.thread_nodes, 1, collection);
// FIXME: use insertion according to self.sort etc instead of sorting everytime
self.inner_sort_by(*self.sort.borrow(), collection);
self.inner_subsort_by(*self.subsort.borrow(), collection);
}
/*
@ -1073,26 +1105,6 @@ impl Index<usize> for Threads {
.expect("thread index out of bounds")
}
}
/*
*
*
/* Update thread's date */
let mut parent_iter = parent_id;
'date: loop {
let p: &mut ThreadNode = &mut self.thread_nodes[parent_iter];
if p.date < envelope.date() {
p.date = envelope.date();
}
if let Some(p) = p.parent {
parent_iter = p;
} else {
break 'date;
}
}
ref_ptr = parent_id;
*
*
* */
fn node_build(
tree: &mut ThreadTree,
@ -1104,6 +1116,10 @@ fn node_build(
if let Some(hash) = thread_nodes[idx].message {
if let Some(parent_id) = thread_nodes[idx].parent {
if let Some(parent_hash) = thread_nodes[parent_id].message {
/* decide if the subject should be shown in the UI.
* If parent subject is Foobar and reply is `Re: Foobar`
* then showing the reply's subject can be reduntant
*/
let mut subject = collection[&hash].subject();
let mut subject = subject.to_mut().as_bytes();
let subject = subject.strip_prefixes();

13
ui/src/components/mail/compose.rs

@ -114,7 +114,7 @@ impl Composer {
let parent_message = &mailbox.collection[&p.message().unwrap()];
let mut op = context.accounts[coordinates.0]
.backend
.operation(parent_message.hash());
.operation(parent_message.hash(), mailbox.folder.hash());
let parent_bytes = op.as_bytes();
ret.draft = Draft::new_reply(parent_message, parent_bytes.unwrap());
@ -456,12 +456,11 @@ impl Component for Composer {
}
fn is_dirty(&self) -> bool {
self.dirty || self.pager.is_dirty()
|| self
.reply_context
.as_ref()
.map(|(_, p)| p.is_dirty())
.unwrap_or(false)
self.dirty || self.pager.is_dirty() || self
.reply_context
.as_ref()
.map(|(_, p)| p.is_dirty())
.unwrap_or(false)
}
fn set_dirty(&mut self) {

19
ui/src/components/mail/listing/compact.rs

@ -113,7 +113,6 @@ impl CompactListing {
});
// Get mailbox as a reference.
//
// TODO: Show progress visually
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
Ok(_) => {}
Err(_) => {
@ -160,6 +159,17 @@ impl CompactListing {
}
threads.thread_nodes()[iter_ptr].message().unwrap()
};
if !mailbox.collection.contains_key(&i) {
eprintln!("key = {}", i);
eprintln!(
"name = {} {}",
mailbox.name(),
context.accounts[self.cursor_pos.0].name()
);
eprintln!("{:#?}", context.accounts);
panic!();
}
let root_envelope: &Envelope = &mailbox.collection[&i];
let fg_color = if thread_node.has_unseen() {
Color::Byte(0)
@ -292,6 +302,10 @@ impl CompactListing {
} else if self.cursor_pos != self.new_cursor_pos {
self.cursor_pos = self.new_cursor_pos;
}
if self.new_cursor_pos.2 >= self.length {
self.new_cursor_pos.2 = self.length - 1;
self.cursor_pos.2 = self.new_cursor_pos.2;
}
/* Page_no has changed, so draw new page */
copy_area(
@ -441,12 +455,13 @@ impl Component for CompactListing {
return true;
}
UIEventType::RefreshMailbox(_) => {
self.dirty = true;
self.view = None;
self.dirty = true;
}
UIEventType::MailboxUpdate((ref idxa, ref idxf)) => {
if *idxa == self.new_cursor_pos.0 && *idxf == self.new_cursor_pos.1 {
self.refresh_mailbox(context);
self.set_dirty();
}
}
UIEventType::ChangeMode(UIMode::Normal) => {

6
ui/src/components/mail/listing/plain.rs

@ -342,9 +342,13 @@ impl Component for PlainListing {
(envelope.hash(), envelope.is_seen())
};
if !is_seen {
let folder_hash = {
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
mailbox.folder.hash()
};
let op = {
let backend = &account.backend;
backend.operation(hash)
backend.operation(hash, folder_hash)
};
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = &mut mailbox

6
ui/src/components/mail/listing/thread.rs

@ -415,9 +415,13 @@ impl Component for ThreadListing {
(envelope.hash(), envelope.is_seen())
};
if !is_seen {
let folder_hash = {
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
mailbox.folder.hash()
};
let op = {
let backend = &account.backend;
backend.operation(hash)
backend.operation(hash, folder_hash)
};
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = mailbox.thread_to_mail_mut(idx);

6
ui/src/components/mail/view/mod.rs

@ -305,7 +305,7 @@ impl Component for MailView {
let envelope: &Envelope = &mailbox.collection[&mailbox_idx.2];
let op = context.accounts[mailbox_idx.0]
.backend
.operation(envelope.hash());
.operation(envelope.hash(), mailbox.folder.hash());
let body = envelope.body(op);
match self.mode {
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
@ -422,7 +422,7 @@ impl Component for MailView {
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash());
.operation(envelope.hash(), mailbox.folder.hash());
if let Some(u) = envelope.body(op).attachments().get(lidx) {
match u.content_type() {
ContentType::MessageRfc822 => {
@ -518,7 +518,7 @@ impl Component for MailView {
let finder = LinkFinder::new();
let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash());
.operation(envelope.hash(), mailbox.folder.hash());
let mut t = envelope.body(op).text().to_string();
let links: Vec<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) {

10
ui/src/state.rs

@ -64,8 +64,7 @@ impl InputHandler {
},
&rx,
)
})
.unwrap();
}).unwrap();
}
fn kill(&self) {
self.tx.send(false);
@ -187,8 +186,7 @@ impl State {
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck))
})),
)
})
.collect();
}).collect();
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
let mut s = State {
cols,
@ -258,6 +256,10 @@ impl State {
id: 0,
event_type: notification,
});
self.context.replies.push_back(UIEvent {
id: 0,
event_type: UIEventType::MailboxUpdate((idxa, idxm)),
});
}
} else {
eprintln!(

87
ui/src/types/accounts.rs

@ -30,6 +30,7 @@ use mailbox::backends::{
};
use mailbox::*;
use melib::error::Result;
use std::mem;
use std::ops::{Index, IndexMut};
use std::result;
use std::sync::Arc;
@ -102,45 +103,49 @@ impl Account {
tx.send(AsyncStatus::Finished);
notify_fn.notify();
ret
})
.unwrap(),
}).unwrap(),
),
)
}
pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option<UIEventType> {
let kind = event.kind();
let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
match kind {
RefreshEventKind::Update(old_hash, envelope) => {
mailbox.update(old_hash, *envelope);
}
RefreshEventKind::Create(envelope) => {
let env: &Envelope = mailbox.insert(*envelope);
let ref_folders: Vec<Folder> = self.backend.folders();
return Some(Notification(
Some("new mail".into()),
format!(
"{:.15}:\nSubject: {:.15}\nFrom: {:.15}",
ref_folders[idx].name(),
env.subject(),
env.field_from_to_string()
),
));
}
RefreshEventKind::Remove(envelope_hash) => {
mailbox.remove(envelope_hash);
}
RefreshEventKind::Rescan => {
let ref_folders: Vec<Folder> = self.backend.folders();
let handle = Account::new_worker(
&self.name,
ref_folders[idx].clone(),
&mut self.backend,
self.notify_fn.clone(),
);
self.workers[idx] = handle;
{
let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
match kind {
RefreshEventKind::Update(old_hash, envelope) => {
mailbox.update(old_hash, *envelope);
}
RefreshEventKind::Create(envelope) => {
let env: &Envelope = mailbox.insert(*envelope);
let ref_folders: Vec<Folder> = self.backend.folders();
return Some(Notification(
Some("new mail".into()),
format!(
"{:.15}:\nSubject: {:.15}\nFrom: {:.15}",
ref_folders[idx].name(),
env.subject(),
env.field_from_to_string()
),
));
}
RefreshEventKind::Remove(envelope_hash) => {
mailbox.remove(envelope_hash);
}
RefreshEventKind::Rescan => {
let ref_folders: Vec<Folder> = self.backend.folders();
let handle = Account::new_worker(
&self.name,
ref_folders[idx].clone(),
&mut self.backend,
self.notify_fn.clone(),
);
self.workers[idx] = handle;
}
}
}
if self.workers[idx].is_some() {
self.folders[idx] = None;
}
None
}
pub fn watch(&self, r: RefreshEventConsumer) -> () {
@ -204,17 +209,15 @@ impl Account {
use std::slice::from_raw_parts_mut;
(
from_raw_parts_mut(ptr.offset(*sent_index as isize), *sent_index + 1)
[0]
.as_mut()
.unwrap()
.as_mut()
.unwrap(),
[0].as_mut()
.unwrap()
.as_mut()
.unwrap(),
from_raw_parts_mut(ptr.offset(folder_index as isize), folder_index + 1)
[0]
.as_mut()
.unwrap()
.as_mut()
.unwrap(),
[0].as_mut()
.unwrap()
.as_mut()
.unwrap(),
)
}
};

Loading…
Cancel
Save