@ -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." ) ) ,
}
}
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." ) ) ,
}
}
#[ derive(Debug) ]
pub struct FolderEntry {
pub status : MailboxStatus ,
pub name : String ,
pub ref_folder : Folder ,
pub conf : FileFolderConf ,
pub worker : Worker ,
}
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 ) ) ,
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 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 , Mailbox Entry> =
let mut folder_entrie s : FnvHashMap < FolderHash , Folder Entry> =
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_nam es = folder_nam es ;
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_entrie s [ & 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_folder s[ & folder_hash ] . name ( ) ,
self . folder_entrie s[ & 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_folder s[ & 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