Replace every use of Folder with Mailbox

Use Mailbox for consistency.
async
Manos Pitsidianakis 2020-02-26 10:54:10 +02:00
parent 1245eae0be
commit 4ac52d9d5b
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
40 changed files with 1237 additions and 1205 deletions

30
meli.1
View File

@ -94,7 +94,7 @@ The menu's visibility may be toggled with
.Ic toggle_menu_visibility Ns
).
.Pp
The view into each folder has 4 modes: plain, threaded, conversations and compact.
The view into each mailbox has 4 modes: plain, threaded, conversations and compact.
Plain views each mail indvidually, threaded shows their thread relationship visually, and conversations includes one entry per thread of emails (compact is one row per thread).
.Pp
If you're using a light color palette in your terminal, you may set
@ -224,9 +224,9 @@ To save your draft without sending it, issue command
.Cm close
and select 'save as draft'.
.Pp
With no Draft or Sent folder,
With no Draft or Sent mailbox,
.Nm
tries first saving mail in your INBOX and then at any other folder.
tries first saving mail in your INBOX and then at any other mailbox.
On complete failure to save your draft or sent message it will be saved in your
.Em tmp
directory instead and you will be notified of its location.
@ -307,17 +307,17 @@ filter mailbox with
key.
Escape exits filter results
.It Cm set read, set unread
.It Cm create-folder Ar ACCOUNT Ar FOLDER_PATH
create folder with given path.
.It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH
create mailbox with given path.
Be careful with backends and separator sensitivity (eg IMAP)
.It Cm subscribe-folder Ar ACCOUNT Ar FOLDER_PATH
subscribe to folder with given path
.It Cm unsubscribe-folder Ar ACCOUNT Ar FOLDER_PATH
unsubscribe to folder with given path
.It Cm rename-folder Ar ACCOUNT Ar FOLDER_PATH_SRC Ar FOLDER_PATH_DEST
rename folder
.It Cm delete-folder Ar ACCOUNT Ar FOLDER_PATH
delete folder
.It Cm subscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
subscribe to mailbox with given path
.It Cm unsubscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
unsubscribe to mailbox with given path
.It Cm rename-mailbox Ar ACCOUNT Ar MAILBOX_PATH_SRC Ar MAILBOX_PATH_DEST
rename mailbox
.It Cm delete-mailbox Ar ACCOUNT Ar MAILBOX_PATH
delete mailbox
.El
.Pp
envelope view commands:
@ -376,9 +376,9 @@ Non-complete list of shortcuts and their default values.
PageUp,
.It Ic next_page
PageDown
.It Ic prev_folder
.It Ic prev_mailbox
\&'K'
.It Ic next_folder
.It Ic next_mailbox
\&'J'
.It Ic prev_account
\&'l'

View File

@ -56,23 +56,23 @@ example configuration
.Bd -literal
# Setting up a Maildir account
[accounts.account-name]
root_folder = "/path/to/root/folder"
root_mailbox = "/path/to/root/folder"
format = "Maildir"
index_style = "Compact"
identity="email@address.tld"
subscribed_folders = ["folder", "folder/Sent"] # or [ "*", ] for all folders
subscribed_mailboxes = ["folder", "folder/Sent"] # or [ "*", ] for all mailboxes
display_name = "Name"
# Set folder-specific settings
[accounts.account-name.folders]
# Set mailbox-specific settings
[accounts.account-name.mailboxes]
"INBOX" = { alias="Inbox" } #inline table
"drafts" = { alias="Drafts" } #inline table
[accounts.account-name.folders."foobar-devel"] # or a regular table
ignore = true # don't show notifications for this folder
[accounts.account-name.mailboxes."foobar-devel"] # or a regular table
ignore = true # don't show notifications for this mailbox
# Setting up an mbox account
[accounts.mbox]
root_folder = "/var/mail/username"
root_mailbox = "/var/mail/username"
format = "mbox"
index_style = "Compact"
identity="username@hostname.local"
@ -105,16 +105,16 @@ available options are listed below.
.Sy default values are shown in parentheses.
.Sh ACCOUNTS
.Bl -tag -width 36n
.It Ic root_folder Ar String
the backend-specific path of the root_folder, usually INBOX.
.It Ic root_mailbox Ar String
the backend-specific path of the root_mailbox, usually INBOX.
.It Ic format Ar String Op maildir mbox imap notmuch jmap
the format of the mail backend.
.It Ic subscribed_folders Ar [String,]
an array of folder paths to display in the UI.
Paths are relative to the root folder (eg "INBOX/Sent", not "Sent").
.It Ic subscribed_mailboxes Ar [String,]
an array of mailbox paths to display in the UI.
Paths are relative to the root mailbox (eg "INBOX/Sent", not "Sent").
The glob wildcard
.Em \&*
can be used to match every folder name and path.
can be used to match every mailbox name and path.
.It Ic identity Ar String
your e-mail address that is inserted in the From: headers of outgoing mail
.It Ic index_style Ar String
@ -148,20 +148,20 @@ Available options are 'none' and 'sqlite3'
.It Ic vcard_folder Ar String
(optional) Folder that contains .vcf files.
They are parsed and imported read-only.
.It Ic folders Ar folder_config
(optional) configuration for each folder.
.It Ic mailboxes Ar mailbox
(optional) configuration for each mailbox.
Its format is described below in
.Sx FOLDERS Ns
.Sx mailboxes Ns
\&.
.El
.Sh notmuch only
.Ic root_folder
.Ic root_mailbox
points to the directory which contains the
.Pa .notmuch/
subdirectory.
notmuch folders are virtual, since they are defined by user-given notmuch queries.
Thus you have to explicitly state the folders you want in the
.Ic folders
notmuch mailboxes are virtual, since they are defined by user-given notmuch queries.
Thus you have to explicitly state the mailboxes you want in the
.Ic mailboxes
field and set the
.Ar query
property to each of them.
@ -170,7 +170,7 @@ Example:
[accounts.notmuch]
format = "notmuch"
\&...
[accounts.notmuch.folders]
[accounts.notmuch.mailboxes]
"INBOX" = { query="tag:inbox", subscribe = true }
"Drafts" = { query="tag:draft", subscribe = true }
"Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
@ -213,22 +213,22 @@ example:
.\" default value
.Pq Em false
.El
.Sh FOLDERS
.Sh mailboxes
.Bl -tag -width 36n
.It Ic alias Ar String
(optional) show a different name for this folder in the UI
(optional) show a different name for this mailbox in the UI
.It Ic autoload Ar boolean
(optional) load this folder on startup (not functional yet)
(optional) load this mailbox on startup (not functional yet)
.It Ic subscribe Ar boolean
(optional) watch this folder for updates
(optional) watch this mailbox for updates
.\" default value
.Pq Em true
.It Ic ignore Ar boolean
(optional) silently insert updates for this folder, if any
(optional) silently insert updates for this mailbox, if any
.\" default value
.Pq Em false
.It Ic usage Ar boolean
(optional) special usage of this folder.
(optional) special usage of this mailbox.
Valid values are:
.Bl -bullet -compact
.It
@ -248,9 +248,9 @@ Valid values are:
.It
.Ar Trash
.El
otherwise usage is inferred from the folder title.
otherwise usage is inferred from the mailbox title.
.It Ic conf_override Ar boolean
(optional) override global settings for this folder.
(optional) override global settings for this mailbox.
Available sections to override are
.Em pager, notifications, shortcuts, composing
and the account options
@ -258,9 +258,9 @@ and the account options
\&.
Example:
.Bd -literal
[accounts."imap.domain.tld".folders."INBOX"]
[accounts."imap.domain.tld".mailboxes."INBOX"]
index_style = "plain"
[accounts."imap.domain.tld".folders."INBOX".pager]
[accounts."imap.domain.tld".mailboxes."INBOX".pager]
filter = ""
.Ed
.El
@ -347,12 +347,12 @@ Go to previous page.
Go to next page.
.\" default value
.Pq Em PageDown
.It Ic prev_folder
Go to previous folder.
.It Ic prev_mailbox
Go to previous mailbox.
.\" default value
.Pq Em K
.It Ic next_folder
Go to next folder.
.It Ic next_mailbox
Go to next mailbox.
.\" default value
.Pq Em J
.It Ic prev_account
@ -372,7 +372,7 @@ Set thread as seen.
.\" default value
.Pq Em n
.It Ic refresh
Manually request a folder refresh.
Manually request a mailbox refresh.
.\" default value
.Pq Em F5
.It Ic search
@ -622,8 +622,8 @@ example configuration:
colors = { signed="#Ff6600", replied="DeepSkyBlue4", draft="#f00", replied="8" }
[accounts.dummy]
\&...
[accounts.dummy.folders]
# per folder override:
[accounts.dummy.mailboxes]
# per mailbox override:
"INBOX" = { tags.ignore_tags=["inbox", ] }
.Ed
.Sh PGP

View File

@ -193,12 +193,12 @@ pub enum RefreshEventKind {
#[derive(Debug)]
pub struct RefreshEvent {
hash: FolderHash,
hash: MailboxHash,
kind: RefreshEventKind,
}
impl RefreshEvent {
pub fn hash(&self) -> FolderHash {
pub fn hash(&self) -> MailboxHash {
self.hash
}
pub fn kind(self) -> RefreshEventKind {
@ -220,7 +220,7 @@ impl RefreshEventConsumer {
}
}
pub struct NotifyFn(Box<dyn Fn(FolderHash) -> () + Send + Sync>);
pub struct NotifyFn(Box<dyn Fn(MailboxHash) -> () + Send + Sync>);
impl fmt::Debug for NotifyFn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -228,17 +228,17 @@ impl fmt::Debug for NotifyFn {
}
}
impl From<Box<dyn Fn(FolderHash) -> () + Send + Sync>> for NotifyFn {
fn from(kind: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
impl From<Box<dyn Fn(MailboxHash) -> () + Send + Sync>> for NotifyFn {
fn from(kind: Box<dyn Fn(MailboxHash) -> () + Send + Sync>) -> Self {
NotifyFn(kind)
}
}
impl NotifyFn {
pub fn new(b: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
pub fn new(b: Box<dyn Fn(MailboxHash) -> () + Send + Sync>) -> Self {
NotifyFn(b)
}
pub fn notify(&self, f: FolderHash) {
pub fn notify(&self, f: MailboxHash) {
self.0(f);
}
}
@ -246,10 +246,10 @@ impl NotifyFn {
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
fn is_online(&self) -> Result<()>;
fn connect(&mut self) {}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>>;
fn refresh(
&mut self,
_folder_hash: FolderHash,
_mailbox_hash: MailboxHash,
_sender: RefreshEventConsumer,
) -> Result<Async<()>> {
Err(MeliError::new("Unimplemented."))
@ -259,10 +259,10 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
sender: RefreshEventConsumer,
work_context: WorkContext,
) -> Result<std::thread::ThreadId>;
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>>;
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>>;
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp>;
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()>;
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()>;
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
None
}
@ -272,32 +272,32 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
unimplemented!()
}
fn create_folder(
fn create_mailbox(
&mut self,
_path: String,
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
Err(MeliError::new("Unimplemented."))
}
fn delete_folder(
fn delete_mailbox(
&mut self,
_folder_hash: FolderHash,
) -> Result<FnvHashMap<FolderHash, Folder>> {
_mailbox_hash: MailboxHash,
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented."))
}
fn set_folder_subscription(&mut self, _folder_hash: FolderHash, _val: bool) -> Result<()> {
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
fn rename_folder(&mut self, _folder_hash: FolderHash, _new_path: String) -> Result<Folder> {
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
Err(MeliError::new("Unimplemented."))
}
fn set_folder_permissions(
fn set_mailbox_permissions(
&mut self,
_folder_hash: FolderHash,
_val: FolderPermissions,
_mailbox_hash: MailboxHash,
_val: MailboxPermissions,
) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
@ -315,7 +315,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
/// ```ignore
/// /* Create operation from Backend */
///
/// let op = backend.operation(message.hash(), mailbox.folder.hash());
/// let op = backend.operation(message.hash(), mailbox.hash());
/// ```
///
/// # Example
@ -443,30 +443,30 @@ impl SpecialUsageMailbox {
}
}
pub trait BackendFolder: Debug {
fn hash(&self) -> FolderHash;
pub trait BackendMailbox: Debug {
fn hash(&self) -> MailboxHash;
fn name(&self) -> &str;
/// Path of folder within the mailbox hierarchy, with `/` as separator.
/// Path of mailbox within the mailbox hierarchy, with `/` as separator.
fn path(&self) -> &str;
fn change_name(&mut self, new_name: &str);
fn clone(&self) -> Folder;
fn children(&self) -> &[FolderHash];
fn parent(&self) -> Option<FolderHash>;
fn clone(&self) -> Mailbox;
fn children(&self) -> &[MailboxHash];
fn parent(&self) -> Option<MailboxHash>;
fn is_subscribed(&self) -> bool;
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()>;
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()>;
fn special_usage(&self) -> SpecialUsageMailbox;
fn permissions(&self) -> FolderPermissions;
fn permissions(&self) -> MailboxPermissions;
fn count(&self) -> Result<(usize, usize)>;
}
#[derive(Debug)]
struct DummyFolder {
v: Vec<FolderHash>,
struct DummyMailbox {
v: Vec<MailboxHash>,
}
impl BackendFolder for DummyFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for DummyMailbox {
fn hash(&self) -> MailboxHash {
0
}
@ -480,24 +480,24 @@ impl BackendFolder for DummyFolder {
fn change_name(&mut self, _s: &str) {}
fn clone(&self) -> Folder {
folder_default()
fn clone(&self) -> Mailbox {
mailbox_default()
}
fn special_usage(&self) -> SpecialUsageMailbox {
SpecialUsageMailbox::Normal
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.v
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
None
}
fn permissions(&self) -> FolderPermissions {
FolderPermissions::default()
fn permissions(&self) -> MailboxPermissions {
MailboxPermissions::default()
}
fn is_subscribed(&self) -> bool {
true
@ -513,29 +513,29 @@ impl BackendFolder for DummyFolder {
}
}
pub fn folder_default() -> Folder {
Box::new(DummyFolder {
pub fn mailbox_default() -> Mailbox {
Box::new(DummyMailbox {
v: Vec::with_capacity(0),
})
}
pub type FolderHash = u64;
pub type Folder = Box<dyn BackendFolder + Send + Sync>;
pub type MailboxHash = u64;
pub type Mailbox = Box<dyn BackendMailbox + Send + Sync>;
impl Clone for Folder {
impl Clone for Mailbox {
fn clone(&self) -> Self {
BackendFolder::clone(self.deref())
BackendMailbox::clone(self.deref())
}
}
impl Default for Folder {
impl Default for Mailbox {
fn default() -> Self {
folder_default()
mailbox_default()
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct FolderPermissions {
pub struct MailboxPermissions {
pub create_messages: bool,
pub remove_messages: bool,
pub set_flags: bool,
@ -546,22 +546,22 @@ pub struct FolderPermissions {
pub change_permissions: bool,
}
impl Default for FolderPermissions {
impl Default for MailboxPermissions {
fn default() -> Self {
FolderPermissions {
MailboxPermissions {
create_messages: false,
remove_messages: false,
set_flags: false,
create_child: false,
rename_messages: false,
delete_messages: false,
delete_mailbox: false,
delete_mailbox: true,
change_permissions: false,
}
}
}
impl std::fmt::Display for FolderPermissions {
impl std::fmt::Display for MailboxPermissions {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{:#?}", self)
}

View File

@ -24,8 +24,8 @@ use smallvec::SmallVec;
#[macro_use]
mod protocol_parser;
pub use protocol_parser::{UntaggedResponse::*, *};
mod folder;
pub use folder::*;
mod mailbox;
pub use mailbox::*;
mod operations;
pub use operations::*;
mod connection;
@ -35,10 +35,10 @@ pub use watch::*;
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp;
use crate::backends::FolderHash;
use crate::backends::MailboxHash;
use crate::backends::RefreshEvent;
use crate::backends::RefreshEventKind::{self, *};
use crate::backends::{BackendFolder, Folder, MailBackend, RefreshEventConsumer};
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result};
@ -117,8 +117,8 @@ macro_rules! get_conf_val {
#[derive(Debug)]
pub struct UIDStore {
uidvalidity: Arc<Mutex<FnvHashMap<FolderHash, UID>>>,
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
uidvalidity: Arc<Mutex<FnvHashMap<MailboxHash, UID>>>,
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, MailboxHash)>>>,
uid_index: Arc<Mutex<FnvHashMap<UID, EnvelopeHash>>>,
byte_cache: Arc<Mutex<FnvHashMap<UID, EnvelopeCache>>>,
@ -134,7 +134,7 @@ pub struct ImapType {
can_create_flags: Arc<Mutex<bool>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
}
impl MailBackend for ImapType {
@ -152,16 +152,16 @@ impl MailBackend for ImapType {
}
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let handle = {
let tx = w.tx();
let uid_store = self.uid_store.clone();
let tag_index = self.tag_index.clone();
let can_create_flags = self.can_create_flags.clone();
let folder_hash = folder.hash();
let (permissions, folder_path, folder_exists, no_select, unseen) = {
let f = &self.folders.read().unwrap()[&folder_hash];
let mailbox_hash = mailbox.hash();
let (permissions, mailbox_path, mailbox_exists, no_select, unseen) = {
let f = &self.mailboxes.read().unwrap()[&mailbox_hash];
(
f.permissions.clone(),
f.imap_path().to_string(),
@ -182,24 +182,24 @@ impl MailBackend for ImapType {
let tx = _tx;
let mut response = String::with_capacity(8 * 1024);
let mut conn = connection.lock()?;
debug!("locked for get {}", folder_path);
debug!("locked for get {}", mailbox_path);
/* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
* returns READ-ONLY for both cases) */
conn.send_command(format!("SELECT \"{}\"", folder_path).as_bytes())?;
conn.send_command(format!("SELECT \"{}\"", mailbox_path).as_bytes())?;
conn.read_response(&mut response)?;
let examine_response = protocol_parser::select_response(&response)?;
*can_create_flags.lock().unwrap() = examine_response.can_create_flags;
debug!(
"folder: {} examine_response: {:?}",
folder_path, examine_response
"mailbox: {} examine_response: {:?}",
mailbox_path, examine_response
);
let mut exists: usize = examine_response.uidnext - 1;
{
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
let v = uidvalidities
.entry(folder_hash)
.entry(mailbox_hash)
.or_insert(examine_response.uidvalidity);
*v = examine_response.uidvalidity;
@ -210,11 +210,11 @@ impl MailBackend for ImapType {
permissions.rename_messages = !examine_response.read_only;
permissions.delete_messages = !examine_response.read_only;
permissions.delete_messages = !examine_response.read_only;
let mut folder_exists = folder_exists.lock().unwrap();
*folder_exists = exists;
let mut mailbox_exists = mailbox_exists.lock().unwrap();
*mailbox_exists = exists;
}
/* reselecting the same mailbox with EXAMINE prevents expunging it */
conn.send_command(format!("EXAMINE \"{}\"", folder_path).as_bytes())?;
conn.send_command(format!("EXAMINE \"{}\"", mailbox_path).as_bytes())?;
conn.read_response(&mut response)?;
let mut tag_lck = tag_index.write().unwrap();
@ -247,7 +247,7 @@ impl MailBackend for ImapType {
let mut env = envelope.unwrap();
let mut h = DefaultHasher::new();
h.write_usize(uid);
h.write(folder_path.as_bytes());
h.write(mailbox_path.as_bytes());
env.set_hash(h.finish());
if let Some((flags, keywords)) = flags {
if !flags.contains(Flag::SEEN) {
@ -266,7 +266,7 @@ impl MailBackend for ImapType {
.hash_index
.lock()
.unwrap()
.insert(env.hash(), (uid, folder_hash));
.insert(env.hash(), (uid, mailbox_hash));
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
envelopes.push(env);
}
@ -290,15 +290,15 @@ impl MailBackend for ImapType {
fn refresh(
&mut self,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
sender: RefreshEventConsumer,
) -> Result<Async<()>> {
self.connection.lock().unwrap().connect()?;
let inbox = self
.folders
.mailboxes
.read()
.unwrap()
.get(&folder_hash)
.get(&mailbox_hash)
.map(std::clone::Clone::clone)
.unwrap();
let tag_index = self.tag_index.clone();
@ -340,7 +340,7 @@ impl MailBackend for ImapType {
sender: RefreshEventConsumer,
work_context: WorkContext,
) -> Result<std::thread::ThreadId> {
let folders = self.folders.clone();
let mailboxes = self.mailboxes.clone();
let tag_index = self.tag_index.clone();
let conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
let main_conn = self.connection.clone();
@ -365,7 +365,7 @@ impl MailBackend for ImapType {
is_online,
main_conn,
uid_store,
folders,
mailboxes,
sender,
work_context,
tag_index,
@ -379,38 +379,41 @@ impl MailBackend for ImapType {
Ok(handle.thread().id())
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
{
let folders = self.folders.read().unwrap();
if !folders.is_empty() {
return Ok(folders
let mailboxes = self.mailboxes.read().unwrap();
if !mailboxes.is_empty() {
return Ok(mailboxes
.iter()
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
.collect());
}
}
let mut folders = self.folders.write()?;
*folders = ImapType::imap_folders(&self.connection)?;
folders.retain(|_, f| (self.is_subscribed)(f.path()));
let keys = folders.keys().cloned().collect::<FnvHashSet<FolderHash>>();
let mut mailboxes = self.mailboxes.write()?;
*mailboxes = ImapType::imap_mailboxes(&self.connection)?;
mailboxes.retain(|_, f| (self.is_subscribed)(f.path()));
let keys = mailboxes
.keys()
.cloned()
.collect::<FnvHashSet<MailboxHash>>();
let mut uid_lock = self.uid_store.uidvalidity.lock().unwrap();
for f in folders.values_mut() {
for f in mailboxes.values_mut() {
uid_lock.entry(f.hash()).or_default();
f.children.retain(|c| keys.contains(c));
}
drop(uid_lock);
Ok(folders
Ok(mailboxes
.iter()
.filter(|(_, f)| f.is_subscribed)
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
.collect())
}
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
let (uid, folder_hash) = self.uid_store.hash_index.lock().unwrap()[&hash];
let (uid, mailbox_hash) = self.uid_store.hash_index.lock().unwrap()[&hash];
Box::new(ImapOp::new(
uid,
self.folders.read().unwrap()[&folder_hash]
self.mailboxes.read().unwrap()[&mailbox_hash]
.imap_path()
.to_string(),
self.connection.clone(),
@ -419,28 +422,28 @@ impl MailBackend for ImapType {
))
}
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
let path = {
let folders = self.folders.read().unwrap();
let mailboxes = self.mailboxes.read().unwrap();
let f_result = folders
let f_result = mailboxes
.values()
.find(|v| v.path == folder || v.name == folder);
.find(|v| v.path == mailbox || v.name == mailbox);
if f_result
.map(|f| !f.permissions.lock().unwrap().create_messages)
.unwrap_or(false)
{
return Err(MeliError::new(format!(
"You are not allowed to create messages in folder {}",
folder
"You are not allowed to create messages in mailbox {}",
mailbox
)));
}
f_result
.map(|v| v.imap_path().to_string())
.ok_or(MeliError::new(format!(
"Folder with name {} not found.",
folder
"Mailbox with name {} not found.",
mailbox
)))?
};
let mut response = String::with_capacity(8 * 1024);
@ -478,10 +481,10 @@ impl MailBackend for ImapType {
}
}
fn create_folder(
fn create_mailbox(
&mut self,
mut path: String,
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
/* Must transform path to something the IMAP server will accept
*
* Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
@ -496,21 +499,21 @@ impl MailBackend for ImapType {
* decision is unpleasant for you.
*/
let mut folders = self.folders.write().unwrap();
for root_folder in folders.values().filter(|f| f.parent.is_none()) {
if path.starts_with(&root_folder.name) {
debug!("path starts with {:?}", &root_folder);
let mut mailboxes = self.mailboxes.write().unwrap();
for root_mailbox in mailboxes.values().filter(|f| f.parent.is_none()) {
if path.starts_with(&root_mailbox.name) {
debug!("path starts with {:?}", &root_mailbox);
path = path.replace(
'/',
(root_folder.separator as char).encode_utf8(&mut [0; 4]),
(root_mailbox.separator as char).encode_utf8(&mut [0; 4]),
);
break;
}
}
if folders.values().any(|f| f.path == path) {
if mailboxes.values().any(|f| f.path == path) {
return Err(MeliError::new(format!(
"Folder named `{}` in account `{}` already exists.",
"Mailbox named `{}` in account `{}` already exists.",
path, self.account_name,
)));
}
@ -527,21 +530,24 @@ impl MailBackend for ImapType {
let ret: Result<()> = ImapResponse::from(&response).into();
ret?;
let new_hash = get_path_hash!(path.as_str());
folders.clear();
drop(folders);
Ok((new_hash, self.folders().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
mailboxes.clear();
drop(mailboxes);
Ok((new_hash, self.mailboxes().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
}
fn delete_folder(&mut self, folder_hash: FolderHash) -> Result<FnvHashMap<FolderHash, Folder>> {
let mut folders = self.folders.write().unwrap();
let permissions = folders[&folder_hash].permissions();
fn delete_mailbox(
&mut self,
mailbox_hash: MailboxHash,
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
let mut mailboxes = self.mailboxes.write().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.delete_mailbox {
return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
}
let mut response = String::with_capacity(8 * 1024);
{
let mut conn_lck = self.connection.lock()?;
if !folders[&folder_hash].no_select {
if !mailboxes[&mailbox_hash].no_select {
/* make sure mailbox is not selected before it gets deleted, otherwise
* connection gets dropped by server */
if conn_lck
@ -550,42 +556,46 @@ impl MailBackend for ImapType {
.any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
{
conn_lck.send_command(
format!("UNSELECT \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("UNSELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
conn_lck.read_response(&mut response)?;
} else {
conn_lck.send_command(
format!("SELECT \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("SELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
conn_lck.read_response(&mut response)?;
conn_lck.send_command(
format!("EXAMINE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("EXAMINE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
conn_lck.read_response(&mut response)?;
}
}
if folders[&folder_hash].is_subscribed() {
if mailboxes[&mailbox_hash].is_subscribed() {
conn_lck.send_command(
format!("UNSUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
conn_lck.read_response(&mut response)?;
}
conn_lck.send_command(
debug!(format!("DELETE \"{}\"", folders[&folder_hash].imap_path())).as_bytes(),
debug!(format!(
"DELETE \"{}\"",
mailboxes[&mailbox_hash].imap_path()
))
.as_bytes(),
)?;
conn_lck.read_response(&mut response)?;
}
let ret: Result<()> = ImapResponse::from(&response).into();
ret?;
folders.clear();
drop(folders);
self.folders().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
mailboxes.clear();
drop(mailboxes);
self.mailboxes().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
}
fn set_folder_subscription(&mut self, folder_hash: FolderHash, new_val: bool) -> Result<()> {
let mut folders = self.folders.write().unwrap();
if folders[&folder_hash].is_subscribed() == new_val {
fn set_mailbox_subscription(&mut self, mailbox_hash: MailboxHash, new_val: bool) -> Result<()> {
let mut mailboxes = self.mailboxes.write().unwrap();
if mailboxes[&mailbox_hash].is_subscribed() == new_val {
return Ok(());
}
@ -594,11 +604,11 @@ impl MailBackend for ImapType {
let mut conn_lck = self.connection.lock()?;
if new_val {
conn_lck.send_command(
format!("SUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
} else {
conn_lck.send_command(
format!("UNSUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
)?;
}
conn_lck.read_response(&mut response)?;
@ -606,24 +616,28 @@ impl MailBackend for ImapType {
let ret: Result<()> = ImapResponse::from(&response).into();
if ret.is_ok() {
folders.entry(folder_hash).and_modify(|entry| {
mailboxes.entry(mailbox_hash).and_modify(|entry| {
let _ = entry.set_is_subscribed(new_val);
});
}
ret
}
fn rename_folder(&mut self, folder_hash: FolderHash, mut new_path: String) -> Result<Folder> {
let mut folders = self.folders.write().unwrap();
let permissions = folders[&folder_hash].permissions();
fn rename_mailbox(
&mut self,
mailbox_hash: MailboxHash,
mut new_path: String,
) -> Result<Mailbox> {
let mut mailboxes = self.mailboxes.write().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.delete_mailbox {
return Err(MeliError::new(format!("You do not have permission to rename folder `{}` (rename is equivalent to delete + create). Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
return Err(MeliError::new(format!("You do not have permission to rename mailbox `{}` (rename is equivalent to delete + create). Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
}
let mut response = String::with_capacity(8 * 1024);
if folders[&folder_hash].separator != b'/' {
if mailboxes[&mailbox_hash].separator != b'/' {
new_path = new_path.replace(
'/',
(folders[&folder_hash].separator as char).encode_utf8(&mut [0; 4]),
(mailboxes[&mailbox_hash].separator as char).encode_utf8(&mut [0; 4]),
);
}
{
@ -631,7 +645,7 @@ impl MailBackend for ImapType {
conn_lck.send_command(
debug!(format!(
"RENAME \"{}\" \"{}\"",
folders[&folder_hash].imap_path(),
mailboxes[&mailbox_hash].imap_path(),
new_path
))
.as_bytes(),
@ -641,23 +655,23 @@ impl MailBackend for ImapType {
let new_hash = get_path_hash!(new_path.as_str());
let ret: Result<()> = ImapResponse::from(&response).into();
ret?;
folders.clear();
drop(folders);
self.folders().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
Ok(BackendFolder::clone(
&self.folders.read().unwrap()[&new_hash],
mailboxes.clear();
drop(mailboxes);
self.mailboxes().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
Ok(BackendMailbox::clone(
&self.mailboxes.read().unwrap()[&new_hash],
))
}
fn set_folder_permissions(
fn set_mailbox_permissions(
&mut self,
folder_hash: FolderHash,
_val: crate::backends::FolderPermissions,
mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions,
) -> Result<()> {
let folders = self.folders.write().unwrap();
let permissions = folders[&folder_hash].permissions();
let mailboxes = self.mailboxes.write().unwrap();
let permissions = mailboxes[&mailbox_hash].permissions();
if !permissions.change_permissions {
return Err(MeliError::new(format!("You do not have permission to change permissions for folder `{}`. Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
return Err(MeliError::new(format!("You do not have permission to change permissions for mailbox `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
}
Err(MeliError::new("Unimplemented."))
@ -698,7 +712,7 @@ impl ImapType {
can_create_flags: Arc::new(Mutex::new(false)),
tag_index: Arc::new(RwLock::new(Default::default())),
folders: Arc::new(RwLock::new(Default::default())),
mailboxes: Arc::new(RwLock::new(Default::default())),
connection: Arc::new(Mutex::new(connection)),
uid_store: Arc::new(UIDStore {
uidvalidity: Default::default(),
@ -742,10 +756,10 @@ impl ImapType {
}
}
pub fn imap_folders(
pub fn imap_mailboxes(
connection: &Arc<Mutex<ImapConnection>>,
) -> Result<FnvHashMap<FolderHash, ImapFolder>> {
let mut folders: FnvHashMap<FolderHash, ImapFolder> = Default::default();
) -> Result<FnvHashMap<MailboxHash, ImapMailbox>> {
let mut mailboxes: FnvHashMap<MailboxHash, ImapMailbox> = Default::default();
let mut res = String::with_capacity(8 * 1024);
let mut conn = connection.lock().unwrap();
conn.send_command(b"LIST \"\" \"*\"")?;
@ -755,33 +769,33 @@ impl ImapType {
/* Remove "M__ OK .." line */
lines.next_back();
for l in lines.map(|l| l.trim()) {
if let Ok(mut folder) =
protocol_parser::list_folder_result(l.as_bytes()).to_full_result()
if let Ok(mut mailbox) =
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
{
if let Some(parent) = folder.parent {
if folders.contains_key(&parent) {
folders
if let Some(parent) = mailbox.parent {
if mailboxes.contains_key(&parent) {
mailboxes
.entry(parent)
.and_modify(|e| e.children.push(folder.hash));
.and_modify(|e| e.children.push(mailbox.hash));
} else {
/* Insert dummy parent entry, populating only the children field. Later
* when we encounter the parent entry we will swap its children with
* dummy's */
folders.insert(
mailboxes.insert(
parent,
ImapFolder {
children: vec![folder.hash],
..ImapFolder::default()
ImapMailbox {
children: vec![mailbox.hash],
..ImapMailbox::default()
},
);
}
}
if folders.contains_key(&folder.hash) {
let entry = folders.entry(folder.hash).or_default();
std::mem::swap(&mut entry.children, &mut folder.children);
*entry = folder;
if mailboxes.contains_key(&mailbox.hash) {
let entry = mailboxes.entry(mailbox.hash).or_default();
std::mem::swap(&mut entry.children, &mut mailbox.children);
*entry = mailbox;
} else {
folders.insert(folder.hash, folder);
mailboxes.insert(mailbox.hash, mailbox);
}
} else {
debug!("parse error for {:?}", l);
@ -795,9 +809,9 @@ impl ImapType {
lines.next_back();
for l in lines.map(|l| l.trim()) {
if let Ok(subscription) =
protocol_parser::list_folder_result(l.as_bytes()).to_full_result()
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
{
if let Some(f) = folders.get_mut(&subscription.hash()) {
if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
if subscription.no_select {
continue;
}
@ -807,7 +821,7 @@ impl ImapType {
debug!("parse error for {:?}", l);
}
}
Ok(debug!(folders))
Ok(debug!(mailboxes))
}
pub fn capabilities(&self) -> Vec<String> {
@ -823,13 +837,13 @@ impl ImapType {
pub fn search(
&self,
query: String,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
let folders_lck = self.folders.read()?;
let mailboxes_lck = self.mailboxes.read()?;
let mut response = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock()?;
conn.send_command(
format!("EXAMINE \"{}\"", folders_lck[&folder_hash].imap_path()).as_bytes(),
format!("EXAMINE \"{}\"", mailboxes_lck[&mailbox_hash].imap_path()).as_bytes(),
)?;
conn.read_response(&mut response)?;
conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query).as_bytes())?;

View File

@ -18,36 +18,38 @@
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::backends::{BackendFolder, Folder, FolderHash, FolderPermissions, SpecialUsageMailbox};
use crate::backends::{
BackendMailbox, Mailbox, MailboxHash, MailboxPermissions, SpecialUsageMailbox,
};
use crate::error::*;
use std::sync::{Arc, Mutex, RwLock};
#[derive(Debug, Default, Clone)]
pub struct ImapFolder {
pub(super) hash: FolderHash,
pub struct ImapMailbox {
pub(super) hash: MailboxHash,
pub(super) imap_path: String,
pub(super) path: String,
pub(super) name: String,
pub(super) parent: Option<FolderHash>,
pub(super) children: Vec<FolderHash>,
pub(super) parent: Option<MailboxHash>,
pub(super) children: Vec<MailboxHash>,
pub separator: u8,
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
pub no_select: bool,
pub is_subscribed: bool,
pub permissions: Arc<Mutex<FolderPermissions>>,
pub permissions: Arc<Mutex<MailboxPermissions>>,
pub exists: Arc<Mutex<usize>>,
pub unseen: Arc<Mutex<usize>>,
}
impl ImapFolder {
impl ImapMailbox {
pub fn imap_path(&self) -> &str {
&self.imap_path
}
}
impl BackendFolder for ImapFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for ImapMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
@ -63,11 +65,11 @@ impl BackendFolder for ImapFolder {
self.name = s.to_string();
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.children
}
fn clone(&self) -> Folder {
fn clone(&self) -> Mailbox {
Box::new(std::clone::Clone::clone(self))
}
@ -75,11 +77,11 @@ impl BackendFolder for ImapFolder {
*self.usage.read().unwrap()
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
self.parent
}
fn permissions(&self) -> FolderPermissions {
fn permissions(&self) -> MailboxPermissions {
*self.permissions.lock().unwrap()
}
fn is_subscribed(&self) -> bool {

View File

@ -34,7 +34,7 @@ pub struct ImapOp {
bytes: Option<String>,
headers: Option<String>,
body: Option<String>,
folder_path: String,
mailbox_path: String,
flags: Cell<Option<Flag>>,
connection: Arc<Mutex<ImapConnection>>,
uid_store: Arc<UIDStore>,
@ -44,7 +44,7 @@ pub struct ImapOp {
impl ImapOp {
pub fn new(
uid: usize,
folder_path: String,
mailbox_path: String,
connection: Arc<Mutex<ImapConnection>>,
uid_store: Arc<UIDStore>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
@ -55,7 +55,7 @@ impl ImapOp {
bytes: None,
headers: None,
body: None,
folder_path,
mailbox_path,
flags: Cell::new(None),
uid_store,
tag_index,
@ -78,7 +78,7 @@ impl BackendOp for ImapOp {
let mut response = String::with_capacity(8 * 1024);
{
let mut conn = self.connection.lock().unwrap();
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
conn.read_response(&mut response)?;
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
conn.read_response(&mut response)?;
@ -116,7 +116,7 @@ impl BackendOp for ImapOp {
} else {
let mut response = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
conn.send_command(format!("EXAMINE \"{}\"", &self.folder_path,).as_bytes())
conn.send_command(format!("EXAMINE \"{}\"", &self.mailbox_path,).as_bytes())
.unwrap();
conn.read_response(&mut response).unwrap();
conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes())
@ -154,7 +154,7 @@ impl BackendOp for ImapOp {
let mut response = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
conn.read_response(&mut response)?;
debug!(&response);
conn.send_command(
@ -190,7 +190,7 @@ impl BackendOp for ImapOp {
fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
let mut response = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
conn.read_response(&mut response)?;
conn.send_command(
format!(

View File

@ -213,7 +213,7 @@ macro_rules! dbg_dmp (
* LIST (\HasChildren) "." INBOX
*/
named!(
pub list_folder_result<ImapFolder>,
pub list_mailbox_result<ImapMailbox>,
do_parse!(
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
>> properties: take_until!(&b")"[0..])
@ -223,7 +223,7 @@ named!(
>> path: alt_complete!(delimited!(tag!("\""), is_not!("\""), tag!("\"")) | call!(rest))
>> ({
let separator: u8 = separator[0];
let mut f = ImapFolder::default();
let mut f = ImapMailbox::default();
f.no_select = false;
f.is_subscribed = false;
for p in properties.split(|&b| b == b' ') {

View File

@ -28,20 +28,20 @@ pub struct ImapWatchKit {
pub is_online: Arc<Mutex<(Instant, Result<()>)>>,
pub main_conn: Arc<Mutex<ImapConnection>>,
pub uid_store: Arc<UIDStore>,
pub folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
pub mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
pub sender: RefreshEventConsumer,
pub work_context: WorkContext,
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
}
macro_rules! exit_on_error {
($sender:expr, $folder_hash:ident, $work_context:ident, $thread_id:ident, $($result:expr)+) => {
($sender:expr, $mailbox_hash:ident, $work_context:ident, $thread_id:ident, $($result:expr)+) => {
$(if let Err(e) = $result {
debug!("failure: {}", e.to_string());
$work_context.set_status.send(($thread_id, e.to_string())).unwrap();
$work_context.finished.send($thread_id).unwrap();
$sender.send(RefreshEvent {
hash: $folder_hash,
hash: $mailbox_hash,
kind: RefreshEventKind::Failure(e.clone()),
});
Err(e)
@ -56,7 +56,7 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
mut conn,
main_conn,
uid_store,
folders,
mailboxes,
sender,
work_context,
tag_index,
@ -76,17 +76,17 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
.send((thread_id, "sleeping...".to_string()))
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(5 * 60 * 1000));
let folders = folders.read().unwrap();
for folder in folders.values() {
let mailboxes = mailboxes.read().unwrap();
for mailbox in mailboxes.values() {
work_context
.set_status
.send((
thread_id,
format!("examining `{}` for updates...", folder.path()),
format!("examining `{}` for updates...", mailbox.path()),
))
.unwrap();
examine_updates(
folder,
mailbox,
&sender,
&mut conn,
&uid_store,
@ -109,7 +109,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
is_online,
main_conn,
uid_store,
folders,
mailboxes,
sender,
work_context,
tag_index,
@ -122,16 +122,16 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
conn.connect()?;
let thread_id: std::thread::ThreadId = std::thread::current().id();
let folder: ImapFolder = match folders
let mailbox: ImapMailbox = match mailboxes
.read()
.unwrap()
.values()
.find(|f| f.parent.is_none() && (f.special_usage() == SpecialUsageMailbox::Inbox))
.map(std::clone::Clone::clone)
{
Some(folder) => folder,
Some(mailbox) => mailbox,
None => {
let err = MeliError::new("INBOX mailbox not found in local folder index. meli may have not parsed the IMAP folders correctly");
let err = MeliError::new("INBOX mailbox not found in local mailbox index. meli may have not parsed the IMAP mailboxes correctly");
debug!("failure: {}", err.to_string());
work_context
.set_status
@ -144,28 +144,28 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
return Err(err);
}
};
let folder_hash = folder.hash();
let mailbox_hash = mailbox.hash();
let mut response = String::with_capacity(8 * 1024);
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(format!("SELECT \"{}\"", folder.imap_path()).as_bytes())
conn.send_command(format!("SELECT \"{}\"", mailbox.imap_path()).as_bytes())
conn.read_response(&mut response)
);
debug!("select response {}", &response);
{
let mut prev_exists = folder.exists.lock().unwrap();
let mut prev_exists = mailbox.exists.lock().unwrap();
*prev_exists = match protocol_parser::select_response(&response) {
Ok(ok) => {
{
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
if let Some(v) = uidvalidities.get_mut(&folder_hash) {
if let Some(v) = uidvalidities.get_mut(&mailbox_hash) {
if *v != ok.uidvalidity {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Rescan,
});
*prev_exists = 0;
@ -176,15 +176,15 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
} else {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Rescan,
});
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Failure(MeliError::new(format!(
"Unknown mailbox: {} {}",
folder.path(),
folder_hash
mailbox.path(),
mailbox_hash
))),
});
}
@ -200,7 +200,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(b"IDLE")
@ -214,14 +214,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
let mut watch = std::time::Instant::now();
/* duration interval to send heartbeat */
let _26_mins = std::time::Duration::from_secs(26 * 60);
/* duration interval to check other folders for changes */
/* duration interval to check other mailboxes for changes */
let _5_mins = std::time::Duration::from_secs(5 * 60);
while let Some(line) = iter.next() {
let now = std::time::Instant::now();
if now.duration_since(beat) >= _26_mins {
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
iter.conn.set_nonblocking(true)
@ -237,21 +237,21 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
if now.duration_since(watch) >= _5_mins {
/* Time to poll all inboxes */
let mut conn = main_conn.lock().unwrap();
for folder in folders.read().unwrap().values() {
for mailbox in mailboxes.read().unwrap().values() {
work_context
.set_status
.send((
thread_id,
format!("examining `{}` for updates...", folder.path()),
format!("examining `{}` for updates...", mailbox.path()),
))
.unwrap();
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
examine_updates(
folder,
mailbox,
&sender,
&mut conn,
&uid_store,
@ -279,7 +279,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
/* UID SEARCH RECENT */
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(b"EXAMINE INBOX")
@ -297,7 +297,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
Ok(v) => {
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(
@ -331,13 +331,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
.hash_index
.lock()
.unwrap()
.insert(env.hash(), (uid, folder_hash));
.insert(env.hash(), (uid, mailbox_hash));
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
debug!(
"Create event {} {} {}",
env.hash(),
env.subject(),
folder.path(),
mailbox.path(),
);
if let Some((_, keywords)) = flags {
let mut tag_lck = tag_index.write().unwrap();
@ -350,12 +350,12 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
}
if !env.is_seen() {
*folder.unseen.lock().unwrap() += 1;
*mailbox.unseen.lock().unwrap() += 1;
}
*folder.exists.lock().unwrap() += 1;
*mailbox.exists.lock().unwrap() += 1;
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}
@ -390,7 +390,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
let mut conn = main_conn.lock().unwrap();
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers
* */
let mut prev_exists = folder.exists.lock().unwrap();
let mut prev_exists = mailbox.exists.lock().unwrap();
debug!("exists {}", n);
work_context
.set_status
@ -400,14 +400,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
"got `{} EXISTS` notification (EXISTS was previously {} for {}",
n,
*prev_exists,
folder.path()
mailbox.path()
),
))
.unwrap();
if n > *prev_exists {
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(b"EXAMINE INBOX")
@ -450,7 +450,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
.hash_index
.lock()
.unwrap()
.insert(env.hash(), (uid, folder_hash));
.insert(env.hash(), (uid, mailbox_hash));
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
if let Some((_, keywords)) = flags {
let mut tag_lck = tag_index.write().unwrap();
@ -466,13 +466,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
"Create event {} {} {}",
env.hash(),
env.subject(),
folder.path(),
mailbox.path(),
);
if !env.is_seen() {
*folder.unseen.lock().unwrap() += 1;
*mailbox.unseen.lock().unwrap() += 1;
}
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}
@ -507,7 +507,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
.unwrap();
work_context.finished.send(thread_id).unwrap();
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Failure(MeliError::new(format!(
"IDLE connection dropped: {}",
&err
@ -517,7 +517,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
pub fn examine_updates(
folder: &ImapFolder,
mailbox: &ImapMailbox,
sender: &RefreshEventConsumer,
conn: &mut ImapConnection,
uid_store: &Arc<UIDStore>,
@ -525,15 +525,15 @@ pub fn examine_updates(
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
) -> Result<()> {
let thread_id: std::thread::ThreadId = std::thread::current().id();
let folder_hash = folder.hash();
debug!("examining folder {} {}", folder_hash, folder.path());
let mailbox_hash = mailbox.hash();
debug!("examining mailbox {} {}", mailbox_hash, mailbox.path());
let mut response = String::with_capacity(8 * 1024);
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(format!("EXAMINE \"{}\"", folder.imap_path()).as_bytes())
conn.send_command(format!("EXAMINE \"{}\"", mailbox.imap_path()).as_bytes())
conn.read_response(&mut response)
);
match protocol_parser::select_response(&response) {
@ -542,10 +542,10 @@ pub fn examine_updates(
{
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
if let Some(v) = uidvalidities.get_mut(&folder_hash) {
if let Some(v) = uidvalidities.get_mut(&mailbox_hash) {
if *v != ok.uidvalidity {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Rescan,
});
uid_store.uid_index.lock().unwrap().clear();
@ -555,27 +555,27 @@ pub fn examine_updates(
}
} else {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Rescan,
});
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Failure(MeliError::new(format!(
"Unknown mailbox: {} {}",
folder.path(),
folder_hash
mailbox.path(),
mailbox_hash
))),
});
}
}
let mut prev_exists = folder.exists.lock().unwrap();
let mut prev_exists = mailbox.exists.lock().unwrap();
let n = ok.exists;
if ok.recent > 0 {
{
/* UID SEARCH RECENT */
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(b"UID SEARCH RECENT")
@ -591,7 +591,7 @@ pub fn examine_updates(
Ok(v) => {
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(
@ -615,7 +615,7 @@ pub fn examine_updates(
.hash_index
.lock()
.unwrap()
.insert(env.hash(), (uid, folder_hash));
.insert(env.hash(), (uid, mailbox_hash));
uid_store
.uid_index
.lock()
@ -625,7 +625,7 @@ pub fn examine_updates(
"Create event {} {} {}",
env.hash(),
env.subject(),
folder.path(),
mailbox.path(),
);
if let Some((_, keywords)) = flags {
let mut tag_lck = tag_index.write().unwrap();
@ -638,10 +638,10 @@ pub fn examine_updates(
}
}
if !env.is_seen() {
*folder.unseen.lock().unwrap() += 1;
*mailbox.unseen.lock().unwrap() += 1;
}
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}
@ -667,7 +667,7 @@ pub fn examine_updates(
debug!("exists {}", n);
exit_on_error!(
sender,
folder_hash,
mailbox_hash,
work_context,
thread_id,
conn.send_command(
@ -693,7 +693,7 @@ pub fn examine_updates(
.hash_index
.lock()
.unwrap()
.insert(env.hash(), (uid, folder_hash));
.insert(env.hash(), (uid, mailbox_hash));
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
if let Some((_, keywords)) = flags {
let mut tag_lck = tag_index.write().unwrap();
@ -709,13 +709,13 @@ pub fn examine_updates(
"Create event {} {} {}",
env.hash(),
env.subject(),
folder.path(),
mailbox.path(),
);
if !env.is_seen() {
*folder.unseen.lock().unwrap() += 1;
*mailbox.unseen.lock().unwrap() += 1;
}
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}

View File

@ -21,8 +21,8 @@
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp;
use crate::backends::FolderHash;
use crate::backends::{BackendFolder, Folder, MailBackend, RefreshEventConsumer};
use crate::backends::MailboxHash;
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result};
@ -71,8 +71,8 @@ use rfc8620::*;
pub mod objects;
use objects::*;
pub mod folder;
use folder::*;
pub mod mailbox;
use mailbox::*;
#[derive(Debug, Default)]
pub struct EnvelopeCache {
@ -189,7 +189,7 @@ pub struct JmapType {
connection: Arc<JmapConnection>,
store: Arc<RwLock<Store>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
folders: Arc<RwLock<FnvHashMap<FolderHash, JmapFolder>>>,
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, JmapMailbox>>>,
}
impl MailBackend for JmapType {
@ -202,18 +202,18 @@ impl MailBackend for JmapType {
if Instant::now().duration_since(self.online.lock().unwrap().0)
>= std::time::Duration::new(2, 0)
{
let _ = self.folders();
let _ = self.mailboxes();
}
}
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let folders = self.folders.clone();
let mailboxes = self.mailboxes.clone();
let store = self.store.clone();
let tag_index = self.tag_index.clone();
let connection = self.connection.clone();
let folder_hash = folder.hash();
let mailbox_hash = mailbox.hash();
let handle = {
let tx = w.tx();
let closure = move |_work_context| {
@ -221,7 +221,7 @@ impl MailBackend for JmapType {
&connection,
&store,
&tag_index,
&folders.read().unwrap()[&folder_hash],
&mailboxes.read().unwrap()[&mailbox_hash],
)))
.unwrap();
tx.send(AsyncStatus::Finished).unwrap();
@ -239,19 +239,19 @@ impl MailBackend for JmapType {
Err(MeliError::from("sadfsa"))
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
if self.folders.read().unwrap().is_empty() {
let folders = std::dbg!(protocol::get_mailboxes(&self.connection))?;
*self.folders.write().unwrap() = folders;
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
if self.mailboxes.read().unwrap().is_empty() {
let mailboxes = std::dbg!(protocol::get_mailboxes(&self.connection))?;
*self.mailboxes.write().unwrap() = mailboxes;
}
Ok(self
.folders
.mailboxes
.read()
.unwrap()
.iter()
.filter(|(_, f)| f.is_subscribed)
.map(|(&h, f)| (h, BackendFolder::clone(f) as Folder))
.map(|(&h, f)| (h, BackendMailbox::clone(f) as Mailbox))
.collect())
}
@ -263,7 +263,7 @@ impl MailBackend for JmapType {
))
}
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
Ok(())
}
@ -291,7 +291,7 @@ impl JmapType {
connection: Arc::new(JmapConnection::new(&server_conf, online.clone())?),
store: Arc::new(RwLock::new(Store::default())),
tag_index: Arc::new(RwLock::new(Default::default())),
folders: Arc::new(RwLock::new(FnvHashMap::default())),
mailboxes: Arc::new(RwLock::new(FnvHashMap::default())),
account_name: s.name.clone(),
online,
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),

View File

@ -20,15 +20,15 @@
*/
use super::*;
use crate::backends::{FolderPermissions, SpecialUsageMailbox};
use crate::backends::{MailboxPermissions, SpecialUsageMailbox};
use std::sync::{Arc, Mutex, RwLock};
#[derive(Debug, Clone)]
pub struct JmapFolder {
pub struct JmapMailbox {
pub name: String,
pub path: String,
pub hash: FolderHash,
pub v: Vec<FolderHash>,
pub hash: MailboxHash,
pub v: Vec<MailboxHash>,
pub id: String,
pub is_subscribed: bool,
pub my_rights: JmapRights,
@ -42,8 +42,8 @@ pub struct JmapFolder {
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
}
impl BackendFolder for JmapFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for JmapMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
@ -57,20 +57,20 @@ impl BackendFolder for JmapFolder {
fn change_name(&mut self, _s: &str) {}
fn clone(&self) -> Folder {
fn clone(&self) -> Mailbox {
Box::new(std::clone::Clone::clone(self))
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.v
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
None
}
fn permissions(&self) -> FolderPermissions {
FolderPermissions::default()
fn permissions(&self) -> MailboxPermissions {
MailboxPermissions::default()
}
fn special_usage(&self) -> SpecialUsageMailbox {
@ -83,7 +83,7 @@ impl BackendFolder for JmapFolder {
Some("sent") => SpecialUsageMailbox::Sent,
Some(other) => {
debug!(
"unknown JMAP mailbox role for folder {}: {}",
"unknown JMAP mailbox role for mailbox {}: {}",
self.path(),
other
);

View File

@ -394,7 +394,7 @@ pub struct EmailQueryResponse {
pub struct EmailQuery {
#[serde(flatten)]
pub query_call: Query<EmailFilterCondition, EmailObject>,
//pub filter: EmailFilterCondition, /* "inMailboxes": [ folder.id ] },*/
//pub filter: EmailFilterCondition, /* "inMailboxes": [ mailbox.id ] },*/
pub collapse_threads: bool,
}

View File

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::folder::JmapFolder;
use super::mailbox::JmapMailbox;
use super::*;
use serde::Serialize;
use serde_json::{json, Value};
@ -94,7 +94,7 @@ impl Request {
}
}
pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<FolderHash, JmapFolder>> {
pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<MailboxHash, JmapMailbox>> {
let seq = get_request_no!(conn.request_no);
let res = conn
.client
@ -141,7 +141,7 @@ pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<FolderHash, Jma
let hash = crate::get_path_hash!(&name);
(
hash,
JmapFolder {
JmapMailbox {
name: name.clone(),
hash,
path: name,
@ -170,12 +170,12 @@ pub struct JsonResponse<'a> {
method_responses: Vec<MethodResponse<'a>>,
}
pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<String>> {
pub fn get_message_list(conn: &JmapConnection, mailbox: &JmapMailbox) -> Result<Vec<String>> {
let email_call: EmailQuery = EmailQuery::new(
Query::new()
.account_id(conn.mail_account_id().to_string())
.filter(Some(
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
))
.position(0),
)
@ -241,13 +241,13 @@ pub fn get(
conn: &JmapConnection,
store: &Arc<RwLock<Store>>,
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
folder: &JmapFolder,
mailbox: &JmapMailbox,
) -> Result<Vec<Envelope>> {
let email_query_call: EmailQuery = EmailQuery::new(
Query::new()
.account_id(conn.mail_account_id().to_string())
.filter(Some(
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
))
.position(0),
)

View File

@ -39,7 +39,7 @@ use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct MaildirOp {
hash_index: HashIndexes,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
hash: EnvelopeHash,
slice: Option<Mmap>,
}
@ -48,7 +48,7 @@ impl Clone for MaildirOp {
fn clone(&self) -> Self {
MaildirOp {
hash_index: self.hash_index.clone(),
folder_hash: self.folder_hash,
mailbox_hash: self.mailbox_hash,
hash: self.hash,
slice: None,
}
@ -56,18 +56,18 @@ impl Clone for MaildirOp {
}
impl MaildirOp {
pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, folder_hash: FolderHash) -> Self {
pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, mailbox_hash: MailboxHash) -> Self {
MaildirOp {
hash_index,
folder_hash,
mailbox_hash,
hash,
slice: None,
}
}
fn path(&self) -> PathBuf {
let map = self.hash_index.lock().unwrap();
let map = &map[&self.folder_hash];
debug!("looking for {} in {} map", self.hash, self.folder_hash);
let map = &map[&self.mailbox_hash];
debug!("looking for {} in {} map", self.hash, self.mailbox_hash);
if !map.contains_key(&self.hash) {
debug!("doesn't contain it though len = {}\n{:#?}", map.len(), map);
for e in map.iter() {
@ -155,7 +155,7 @@ impl<'a> BackendOp for MaildirOp {
let new_name: PathBuf = new_name.into();
let hash_index = self.hash_index.clone();
let mut map = hash_index.lock().unwrap();
let map = map.entry(self.folder_hash).or_default();
let map = map.entry(self.mailbox_hash).or_default();
map.entry(old_hash).or_default().modified = Some(PathMod::Path(new_name.clone()));
debug!("renaming {:?} to {:?}", path, new_name);
@ -170,37 +170,37 @@ impl<'a> BackendOp for MaildirOp {
}
#[derive(Debug, Default, Clone)]
pub struct MaildirFolder {
hash: FolderHash,
pub struct MaildirMailbox {
hash: MailboxHash,
name: String,
fs_path: PathBuf,
path: PathBuf,
parent: Option<FolderHash>,
children: Vec<FolderHash>,
parent: Option<MailboxHash>,
children: Vec<MailboxHash>,
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
pub is_subscribed: bool,
permissions: FolderPermissions,
permissions: MailboxPermissions,
pub total: Arc<Mutex<usize>>,
pub unseen: Arc<Mutex<usize>>,
}
impl MaildirFolder {
impl MaildirMailbox {
pub fn new(
path: String,
file_name: String,
parent: Option<FolderHash>,
children: Vec<FolderHash>,
parent: Option<MailboxHash>,
children: Vec<MailboxHash>,
settings: &AccountSettings,
) -> Result<Self> {
let pathbuf = PathBuf::from(&path);
let mut h = DefaultHasher::new();
pathbuf.hash(&mut h);
/* Check if folder path (Eg `INBOX/Lists/luddites`) is included in the subscribed
/* Check if mailbox path (Eg `INBOX/Lists/luddites`) is included in the subscribed
* mailboxes in user configuration */
let fname = pathbuf
.strip_prefix(
PathBuf::from(&settings.root_folder)
PathBuf::from(&settings.root_mailbox)
.expand()
.parent()
.unwrap_or_else(|| &Path::new("/")),
@ -213,7 +213,7 @@ impl MaildirFolder {
true
};
let ret = MaildirFolder {
let ret = MaildirMailbox {
hash: h.finish(),
name: file_name,
path: fname.unwrap().to_path_buf(),
@ -222,7 +222,7 @@ impl MaildirFolder {
children,
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
is_subscribed: false,
permissions: FolderPermissions {
permissions: MailboxPermissions {
create_messages: !read_only,
remove_messages: !read_only,
set_flags: !read_only,
@ -250,7 +250,7 @@ impl MaildirFolder {
p.push(d);
if !p.is_dir() {
return Err(MeliError::new(format!(
"{} is not a valid maildir folder",
"{} is not a valid maildir mailbox",
path.display()
)));
}
@ -260,8 +260,8 @@ impl MaildirFolder {
}
}
impl BackendFolder for MaildirFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for MaildirMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
@ -277,11 +277,11 @@ impl BackendFolder for MaildirFolder {
self.name = s.to_string();
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.children
}
fn clone(&self) -> Folder {
fn clone(&self) -> Mailbox {
Box::new(std::clone::Clone::clone(self))
}
@ -289,11 +289,11 @@ impl BackendFolder for MaildirFolder {
*self.usage.read().unwrap()
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
self.parent
}
fn permissions(&self) -> FolderPermissions {
fn permissions(&self) -> MailboxPermissions {
self.permissions
}
fn is_subscribed(&self) -> bool {

View File

@ -20,10 +20,10 @@
*/
use super::{
BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
RefreshEventKind::*,
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxHash, RefreshEvent,
RefreshEventConsumer, RefreshEventKind::*,
};
use super::{MaildirFolder, MaildirOp};
use super::{MaildirMailbox, MaildirOp};
use crate::async_workers::*;
use crate::conf::AccountSettings;
use crate::email::{Envelope, EnvelopeHash, Flag};
@ -88,7 +88,7 @@ impl From<PathBuf> for MaildirPath {
#[derive(Debug, Default)]
pub struct HashIndex {
index: FnvHashMap<EnvelopeHash, MaildirPath>,
hash: FolderHash,
hash: MailboxHash,
}
impl Deref for HashIndex {
@ -104,14 +104,14 @@ impl DerefMut for HashIndex {
}
}
pub type HashIndexes = Arc<Mutex<FnvHashMap<FolderHash, HashIndex>>>;
pub type HashIndexes = Arc<Mutex<FnvHashMap<MailboxHash, HashIndex>>>;
/// Maildir backend https://cr.yp.to/proto/maildir.html
#[derive(Debug)]
pub struct MaildirType {
name: String,
folders: FnvHashMap<FolderHash, MaildirFolder>,
folder_index: Arc<Mutex<FnvHashMap<EnvelopeHash, FolderHash>>>,
mailboxes: FnvHashMap<MailboxHash, MaildirMailbox>,
mailbox_index: Arc<Mutex<FnvHashMap<EnvelopeHash, MailboxHash>>>,
hash_indexes: HashIndexes,
path: PathBuf,
}
@ -181,32 +181,32 @@ impl MailBackend for MaildirType {
Ok(())
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
Ok(self
.folders
.mailboxes
.iter()
.map(|(h, f)| (*h, BackendFolder::clone(f)))
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
.collect())
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, folder)
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, mailbox)
}
fn refresh(
&mut self,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
sender: RefreshEventConsumer,
) -> Result<Async<()>> {
let w = AsyncBuilder::new();
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
let handle = {
let folder: &MaildirFolder = &self.folders[&folder_hash];
let path: PathBuf = folder.fs_path().into();
let name = format!("refresh {:?}", folder.name());
let mailbox: &MaildirMailbox = &self.mailboxes[&mailbox_hash];
let path: PathBuf = mailbox.fs_path().into();
let name = format!("refresh {:?}", mailbox.name());
let root_path = self.path.to_path_buf();
let map = self.hash_indexes.clone();
let folder_index = self.folder_index.clone();
let mailbox_index = self.mailbox_index.clone();
Box::new(move |work_context: crate::async_workers::WorkContext| {
work_context
@ -237,14 +237,14 @@ impl MailBackend for MaildirType {
}
let mut current_hashes = {
let mut map = map.lock().unwrap();
let map = map.entry(folder_hash).or_default();
let map = map.entry(mailbox_hash).or_default();
map.keys().cloned().collect::<FnvHashSet<EnvelopeHash>>()
};
for file in files {
let hash = get_file_hash(&file);
{
let mut map = map.lock().unwrap();
let map = map.entry(folder_hash).or_default();
let map = map.entry(mailbox_hash).or_default();
if map.contains_key(&hash) {
map.remove(&hash);
current_hashes.remove(&hash);
@ -252,9 +252,9 @@ impl MailBackend for MaildirType {
}
(*map).insert(hash, PathBuf::from(&file).into());
}
let op = Box::new(MaildirOp::new(hash, map.clone(), folder_hash));
let op = Box::new(MaildirOp::new(hash, map.clone(), mailbox_hash));
if let Some(e) = Envelope::from_token(op, hash) {
folder_index.lock().unwrap().insert(e.hash(), folder_hash);
mailbox_index.lock().unwrap().insert(e.hash(), mailbox_hash);
let file_name = PathBuf::from(file)
.strip_prefix(&root_path)
.unwrap()
@ -277,7 +277,7 @@ impl MailBackend for MaildirType {
bincode::serialize_into(writer, &e).unwrap();
}
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(e)),
});
} else {
@ -290,7 +290,7 @@ impl MailBackend for MaildirType {
}
}
for ev in current_hashes.into_iter().map(|h| RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Remove(h),
}) {
sender.send(ev);
@ -299,7 +299,7 @@ impl MailBackend for MaildirType {
};
if let Err(err) = thunk(&sender) {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Failure(err),
});
}
@ -319,14 +319,14 @@ impl MailBackend for MaildirType {
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
debug!("watching {:?}", root_path);
let hash_indexes = self.hash_indexes.clone();
let folder_index = self.folder_index.clone();
let folder_counts = self
.folders
let mailbox_index = self.mailbox_index.clone();
let mailbox_counts = self
.mailboxes
.iter()
.map(|(&k, v)| (k, (v.unseen.clone(), v.total.clone())))
.collect::<FnvHashMap<FolderHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
.collect::<FnvHashMap<MailboxHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
let handle = thread::Builder::new()
.name("folder watch".to_string())
.name("mailbox watch".to_string())
.spawn(move || {
// Move `watcher` in the closure's scope so that it doesn't get dropped.
let _watcher = watcher;
@ -360,7 +360,7 @@ impl MailBackend for MaildirType {
}
let folder_hash = get_path_hash!(pathbuf);
let mailbox_hash = get_path_hash!(pathbuf);
let file_name = pathbuf
.as_path()
.strip_prefix(&root_path)
@ -368,12 +368,12 @@ impl MailBackend for MaildirType {
.to_path_buf();
if let Some(env) = add_path_to_index(
&hash_indexes,
folder_hash,
mailbox_hash,
pathbuf.as_path(),
&cache_dir,
file_name,
) {
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
debug!(
"Create event {} {} {}",
env.hash(),
@ -381,11 +381,11 @@ impl MailBackend for MaildirType {
pathbuf.display()
);
if !env.is_seen() {
*folder_counts[&folder_hash].0.lock().unwrap() += 1;
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
}
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}
@ -394,10 +394,10 @@ impl MailBackend for MaildirType {
DebouncedEvent::NoticeWrite(pathbuf)
| DebouncedEvent::Write(pathbuf) => {
debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
let folder_hash = get_path_hash!(pathbuf);
let mailbox_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();
&mut hash_indexes_lock.entry(mailbox_hash).or_default();
let file_name = pathbuf
.as_path()
.strip_prefix(&root_path)
@ -418,14 +418,14 @@ impl MailBackend for MaildirType {
* envelope. */
if let Some(env) = add_path_to_index(
&hash_indexes,
folder_hash,
mailbox_hash,
pathbuf.as_path(),
&cache_dir,
file_name,
) {
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
}
@ -438,7 +438,7 @@ impl MailBackend for MaildirType {
let op = Box::new(MaildirOp::new(
new_hash,
hash_indexes.clone(),
folder_hash,
mailbox_hash,
));
if let Some(env) = Envelope::from_token(op, new_hash) {
debug!("{}\t{:?}", new_hash, &pathbuf);
@ -451,7 +451,7 @@ impl MailBackend for MaildirType {
/* Send Write notice */
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Update(old_hash, Box::new(env)),
});
}
@ -461,9 +461,9 @@ impl MailBackend for MaildirType {
DebouncedEvent::NoticeRemove(pathbuf)
| DebouncedEvent::Remove(pathbuf) => {
debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
let folder_hash = get_path_hash!(pathbuf);
let mailbox_hash = get_path_hash!(pathbuf);
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
let hash: EnvelopeHash = if let Some((k, _)) =
index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
{
@ -493,9 +493,9 @@ impl MailBackend for MaildirType {
});
//FIXME: check if envelope was unseen to update unseen count
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Remove(hash),
});
}
@ -505,12 +505,12 @@ impl MailBackend for MaildirType {
"DebouncedEvent::Rename(src = {:?}, dest = {:?})",
src, dest
);
let folder_hash = get_path_hash!(src);
let mailbox_hash = get_path_hash!(src);
let old_hash: EnvelopeHash = get_file_hash(src.as_path());
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
if index_lock.contains_key(&old_hash)
&& !index_lock[&old_hash].removed
@ -524,7 +524,7 @@ impl MailBackend for MaildirType {
hash: get_path_hash!(dest),
kind: Rename(old_hash, new_hash),
});
folder_index.lock().unwrap().insert(new_hash,get_path_hash!(dest) );
mailbox_index.lock().unwrap().insert(new_hash,get_path_hash!(dest) );
index_lock.insert(new_hash, dest.into());
continue;
} else if !index_lock.contains_key(&new_hash)
@ -556,12 +556,12 @@ impl MailBackend for MaildirType {
drop(hash_indexes_lock);
if let Some(env) = add_path_to_index(
&hash_indexes,
folder_hash,
mailbox_hash,
dest.as_path(),
&cache_dir,
file_name,
) {
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
mailbox_index.lock().unwrap().insert(env.hash(), mailbox_hash);
debug!(
"Create event {} {} {}",
env.hash(),
@ -569,11 +569,11 @@ impl MailBackend for MaildirType {
dest.display()
);
if !env.is_seen() {
*folder_counts[&folder_hash].0.lock().unwrap() += 1;
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
}
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: Create(Box::new(env)),
});
continue;
@ -595,10 +595,10 @@ impl MailBackend for MaildirType {
});
*/
}
/* Trigger rescan of folder */
/* Trigger rescan of mailbox */
DebouncedEvent::Rescan => {
/* Actually should rescan all folders */
unreachable!("Unimplemented: rescan of all folders in MaildirType")
/* Actually should rescan all mailboxes */
unreachable!("Unimplemented: rescan of all mailboxes in MaildirType")
}
_ => {}
},
@ -613,20 +613,20 @@ impl MailBackend for MaildirType {
Box::new(MaildirOp::new(
hash,
self.hash_indexes.clone(),
self.folder_index.lock().unwrap()[&hash],
self.mailbox_index.lock().unwrap()[&hash],
))
}
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
for f in self.folders.values() {
if f.name == folder || f.path.to_str().unwrap() == folder {
return MaildirType::save_to_folder(f.fs_path.clone(), bytes, flags);
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
for f in self.mailboxes.values() {
if f.name == mailbox || f.path.to_str().unwrap() == mailbox {
return MaildirType::save_to_mailbox(f.fs_path.clone(), bytes, flags);
}
}
Err(MeliError::new(format!(
"'{}' is not a valid folder.",
folder
"'{}' is not a valid mailbox.",
mailbox
)))
}
@ -634,35 +634,35 @@ impl MailBackend for MaildirType {
self
}
fn create_folder(
fn create_mailbox(
&mut self,
new_path: String,
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
let mut path = self.path.clone();
path.push(&new_path);
if !path.starts_with(&self.path) {
return Err(MeliError::new(format!("Path given (`{}`) is absolute. Please provide a path relative to the account's root folder.", &new_path)));
return Err(MeliError::new(format!("Path given (`{}`) is absolute. Please provide a path relative to the account's root mailbox.", &new_path)));
}
std::fs::create_dir(&path)?;
/* create_dir does not create intermediate directories (like `mkdir -p`), so the parent must be a valid
* folder at this point. */
* mailbox at this point. */
let parent = path.parent().and_then(|p| {
self.folders
self.mailboxes
.iter()
.find(|(_, f)| f.fs_path == p)
.map(|item| *item.0)
});
let folder_hash = get_path_hash!(&path);
let mailbox_hash = get_path_hash!(&path);
if let Some(parent) = parent {
self.folders
self.mailboxes
.entry(parent)
.and_modify(|entry| entry.children.push(folder_hash));
.and_modify(|entry| entry.children.push(mailbox_hash));
}
let new_folder = MaildirFolder {
hash: folder_hash,
let new_mailbox = MaildirMailbox {
hash: mailbox_hash,
path: PathBuf::from(&new_path),
name: new_path,
fs_path: path,
@ -675,29 +675,29 @@ impl MailBackend for MaildirType {
total: Default::default(),
};
self.folders.insert(folder_hash, new_folder);
Ok((folder_hash, self.folders()?))
self.mailboxes.insert(mailbox_hash, new_mailbox);
Ok((mailbox_hash, self.mailboxes()?))
}
fn delete_folder(
fn delete_mailbox(
&mut self,
_folder_hash: FolderHash,
) -> Result<FnvHashMap<FolderHash, Folder>> {
_mailbox_hash: MailboxHash,
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
Err(MeliError::new("Unimplemented."))
}
fn set_folder_subscription(&mut self, _folder_hash: FolderHash, _val: bool) -> Result<()> {
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
fn rename_folder(&mut self, _folder_hash: FolderHash, _new_path: String) -> Result<Folder> {
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
Err(MeliError::new("Unimplemented."))
}
fn set_folder_permissions(
fn set_mailbox_permissions(
&mut self,
_folder_hash: FolderHash,
_val: crate::backends::FolderPermissions,
_mailbox_hash: MailboxHash,
_val: crate::backends::MailboxPermissions,
) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
@ -708,12 +708,12 @@ impl MaildirType {
settings: &AccountSettings,
is_subscribed: Box<dyn Fn(&str) -> bool>,
) -> Result<Box<dyn MailBackend>> {
let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
fn recurse_folders<P: AsRef<Path>>(
folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
let mut mailboxes: FnvHashMap<MailboxHash, MaildirMailbox> = Default::default();
fn recurse_mailboxes<P: AsRef<Path>>(
mailboxes: &mut FnvHashMap<MailboxHash, MaildirMailbox>,
settings: &AccountSettings,
p: P,
) -> Result<Vec<FolderHash>> {
) -> Result<Vec<MailboxHash>> {
if !p.as_ref().exists() || !p.as_ref().is_dir() {
return Err(MeliError::new(format!(
"Configuration error: Path \"{}\" {}",
@ -734,20 +734,20 @@ impl MaildirType {
continue 'entries;
}
if path.is_dir() {
if let Ok(mut f) = MaildirFolder::new(
if let Ok(mut f) = MaildirMailbox::new(
path.to_str().unwrap().to_string(),
path.file_name().unwrap().to_str().unwrap().to_string(),
None,
Vec::new(),
&settings,
) {
f.children = recurse_folders(folders, settings, &path)?;
f.children = recurse_mailboxes(mailboxes, settings, &path)?;
f.children
.iter()
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = Some(f.hash)))
.count();
children.push(f.hash);
folders.insert(f.hash, f);
mailboxes.insert(f.hash, f);
}
}
}
@ -755,55 +755,55 @@ impl MaildirType {
}
Ok(children)
};
let root_path = PathBuf::from(settings.root_folder()).expand();
let root_path = PathBuf::from(settings.root_mailbox()).expand();
if !root_path.exists() {
return Err(MeliError::new(format!(
"Configuration error ({}): root_path `{}` is not a valid directory.",
settings.name(),
settings.root_folder.as_str()
settings.root_mailbox.as_str()
)));
} else if !root_path.is_dir() {
return Err(MeliError::new(format!(
"Configuration error ({}): root_path `{}` is not a directory.",
settings.name(),
settings.root_folder.as_str()
settings.root_mailbox.as_str()
)));
}
if let Ok(f) = MaildirFolder::new(
if let Ok(f) = MaildirMailbox::new(
root_path.to_str().unwrap().to_string(),
root_path.file_name().unwrap().to_str().unwrap().to_string(),
None,
Vec::with_capacity(0),
settings,
) {
folders.insert(f.hash, f);
mailboxes.insert(f.hash, f);
}
if folders.is_empty() {
let children = recurse_folders(&mut folders, settings, &root_path)?;
if mailboxes.is_empty() {
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
children
.iter()
.map(|c| folders.get_mut(c).map(|f| f.parent = None))
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = None))
.count();
} else {
let root_hash = *folders.keys().nth(0).unwrap();
let children = recurse_folders(&mut folders, settings, &root_path)?;
let root_hash = *mailboxes.keys().nth(0).unwrap();
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
children
.iter()
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = Some(root_hash)))
.count();
folders.get_mut(&root_hash).map(|f| f.children = children);
mailboxes.get_mut(&root_hash).map(|f| f.children = children);
}
for f in folders.values_mut() {
for f in mailboxes.values_mut() {
if is_subscribed(f.path()) {
f.is_subscribed = true;
}
}
let mut hash_indexes =
FnvHashMap::with_capacity_and_hasher(folders.len(), Default::default());
for &fh in folders.keys() {
FnvHashMap::with_capacity_and_hasher(mailboxes.len(), Default::default());
for &fh in mailboxes.keys() {
hash_indexes.insert(
fh,
HashIndex {
@ -814,37 +814,37 @@ impl MaildirType {
}
Ok(Box::new(MaildirType {
name: settings.name().to_string(),
folders,
mailboxes,
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
folder_index: Default::default(),
mailbox_index: Default::default(),
path: root_path,
}))
}
fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
fn owned_mailbox_idx(&self, mailbox: &Mailbox) -> MailboxHash {
*self
.folders
.mailboxes
.iter()
.find(|(_, f)| f.hash() == folder.hash())
.find(|(_, f)| f.hash() == mailbox.hash())
.unwrap()
.0
}
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
pub fn multicore(&mut self, cores: usize, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
let handle = {
let tx = w.tx();
let folder: &MaildirFolder = &self.folders[&self.owned_folder_idx(folder)];
let folder_hash = folder.hash();
let unseen = folder.unseen.clone();
let total = folder.total.clone();
let mailbox: &MaildirMailbox = &self.mailboxes[&self.owned_mailbox_idx(mailbox)];
let mailbox_hash = mailbox.hash();
let unseen = mailbox.unseen.clone();
let total = mailbox.total.clone();
let tx_final = w.tx();
let mut path: PathBuf = folder.fs_path().into();
let name = format!("parsing {:?}", folder.name());
let mut path: PathBuf = mailbox.fs_path().into();
let name = format!("parsing {:?}", mailbox.name());
let root_path = self.path.to_path_buf();
let map = self.hash_indexes.clone();
let folder_index = self.folder_index.clone();
let mailbox_index = self.mailbox_index.clone();
let closure = move |work_context: crate::async_workers::WorkContext| {
work_context
@ -886,7 +886,7 @@ impl MaildirType {
let cache_dir = cache_dir.clone();
let tx = tx.clone();
let map = map.clone();
let folder_index = folder_index.clone();
let mailbox_index = mailbox_index.clone();
let root_path = root_path.clone();
let s = scope.builder().name(name.clone()).spawn(move |_| {
let len = chunk.len();
@ -896,7 +896,7 @@ impl MaildirType {
for c in chunk.chunks(size) {
//thread::yield_now();
let map = map.clone();
let folder_index = folder_index.clone();
let mailbox_index = mailbox_index.clone();
let len = c.len();
for file in c {
/* Check if we have a cache file with this email's
@ -916,13 +916,13 @@ impl MaildirType {
bincode::deserialize_from(reader);
if let Ok(env) = result {
let mut map = map.lock().unwrap();
let map = map.entry(folder_hash).or_default();
let map = map.entry(mailbox_hash).or_default();
let hash = env.hash();
map.insert(hash, file.clone().into());
folder_index
mailbox_index
.lock()
.unwrap()
.insert(hash, folder_hash);
.insert(hash, mailbox_hash);
if !env.is_seen() {
*unseen.lock().unwrap() += 1;
}
@ -934,19 +934,19 @@ impl MaildirType {
let hash = get_file_hash(file);
{
let mut map = map.lock().unwrap();
let map = map.entry(folder_hash).or_default();
let map = map.entry(mailbox_hash).or_default();
(*map).insert(hash, PathBuf::from(file).into());
}
let op = Box::new(MaildirOp::new(
hash,
map.clone(),
folder_hash,
mailbox_hash,
));
if let Some(e) = Envelope::from_token(op, hash) {
folder_index
mailbox_index
.lock()
.unwrap()
.insert(e.hash(), folder_hash);
.insert(e.hash(), mailbox_hash);
if let Ok(cached) =
cache_dir.place_cache_file(file_name)
{
@ -1011,7 +1011,7 @@ impl MaildirType {
w.build(handle)
}
pub fn save_to_folder(mut path: PathBuf, bytes: &[u8], flags: Option<Flag>) -> Result<()> {
pub fn save_to_mailbox(mut path: PathBuf, bytes: &[u8], flags: Option<Flag>) -> Result<()> {
path.push("cur");
{
let mut rand_buf = [0u8; 16];
@ -1071,18 +1071,18 @@ impl MaildirType {
}
pub fn validate_config(s: &AccountSettings) -> Result<()> {
let root_path = PathBuf::from(s.root_folder()).expand();
let root_path = PathBuf::from(s.root_mailbox()).expand();
if !root_path.exists() {
return Err(MeliError::new(format!(
"Configuration error ({}): root_path `{}` is not a valid directory.",
s.name(),
s.root_folder.as_str()
s.root_mailbox.as_str()
)));
} else if !root_path.is_dir() {
return Err(MeliError::new(format!(
"Configuration error ({}): root_path `{}` is not a directory.",
s.name(),
s.root_folder.as_str()
s.root_mailbox.as_str()
)));
}
@ -1092,7 +1092,7 @@ impl MaildirType {
fn add_path_to_index(
hash_index: &HashIndexes,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
path: &Path,
cache_dir: &xdg::BaseDirectories,
file_name: PathBuf,
@ -1102,16 +1102,16 @@ fn add_path_to_index(
let hash = get_file_hash(path);
{
let mut map = hash_index.lock().unwrap();
let map = map.entry(folder_hash).or_default();
let map = map.entry(mailbox_hash).or_default();
map.insert(hash, path.to_path_buf().into());
debug!(
"inserted {} in {} map, len={}",
hash,
folder_hash,
mailbox_hash,
map.len()
);
}
let op = Box::new(MaildirOp::new(hash, hash_index.clone(), folder_hash));
let op = Box::new(MaildirOp::new(hash, hash_index.clone(), mailbox_hash));
if let Some(e) = Envelope::from_token(op, hash) {
debug!("add_path_to_index gen {}\t{}", hash, file_name.display());
if let Ok(cached) = cache_dir.place_cache_file(file_name) {

View File

@ -25,9 +25,9 @@
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::BackendOp;
use crate::backends::FolderHash;
use crate::backends::MailboxHash;
use crate::backends::{
BackendFolder, Folder, FolderPermissions, MailBackend, RefreshEvent, RefreshEventConsumer,
BackendMailbox, MailBackend, Mailbox, MailboxPermissions, RefreshEvent, RefreshEventConsumer,
RefreshEventKind, SpecialUsageMailbox,
};
use crate::conf::AccountSettings;
@ -74,22 +74,22 @@ fn get_rw_lock_blocking(f: &File) {
}
#[derive(Debug)]
struct MboxFolder {
hash: FolderHash,
struct MboxMailbox {
hash: MailboxHash,
name: String,
path: PathBuf,
content: Vec<u8>,
children: Vec<FolderHash>,
parent: Option<FolderHash>,
children: Vec<MailboxHash>,
parent: Option<MailboxHash>,
usage: Arc<RwLock<SpecialUsageMailbox>>,
is_subscribed: bool,
permissions: FolderPermissions,
permissions: MailboxPermissions,
pub total: Arc<Mutex<usize>>,
pub unseen: Arc<Mutex<usize>>,
}
impl BackendFolder for MboxFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for MboxMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
@ -106,8 +106,8 @@ impl BackendFolder for MboxFolder {
self.name = s.to_string();
}
fn clone(&self) -> Folder {
Box::new(MboxFolder {
fn clone(&self) -> Mailbox {
Box::new(MboxMailbox {
hash: self.hash,
name: self.name.clone(),
path: self.path.clone(),
@ -122,11 +122,11 @@ impl BackendFolder for MboxFolder {
})
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.children
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
self.parent
}
@ -134,7 +134,7 @@ impl BackendFolder for MboxFolder {
*self.usage.read().unwrap()
}
fn permissions(&self) -> FolderPermissions {
fn permissions(&self) -> MailboxPermissions {
self.permissions
}
fn is_subscribed(&self) -> bool {
@ -388,28 +388,28 @@ pub fn mbox_parse(
pub struct MboxType {
path: PathBuf,
index: Arc<Mutex<FnvHashMap<EnvelopeHash, (Offset, Length)>>>,
folders: Arc<Mutex<FnvHashMap<FolderHash, MboxFolder>>>,
mailboxes: Arc<Mutex<FnvHashMap<MailboxHash, MboxMailbox>>>,
}
impl MailBackend for MboxType {
fn is_online(&self) -> Result<()> {
Ok(())
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let handle = {
let tx = w.tx();
let index = self.index.clone();
let folder_path = folder.path().to_string();
let folder_hash = folder.hash();
let folders = self.folders.clone();
let mailbox_path = mailbox.path().to_string();
let mailbox_hash = mailbox.hash();
let mailboxes = self.mailboxes.clone();
let closure = move |_work_context| {
let tx = tx.clone();
let index = index.clone();
let file = match std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&folder_path)
.open(&mailbox_path)
{
Ok(f) => f,
Err(e) => {
@ -431,9 +431,9 @@ impl MailBackend for MboxType {
.to_full_result()
.map_err(|e| MeliError::from(e));
{
let mut folder_lock = folders.lock().unwrap();
folder_lock
.entry(folder_hash)
let mut mailbox_lock = mailboxes.lock().unwrap();
mailbox_lock
.entry(mailbox_hash)
.and_modify(|f| f.content = contents);
}
@ -454,7 +454,7 @@ impl MailBackend for MboxType {
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
.map_err(|e| e.to_string())
.map_err(MeliError::new)?;
for f in self.folders.lock().unwrap().values() {
for f in self.mailboxes.lock().unwrap().values() {
watcher
.watch(&f.path, RecursiveMode::Recursive)
.map_err(|e| e.to_string())
@ -462,7 +462,7 @@ impl MailBackend for MboxType {
debug!("watching {:?}", f.path.as_path());
}
let index = self.index.clone();
let folders = self.folders.clone();
let mailboxes = self.mailboxes.clone();
let handle = std::thread::Builder::new()
.name(format!(
"watching {}",
@ -473,7 +473,7 @@ impl MailBackend for MboxType {
let _watcher = watcher;
let _work_context = work_context;
let index = index;
let folders = folders;
let mailboxes = mailboxes;
loop {
match rx.recv() {
/*
@ -490,7 +490,7 @@ impl MailBackend for MboxType {
/* Update */
DebouncedEvent::NoticeWrite(pathbuf)
| DebouncedEvent::Write(pathbuf) => {
let folder_hash = get_path_hash!(&pathbuf);
let mailbox_hash = get_path_hash!(&pathbuf);
let file = match std::fs::OpenOptions::new()
.read(true)
.write(true)
@ -502,7 +502,7 @@ impl MailBackend for MboxType {
}
};
get_rw_lock_blocking(&file);
let mut folder_lock = folders.lock().unwrap();
let mut mailbox_lock = mailboxes.lock().unwrap();
let mut buf_reader = BufReader::new(file);
let mut contents = Vec::new();
if let Err(e) = buf_reader.read_to_end(&mut contents) {
@ -510,46 +510,46 @@ impl MailBackend for MboxType {
continue;
};
if contents
.starts_with(folder_lock[&folder_hash].content.as_slice())
.starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
{
if let Ok(envelopes) = mbox_parse(
index.clone(),
&contents[folder_lock[&folder_hash].content.len()..],
folder_lock[&folder_hash].content.len(),
&contents[mailbox_lock[&mailbox_hash].content.len()..],
mailbox_lock[&mailbox_hash].content.len(),
)
.to_full_result()
{
for env in envelopes {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Create(Box::new(env)),
});
}
}
} else {
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Rescan,
});
}
folder_lock
.entry(folder_hash)
mailbox_lock
.entry(mailbox_hash)
.and_modify(|f| f.content = contents);
}
/* Remove */
DebouncedEvent::NoticeRemove(pathbuf)
| DebouncedEvent::Remove(pathbuf) => {
if folders
if mailboxes
.lock()
.unwrap()
.values()
.any(|f| &f.path == &pathbuf)
{
let folder_hash = get_path_hash!(&pathbuf);
let mailbox_hash = get_path_hash!(&pathbuf);
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Failure(MeliError::new(format!(
"mbox folder {} was removed.",
"mbox mailbox {} was removed.",
pathbuf.display()
))),
});
@ -557,12 +557,12 @@ impl MailBackend for MboxType {
}
}
DebouncedEvent::Rename(src, dest) => {
if folders.lock().unwrap().values().any(|f| &f.path == &src) {
let folder_hash = get_path_hash!(&src);
if mailboxes.lock().unwrap().values().any(|f| &f.path == &src) {
let mailbox_hash = get_path_hash!(&src);
sender.send(RefreshEvent {
hash: folder_hash,
hash: mailbox_hash,
kind: RefreshEventKind::Failure(MeliError::new(format!(
"mbox folder {} was renamed to {}.",
"mbox mailbox {} was renamed to {}.",
src.display(),
dest.display()
))),
@ -570,9 +570,9 @@ impl MailBackend for MboxType {
return;
}
}
/* Trigger rescan of folders */
/* Trigger rescan of mailboxes */
DebouncedEvent::Rescan => {
for h in folders.lock().unwrap().keys() {
for h in mailboxes.lock().unwrap().keys() {
sender.send(RefreshEvent {
hash: *h,
kind: RefreshEventKind::Rescan,
@ -588,13 +588,13 @@ impl MailBackend for MboxType {
})?;
Ok(handle.thread().id())
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
Ok(self
.folders
.mailboxes
.lock()
.unwrap()
.iter()
.map(|(h, f)| (*h, f.clone() as Folder))
.map(|(h, f)| (*h, f.clone() as Mailbox))
.collect())
}
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
@ -605,7 +605,7 @@ impl MailBackend for MboxType {
Box::new(MboxOp::new(hash, self.path.as_path(), offset, length))
}
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
@ -619,11 +619,11 @@ impl MboxType {
s: &AccountSettings,
_is_subscribed: Box<dyn Fn(&str) -> bool>,
) -> Result<Box<dyn MailBackend>> {
let path = Path::new(s.root_folder.as_str()).expand();
let path = Path::new(s.root_mailbox.as_str()).expand();
if !path.exists() {
return Err(MeliError::new(format!(
"\"root_folder\" {} for account {} is not a valid path.",
s.root_folder.as_str(),
"\"root_mailbox\" {} for account {} is not a valid path.",
s.root_mailbox.as_str(),
s.name()
)));
}
@ -644,9 +644,9 @@ impl MboxType {
true
};
ret.folders.lock().unwrap().insert(
ret.mailboxes.lock().unwrap().insert(
hash,
MboxFolder {
MboxMailbox {
hash,
path: ret.path.clone(),
name,
@ -655,7 +655,7 @@ impl MboxType {
parent: None,
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
is_subscribed: true,
permissions: FolderPermissions {
permissions: MailboxPermissions {
create_messages: !read_only,
remove_messages: !read_only,
set_flags: !read_only,
@ -671,8 +671,8 @@ impl MboxType {
);
/*
/* Look for other mailboxes */
let parent_folder = Path::new(path).parent().unwrap();
let read_dir = std::fs::read_dir(parent_folder);
let parent_mailbox = Path::new(path).parent().unwrap();
let read_dir = std::fs::read_dir(parent_mailbox);
if read_dir.is_ok() {
for f in read_dir.unwrap() {
if f.is_err() {
@ -685,9 +685,9 @@ impl MboxType {
.map(|f| f.to_string_lossy().into())
.unwrap_or(String::new());
let hash = get_path_hash!(f);
ret.folders.lock().unwrap().insert(
ret.mailboxes.lock().unwrap().insert(
hash,
MboxFolder {
MboxMailbox {
hash,
path: f,
name,
@ -704,11 +704,11 @@ impl MboxType {
}
pub fn validate_config(s: &AccountSettings) -> Result<()> {
let path = Path::new(s.root_folder.as_str()).expand();
let path = Path::new(s.root_mailbox.as_str()).expand();
if !path.exists() {
return Err(MeliError::new(format!(
"\"root_folder\" {} for account {} is not a valid path.",
s.root_folder.as_str(),
"\"root_mailbox\" {} for account {} is not a valid path.",
s.root_mailbox.as_str(),
s.name()
)));
}

View File

@ -20,9 +20,9 @@
*/
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use crate::backends::FolderHash;
use crate::backends::MailboxHash;
use crate::backends::{
BackendFolder, BackendOp, Folder, FolderPermissions, MailBackend, RefreshEventConsumer,
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxPermissions, RefreshEventConsumer,
SpecialUsageMailbox,
};
use crate::conf::AccountSettings;
@ -54,7 +54,7 @@ unsafe impl Sync for DbWrapper {}
#[derive(Debug)]
pub struct NotmuchDb {
database: DbWrapper,
folders: Arc<RwLock<FnvHashMap<FolderHash, NotmuchFolder>>>,
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, NotmuchMailbox>>>,
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
path: PathBuf,
@ -66,7 +66,7 @@ unsafe impl Sync for NotmuchDb {}
impl Drop for NotmuchDb {
fn drop(&mut self) {
for f in self.folders.write().unwrap().values_mut() {
for f in self.mailboxes.write().unwrap().values_mut() {
if let Some(query) = f.query.take() {
unsafe {
notmuch_query_destroy(query);
@ -82,10 +82,10 @@ impl Drop for NotmuchDb {
}
#[derive(Debug, Clone, Default)]
struct NotmuchFolder {
hash: FolderHash,
children: Vec<FolderHash>,
parent: Option<FolderHash>,
struct NotmuchMailbox {
hash: MailboxHash,
children: Vec<MailboxHash>,
parent: Option<MailboxHash>,
name: String,
path: String,
query_str: String,
@ -97,8 +97,8 @@ struct NotmuchFolder {
unseen: Arc<Mutex<usize>>,
}
impl BackendFolder for NotmuchFolder {
fn hash(&self) -> FolderHash {
impl BackendMailbox for NotmuchMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
@ -112,15 +112,15 @@ impl BackendFolder for NotmuchFolder {
fn change_name(&mut self, _s: &str) {}
fn clone(&self) -> Folder {
fn clone(&self) -> Mailbox {
Box::new(std::clone::Clone::clone(self))
}
fn children(&self) -> &[FolderHash] {
fn children(&self) -> &[MailboxHash] {
&self.children
}
fn parent(&self) -> Option<FolderHash> {
fn parent(&self) -> Option<MailboxHash> {
self.parent
}
@ -128,8 +128,8 @@ impl BackendFolder for NotmuchFolder {
*self.usage.read().unwrap()
}
fn permissions(&self) -> FolderPermissions {
FolderPermissions::default()
fn permissions(&self) -> MailboxPermissions {
MailboxPermissions::default()
}
fn is_subscribed(&self) -> bool {
@ -150,8 +150,8 @@ impl BackendFolder for NotmuchFolder {
}
}
unsafe impl Send for NotmuchFolder {}
unsafe impl Sync for NotmuchFolder {}
unsafe impl Send for NotmuchMailbox {}
unsafe impl Sync for NotmuchMailbox {}
impl NotmuchDb {
pub fn new(
@ -159,11 +159,11 @@ impl NotmuchDb {
_is_subscribed: Box<dyn Fn(&str) -> bool>,
) -> Result<Box<dyn MailBackend>> {
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
let path = Path::new(s.root_mailbox.as_str()).expand().to_path_buf();
if !path.exists() {
return Err(MeliError::new(format!(
"\"root_folder\" {} for account {} is not a valid path.",
s.root_folder.as_str(),
"\"root_mailbox\" {} for account {} is not a valid path.",
s.root_mailbox.as_str(),
s.name()
)));
}
@ -180,22 +180,22 @@ impl NotmuchDb {
if status != 0 {
return Err(MeliError::new(format!(
"Could not open notmuch database at path {}. notmuch_database_open returned {}.",
s.root_folder.as_str(),
s.root_mailbox.as_str(),
status
)));
}
assert!(!database.is_null());
let mut folders = FnvHashMap::default();
for (k, f) in s.folders.iter() {
let mut mailboxes = FnvHashMap::default();
for (k, f) in s.mailboxes.iter() {
if let Some(query_str) = f.extra.get("query") {
let hash = {
let mut h = DefaultHasher::new();
k.hash(&mut h);
h.finish()
};
folders.insert(
mailboxes.insert(
hash,
NotmuchFolder {
NotmuchMailbox {
hash,
name: k.to_string(),
path: k.to_string(),
@ -211,7 +211,7 @@ impl NotmuchDb {
);
} else {
return Err(MeliError::new(format!(
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
"notmuch mailbox configuration entry \"{}\" should have a \"query\" value set.",
k
)));
}
@ -225,24 +225,24 @@ impl NotmuchDb {
index: Arc::new(RwLock::new(Default::default())),
tag_index: Arc::new(RwLock::new(Default::default())),
folders: Arc::new(RwLock::new(folders)),
mailboxes: Arc::new(RwLock::new(mailboxes)),
save_messages_to: None,
}))
}
pub fn validate_config(s: &AccountSettings) -> Result<()> {
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
let path = Path::new(s.root_mailbox.as_str()).expand().to_path_buf();
if !path.exists() {
return Err(MeliError::new(format!(
"\"root_folder\" {} for account {} is not a valid path.",
s.root_folder.as_str(),
"\"root_mailbox\" {} for account {} is not a valid path.",
s.root_mailbox.as_str(),
s.name()
)));
}
for (k, f) in s.folders.iter() {
for (k, f) in s.mailboxes.iter() {
if f.extra.get("query").is_none() {
return Err(MeliError::new(format!(
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
"notmuch mailbox configuration entry \"{}\" should have a \"query\" value set.",
k
)));
}
@ -288,21 +288,21 @@ impl MailBackend for NotmuchDb {
fn is_online(&self) -> Result<()> {
Ok(())
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let folder_hash = folder.hash();
let mailbox_hash = mailbox.hash();
let database = self.database.clone();
let index = self.index.clone();
let tag_index = self.tag_index.clone();
let folders = self.folders.clone();
let mailboxes = self.mailboxes.clone();
let handle = {
let tx = w.tx();
let closure = move |_work_context| {
let mut ret: Vec<Envelope> = Vec::new();
let database_lck = database.inner.read().unwrap();
let mut folders_lck = folders.write().unwrap();
let folder = folders_lck.get_mut(&folder_hash).unwrap();
let query_str = std::ffi::CString::new(folder.query_str.as_str()).unwrap();
let mut mailboxes_lck = mailboxes.write().unwrap();
let mailbox = mailboxes_lck.get_mut(&mailbox_hash).unwrap();
let query_str = std::ffi::CString::new(mailbox.query_str.as_str()).unwrap();
let query: *mut notmuch_query_t =
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
if query.is_null() {
@ -319,7 +319,7 @@ impl MailBackend for NotmuchDb {
if status != 0 {
tx.send(AsyncStatus::Payload(Err(MeliError::new(format!(
"Search for {} returned {}",
folder.query_str.as_str(),
mailbox.query_str.as_str(),
status,
)))))
.unwrap();
@ -383,7 +383,7 @@ impl MailBackend for NotmuchDb {
index.write().unwrap().remove(&env_hash);
}
}
folder.query = Some(query);
mailbox.query = Some(query);
tx.send(AsyncStatus::Payload(Ok(ret))).unwrap();
tx.send(AsyncStatus::Finished).unwrap();
};
@ -405,13 +405,13 @@ impl MailBackend for NotmuchDb {
.spawn(move || {})?;
Ok(handle.thread().id())
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
Ok(self
.folders
.mailboxes
.read()
.unwrap()
.iter()
.map(|(k, f)| (*k, BackendFolder::clone(f)))
.map(|(k, f)| (*k, BackendMailbox::clone(f)))
.collect())
}
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
@ -424,7 +424,7 @@ impl MailBackend for NotmuchDb {
})
}
fn save(&self, bytes: &[u8], _folder: &str, flags: Option<Flag>) -> Result<()> {
fn save(&self, bytes: &[u8], _mailbox: &str, flags: Option<Flag>) -> Result<()> {
let mut path = self
.save_messages_to
.as_ref()
@ -435,7 +435,7 @@ impl MailBackend for NotmuchDb {
path.push(d);
if !path.is_dir() {
return Err(MeliError::new(format!(
"{} is not a valid maildir folder",
"{} is not a valid maildir mailbox",
path.display()
)));
}
@ -443,7 +443,7 @@ impl MailBackend for NotmuchDb {
}
path.push("cur");
}
crate::backends::MaildirType::save_to_folder(path, bytes, flags)
crate::backends::MaildirType::save_to_mailbox(path, bytes, flags)
}
fn as_any(&self) -> &dyn::std::any::Any {

View File

@ -20,7 +20,7 @@
*/
use super::*;
use crate::backends::FolderHash;
use crate::backends::MailboxHash;
use core::ops::{Index, IndexMut};
use smallvec::SmallVec;
use std::collections::BTreeMap;
@ -67,9 +67,9 @@ pub struct Collection {
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
pub threads: FnvHashMap<FolderHash, Threads>,
sent_folder: Option<FolderHash>,
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
pub threads: FnvHashMap<MailboxHash, Threads>,
sent_mailbox: Option<MailboxHash>,
pub mailboxes: FnvHashMap<MailboxHash, FnvHashSet<EnvelopeHash>>,
}
impl Drop for Collection {
@ -112,7 +112,7 @@ impl Collection {
subject_index,
threads,
mailboxes,
sent_folder: None,
sent_mailbox: None,
}
}
@ -124,18 +124,18 @@ impl Collection {
self.envelopes.read().unwrap().is_empty()
}
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
pub fn remove(&mut self, envelope_hash: EnvelopeHash, mailbox_hash: MailboxHash) {
debug!("DEBUG: Removing {}", envelope_hash);
self.envelopes.write().unwrap().remove(&envelope_hash);
self.mailboxes.entry(folder_hash).and_modify(|m| {
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
m.remove(&envelope_hash);
});
self.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.remove(envelope_hash);
for (h, t) in self.threads.iter_mut() {
if *h == folder_hash {
if *h == mailbox_hash {
continue;
}
t.remove(envelope_hash);
@ -146,13 +146,13 @@ impl Collection {
&mut self,
old_hash: EnvelopeHash,
new_hash: EnvelopeHash,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
) {
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
return;
}
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
self.mailboxes.entry(folder_hash).and_modify(|m| {
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
m.remove(&old_hash);
m.insert(new_hash);
});
@ -163,7 +163,7 @@ impl Collection {
{
if self
.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.update_envelope(&self.envelopes, old_hash, new_hash)
.is_ok()
@ -173,11 +173,11 @@ impl Collection {
}
/* envelope is not in threads, so insert it */
self.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.insert(&mut self.envelopes, new_hash);
for (h, t) in self.threads.iter_mut() {
if *h == folder_hash {
if *h == mailbox_hash {
continue;
}
t.update_envelope(&self.envelopes, old_hash, new_hash)
@ -187,14 +187,14 @@ impl Collection {
}
/// Merge new mailbox to collection and update threads.
/// Returns a list of already existing folders whose threads were updated
/// Returns a list of already existing mailboxs whose threads were updated
pub fn merge(
&mut self,
mut new_envelopes: FnvHashMap<EnvelopeHash, Envelope>,
folder_hash: FolderHash,
sent_folder: Option<FolderHash>,
) -> Option<SmallVec<[FolderHash; 8]>> {
self.sent_folder = sent_folder;
mailbox_hash: MailboxHash,
sent_mailbox: Option<MailboxHash>,
) -> Option<SmallVec<[MailboxHash; 8]>> {
self.sent_mailbox = sent_mailbox;
for (h, e) in new_envelopes.iter() {
self.message_ids.insert(e.message_id().raw().to_vec(), *h);
}
@ -203,21 +203,21 @@ impl Collection {
ref mut threads,
ref mut envelopes,
ref mut mailboxes,
ref sent_folder,
ref sent_mailbox,
..
} = self;
if !threads.contains_key(&folder_hash) {
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
if !threads.contains_key(&mailbox_hash) {
threads.insert(mailbox_hash, Threads::new(new_envelopes.len()));
mailboxes.insert(mailbox_hash, new_envelopes.keys().cloned().collect());
for (h, e) in new_envelopes {
envelopes.write().unwrap().insert(h, e);
}
} else {
mailboxes.entry(folder_hash).and_modify(|m| {
mailboxes.entry(mailbox_hash).and_modify(|m| {
m.extend(new_envelopes.keys().cloned());
});
threads.entry(folder_hash).and_modify(|t| {
threads.entry(mailbox_hash).and_modify(|t| {
let mut ordered_hash_set =
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
ordered_hash_set.sort_by(|a, b| {
@ -237,14 +237,14 @@ impl Collection {
}
let mut ret = SmallVec::new();
let keys = threads.keys().cloned().collect::<Vec<FolderHash>>();
let keys = threads.keys().cloned().collect::<Vec<MailboxHash>>();
for t_fh in keys {
if t_fh == folder_hash {
if t_fh == mailbox_hash {
continue;
}
if sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
if sent_mailbox.map(|f| f == mailbox_hash).unwrap_or(false) {
let envelopes_lck = envelopes.read().unwrap();
let mut ordered_hash_set = threads[&folder_hash]
let mut ordered_hash_set = threads[&mailbox_hash]
.hash_set
.iter()
.cloned()
@ -265,7 +265,7 @@ impl Collection {
}
continue;
}
if sent_folder.map(|f| f == t_fh).unwrap_or(false) {
if sent_mailbox.map(|f| f == t_fh).unwrap_or(false) {
let envelopes_lck = envelopes.read().unwrap();
let mut ordered_hash_set = threads[&t_fh]
.hash_set
@ -282,12 +282,12 @@ impl Collection {
let mut updated = false;
for h in ordered_hash_set {
updated |= threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.insert_reply(envelopes, h);
}
if updated {
ret.push(folder_hash);
ret.push(mailbox_hash);
}
}
}
@ -302,19 +302,23 @@ impl Collection {
&mut self,
old_hash: EnvelopeHash,
mut envelope: Envelope,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
) {
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
envelope.set_thread(old_env.thread());
let new_hash = envelope.hash();
self.mailboxes.entry(folder_hash).and_modify(|m| {
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
m.remove(&old_hash);
m.insert(new_hash);
});
self.message_ids
.insert(envelope.message_id().raw().to_vec(), new_hash);
self.envelopes.write().unwrap().insert(new_hash, envelope);
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
if self
.sent_mailbox
.map(|f| f == mailbox_hash)
.unwrap_or(false)
{
for (_, t) in self.threads.iter_mut() {
t.update_envelope(&self.envelopes, old_hash, new_hash)
.unwrap_or(());
@ -323,7 +327,7 @@ impl Collection {
{
if self
.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.update_envelope(&self.envelopes, old_hash, new_hash)
.is_ok()
@ -333,11 +337,11 @@ impl Collection {
}
/* envelope is not in threads, so insert it */
self.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.insert(&mut self.envelopes, new_hash);
for (h, t) in self.threads.iter_mut() {
if *h == folder_hash {
if *h == mailbox_hash {
continue;
}
t.update_envelope(&self.envelopes, old_hash, new_hash)
@ -346,19 +350,23 @@ impl Collection {
}
}
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
pub fn insert(&mut self, envelope: Envelope, mailbox_hash: MailboxHash) {
let hash = envelope.hash();
self.mailboxes.entry(folder_hash).and_modify(|m| {
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
m.insert(hash);
});
self.message_ids
.insert(envelope.message_id().raw().to_vec(), hash);
self.envelopes.write().unwrap().insert(hash, envelope);
self.threads
.entry(folder_hash)
.entry(mailbox_hash)
.or_default()
.insert(&mut self.envelopes, hash);
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
if self
.sent_mailbox
.map(|f| f == mailbox_hash)
.unwrap_or(false)
{
self.insert_reply(hash);
}
}
@ -385,15 +393,15 @@ impl Collection {
}
}
impl Index<&FolderHash> for Collection {
impl Index<&MailboxHash> for Collection {
type Output = FnvHashSet<EnvelopeHash>;
fn index(&self, index: &FolderHash) -> &FnvHashSet<EnvelopeHash> {
fn index(&self, index: &MailboxHash) -> &FnvHashSet<EnvelopeHash> {
&self.mailboxes[index]
}
}
impl IndexMut<&FolderHash> for Collection {
fn index_mut(&mut self, index: &FolderHash) -> &mut FnvHashSet<EnvelopeHash> {
impl IndexMut<&MailboxHash> for Collection {
fn index_mut(&mut self, index: &MailboxHash) -> &mut FnvHashSet<EnvelopeHash> {
self.mailboxes.get_mut(index).unwrap()
}
}

View File

@ -25,14 +25,14 @@ use std::collections::HashMap;
#[derive(Debug, Serialize, Default, Clone)]
pub struct AccountSettings {
pub name: String,
pub root_folder: String,
pub root_mailbox: String,
pub format: String,
pub identity: String,
pub read_only: bool,
pub display_name: Option<String>,
pub subscribed_folders: Vec<String>,
pub subscribed_mailboxes: Vec<String>,
#[serde(default)]
pub folders: HashMap<String, FolderConf>,
pub mailboxes: HashMap<String, MailboxConf>,
#[serde(default)]
pub manual_refresh: bool,
#[serde(flatten)]
@ -49,8 +49,8 @@ impl AccountSettings {
pub fn set_name(&mut self, s: String) {
self.name = s;
}
pub fn root_folder(&self) -> &str {
&self.root_folder
pub fn root_mailbox(&self) -> &str {
&self.root_mailbox
}
pub fn identity(&self) -> &str {
&self.identity
@ -62,8 +62,8 @@ impl AccountSettings {
self.display_name.as_ref()
}
pub fn subscribed_folders(&self) -> &Vec<String> {
&self.subscribed_folders
pub fn subscribed_mailboxes(&self) -> &Vec<String> {
&self.subscribed_mailboxes
}
#[cfg(feature = "vcard")]
@ -74,7 +74,7 @@ impl AccountSettings {
#[serde(default)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FolderConf {
pub struct MailboxConf {
pub alias: Option<String>,
#[serde(default = "true_val")]
pub autoload: bool,
@ -88,9 +88,9 @@ pub struct FolderConf {
pub extra: HashMap<String, String>,
}
impl Default for FolderConf {
impl Default for MailboxConf {
fn default() -> Self {
FolderConf {
MailboxConf {
alias: None,
autoload: true,
subscribe: ToggleFlag::Unset,
@ -101,7 +101,7 @@ impl Default for FolderConf {
}
}
impl FolderConf {
impl MailboxConf {
pub fn alias(&self) -> Option<&str> {
self.alias.as_ref().map(String::as_str)
}

View File

@ -650,7 +650,7 @@ impl Threads {
&mut self,
envelopes: &mut Envelopes,
env_hash: EnvelopeHash,
other_folder: bool,
other_mailbox: bool,
) -> bool {
let envelopes_lck = envelopes.read().unwrap();
let reply_to_id: Option<ThreadNodeHash> = envelopes_lck[&env_hash]
@ -664,7 +664,7 @@ impl Threads {
return false;
}
if other_folder
if other_mailbox
&& reply_to_id.is_none()
&& !self.message_ids.contains_key(message_id)
&& !envelopes_lck[&env_hash]

View File

@ -9,29 +9,29 @@
#
# Setting up a Maildir account
#[accounts.account-name]
#root_folder = "/path/to/root/folder"
#root_mailbox = "/path/to/root/mailbox"
#format = "Maildir"
#index_style = "Conversations" # or [plain, threaded, compact]
#identity="email@address.tld"
#display_name = "Name"
#subscribed_folders = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
# Set folder-specific settings
# [accounts.account-name.folders]
# Set mailbox-specific settings
# [accounts.account-name.mailboxes]
# "INBOX" = { rename="Inbox" }
# "drafts" = { rename="Drafts" }
# "foobar-devel" = { ignore = true } # don't show notifications for this folder
# "foobar-devel" = { ignore = true } # don't show notifications for this mailbox
# Setting up an mbox account
#[accounts.mbox]
#root_folder = "/var/mail/username"
#root_mailbox = "/var/mail/username"
#format = "mbox"
#index_style = "Compact"
#identity="username@hostname.local"
#
# Setting up an IMAP account
#[accounts."imap"]
#root_folder = "INBOX"
#root_mailbox = "INBOX"
#format = "imap"
#server_hostname="mail.server.tld"
#server_password="pha2hiLohs2eeeish2phaii1We3ood4chakaiv0hien2ahie3m"
@ -42,20 +42,20 @@
#index_style = "Conversations"
#identity = "username@server.tld"
#display_name = "Name Name"
## match every folder:
#subscribed_folders = ["*" ]
## match specific folders:
#subscribed_folders = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
## match every mailbox:
#subscribed_mailboxes = ["*" ]
## match specific mailboxes:
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
# Setting up an account for an already existing notmuch database
#[accounts.notmuch]
#root_folder = "/path/to/folder" # where .notmuch/ directory is located
#root_mailbox = "/path/to/folder" # where .notmuch/ directory is located
#format = "notmuch"
#index_style = "conversations"
#identity="username@server.tld"
#display_name = "Name Name"
# # notmuch folders are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
# [accounts.notmuch.folders]
# # notmuch mailboxes are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
# [accounts.notmuch.mailboxes]
# "INBOX" = { query="tag:inbox", subscribe = true }
# "Drafts" = { query="tag:draft", subscribe = true }
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
@ -88,8 +88,8 @@
#[shortcuts.listing]
#prev_page = "PageUp"
#next_page = "PageDown"
#prev_folder = 'K'
#next_folder = 'J'
#prev_mailbox = 'K'
#next_mailbox = 'J'
#prev_account = 'l'
#next_account = 'h'
#new_mail = 'm'

View File

@ -24,7 +24,7 @@
use melib::parsec::*;
use melib::UnixTimestamp;
use melib::{
backends::{FolderHash, MailBackend},
backends::{MailBackend, MailboxHash},
email::EnvelopeHash,
thread::{SortField, SortOrder},
Result,
@ -429,7 +429,7 @@ pub fn query_to_imap(q: &Query) -> String {
pub fn imap_search(
term: &str,
(_sort_field, _sort_order): (SortField, SortOrder),
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
) -> Result<smallvec::SmallVec<[EnvelopeHash; 512]>> {
let query = query().parse(term)?.1;
@ -437,7 +437,7 @@ pub fn imap_search(
let b = (*backend_lck).as_any();
if let Some(imap_backend) = b.downcast_ref::<melib::backends::ImapType>() {
imap_backend.search(query_to_imap(&query), folder_hash)
imap_backend.search(query_to_imap(&query), mailbox_hash)
} else {
panic!("Could not downcast ImapType backend. BUG");
}

View File

@ -22,8 +22,8 @@
/*! Entities that handle Mail specific functions.
*/
use super::*;
use melib::backends::Folder;
use melib::backends::FolderHash;
use melib::backends::Mailbox;
use melib::backends::MailboxHash;
use melib::thread::ThreadNodeHash;
pub mod listing;

View File

@ -63,7 +63,7 @@ impl std::ops::DerefMut for EmbedStatus {
#[derive(Debug)]
pub struct Composer {
reply_context: Option<(FolderHash, EnvelopeHash)>,
reply_context: Option<(MailboxHash, EnvelopeHash)>,
account_cursor: usize,
cursor: Cursor,
@ -164,7 +164,7 @@ impl Composer {
}
pub fn with_context(
coordinates: (usize, FolderHash),
coordinates: (usize, MailboxHash),
msg: EnvelopeHash,
context: &Context,
) -> Self {
@ -1092,7 +1092,7 @@ pub fn send_draft(
context: &mut Context,
account_cursor: usize,
mut draft: Draft,
folder_type: SpecialUsageMailbox,
mailbox_type: SpecialUsageMailbox,
flags: Flag,
) -> bool {
use std::io::Write;
@ -1229,7 +1229,7 @@ pub fn send_draft(
save_draft(
bytes.as_bytes(),
context,
folder_type,
mailbox_type,
flags,
account_cursor,
);
@ -1239,12 +1239,12 @@ pub fn send_draft(
pub fn save_draft(
bytes: &[u8],
context: &mut Context,
folder_type: SpecialUsageMailbox,
mailbox_type: SpecialUsageMailbox,
flags: Flag,
account_cursor: usize,
) {
if let Err(MeliError { summary, details }) =
context.accounts[account_cursor].save_special(bytes, folder_type, flags)
context.accounts[account_cursor].save_special(bytes, mailbox_type, flags)
{
context.replies.push_back(UIEvent::Notification(
summary.map(|s| s.into()),

View File

@ -125,7 +125,7 @@ struct AccountMenuEntry {
name: String,
// Index in the config account vector.
index: usize,
entries: SmallVec<[(usize, FolderHash); 16]>,
entries: SmallVec<[(usize, MailboxHash); 16]>,
}
pub trait MailListingTrait: ListingTrait {
@ -137,10 +137,10 @@ pub trait MailListingTrait: ListingTrait {
) {
let account = &mut context.accounts[self.coordinates().0];
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
let folder_hash = self.coordinates().1;
for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) {
let mailbox_hash = self.coordinates().1;
for (_, h) in account.collection.threads[&mailbox_hash].thread_group_iter(thread_hash) {
envs_to_set.push(
account.collection.threads[&folder_hash].thread_nodes()[&h]
account.collection.threads[&mailbox_hash].thread_nodes()[&h]
.message()
.unwrap(),
);
@ -210,8 +210,8 @@ pub trait MailListingTrait: ListingTrait {
}
pub trait ListingTrait: Component {
fn coordinates(&self) -> (usize, FolderHash);
fn set_coordinates(&mut self, _: (usize, FolderHash));
fn coordinates(&self) -> (usize, MailboxHash);
fn set_coordinates(&mut self, _: (usize, MailboxHash));
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
@ -446,11 +446,11 @@ impl Component for Listing {
self.change_account(context);
} else {
self.accounts[*account_index].entries = context.accounts[*account_index]
.list_folders()
.list_mailboxes()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index][&folder_node.hash]
.ref_folder
.filter(|mailbox_node| {
context.accounts[*account_index][&mailbox_node.hash]
.ref_mailbox
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
@ -459,14 +459,14 @@ impl Component for Listing {
}
return true;
}
UIEvent::MailboxDelete((account_index, _folder_hash))
| UIEvent::MailboxCreate((account_index, _folder_hash)) => {
UIEvent::MailboxDelete((account_index, _mailbox_hash))
| UIEvent::MailboxCreate((account_index, _mailbox_hash)) => {
self.accounts[*account_index].entries = context.accounts[*account_index]
.list_folders()
.list_mailboxes()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index][&folder_node.hash]
.ref_folder
.filter(|mailbox_node| {
context.accounts[*account_index][&mailbox_node.hash]
.ref_mailbox
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
@ -495,8 +495,8 @@ impl Component for Listing {
let shortcuts = self.get_shortcuts(context);
match *event {
UIEvent::Input(ref k)
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"])
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) =>
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"])
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) =>
{
let amount = if self.cmd_buf.is_empty() {
1
@ -514,28 +514,28 @@ impl Component for Listing {
return true;
};
match k {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) => {
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => {
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1 + amount)
{
self.cursor_pos.1 += amount;
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
self.set_dirty(true);
} else {
return true;
}
}
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) => {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => {
if self.cursor_pos.1 >= amount {
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1 - amount)
{
self.cursor_pos.1 -= amount;
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
self.set_dirty(true);
} else {
return true;
@ -546,16 +546,16 @@ impl Component for Listing {
}
_ => {}
}
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1)
{
/* Account might have no folders yet if it's offline */
/* Check if per-folder configuration overrides general configuration */
/* Account might have no mailboxes yet if it's offline */
/* Check if per-mailbox configuration overrides general configuration */
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
.and_then(|account| account[mailbox_hash].conf.conf_override.index_style)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
@ -642,12 +642,12 @@ impl Component for Listing {
return true;
}
Action::ViewMailbox(idx) => {
if let Some((_, folder_hash)) =
if let Some((_, mailbox_hash)) =
self.accounts[self.cursor_pos.0].entries.get(*idx)
{
self.cursor_pos.1 = *idx;
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
self.set_dirty(true);
} else {
return true;
@ -791,8 +791,8 @@ impl Component for Listing {
if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) =>
{
let account = &mut context.accounts[self.cursor_pos.0];
if let Some(&folder_hash) = account.folders_order.get(self.cursor_pos.1) {
if let Err(err) = account.refresh(folder_hash) {
if let Some(&mailbox_hash) = account.mailboxes_order.get(self.cursor_pos.1) {
if let Err(err) = account.refresh(mailbox_hash) {
context.replies.push_back(UIEvent::Notification(
Some("Could not refresh.".to_string()),
err.to_string(),
@ -862,36 +862,36 @@ impl Component for Listing {
}
fn get_status(&self, context: &Context) -> String {
let folder_hash = if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
let mailbox_hash = if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1)
{
*folder_hash
*mailbox_hash
} else {
return String::new();
};
let account = &context.accounts[self.cursor_pos.0];
use crate::conf::accounts::MailboxStatus;
match account[&folder_hash].status {
match account[&mailbox_hash].status {
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
"Mailbox: {}, Messages: {}, New: {}",
account[&folder_hash].ref_folder.name(),
account.collection[&folder_hash].len(),
account[&folder_hash]
.ref_folder
account[&mailbox_hash].ref_mailbox.name(),
account.collection[&mailbox_hash].len(),
account[&mailbox_hash]
.ref_mailbox
.count()
.ok()
.map(|(v, _)| v)
.unwrap_or(0),
),
MailboxStatus::Failed(_) | MailboxStatus::None => account[&folder_hash].status(),
MailboxStatus::Failed(_) | MailboxStatus::None => account[&mailbox_hash].status(),
}
}
}
impl From<(IndexStyle, (usize, FolderHash))> for ListingComponent {
fn from((index_style, coordinates): (IndexStyle, (usize, FolderHash))) -> Self {
impl From<(IndexStyle, (usize, MailboxHash))> for ListingComponent {
fn from((index_style, coordinates): (IndexStyle, (usize, MailboxHash))) -> Self {
match index_style {
IndexStyle::Plain => Plain(PlainListing::new(coordinates)),
IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)),
@ -909,10 +909,10 @@ impl Listing {
.iter()
.enumerate()
.map(|(i, a)| {
let entries: SmallVec<[(usize, FolderHash); 16]> = a
.list_folders()
let entries: SmallVec<[(usize, MailboxHash); 16]> = a
.list_mailboxes()
.into_iter()
.filter(|folder_node| a[&folder_node.hash].ref_folder.is_subscribed())
.filter(|mailbox_node| a[&mailbox_node.hash].ref_mailbox.is_subscribed())
.map(|f| (f.depth, f.hash))
.collect::<_>();
@ -981,10 +981,10 @@ impl Listing {
debug!("BUG: invalid area in print_account");
}
// Each entry and its index in the account
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
.folder_entries
let mailboxes: FnvHashMap<MailboxHash, Mailbox> = context.accounts[a.index]
.mailbox_entries
.iter()
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
.map(|(&hash, entry)| (hash, entry.ref_mailbox.clone()))
.collect();
let upper_left = upper_left!(area);
@ -992,21 +992,21 @@ impl Listing {
let must_highlight_account: bool = self.cursor_pos.0 == a.index;
let mut lines: Vec<(usize, usize, FolderHash, Option<usize>)> = Vec::new();
let mut lines: Vec<(usize, usize, MailboxHash, Option<usize>)> = Vec::new();
for (i, &(depth, folder_hash)) in a.entries.iter().enumerate() {
if folders[&folder_hash].is_subscribed() {
match context.accounts[a.index].status(folder_hash) {
for (i, &(depth, mailbox_hash)) in a.entries.iter().enumerate() {
if mailboxes[&mailbox_hash].is_subscribed() {
match context.accounts[a.index].status(mailbox_hash) {
Ok(_) => {
lines.push((
depth,
i,
folder_hash,
folders[&folder_hash].count().ok().map(|(v, _)| v),
mailbox_hash,
mailboxes[&mailbox_hash].count().ok().map(|(v, _)| v),
));
}
Err(_) => {
lines.push((depth, i, folder_hash, None));
lines.push((depth, i, mailbox_hash, None));
}
}
}
@ -1075,8 +1075,8 @@ impl Listing {
)
};
let (depth, inc, folder_idx, count) = lines[idx];
/* Calculate how many columns the folder index tags should occupy with right alignment,
let (depth, inc, mailbox_idx, count) = lines[idx];
/* Calculate how many columns the mailbox index tags should occupy with right alignment,
* eg.
* 1
* 2
@ -1084,7 +1084,7 @@ impl Listing {
* 9
* 10
*/
let total_folder_no_digits = {
let total_mailbox_no_digits = {
let mut len = lines_len;
let mut ctr = 1;
while len > 9 {
@ -1094,7 +1094,7 @@ impl Listing {
ctr
};
let (x, _) = write_string_to_grid(
&format!("{:>width$}", inc, width = total_folder_no_digits),
&format!("{:>width$}", inc, width = total_mailbox_no_digits),
grid,
index_att.fg,
index_att.bg,
@ -1112,7 +1112,7 @@ impl Listing {
None,
);
let (x, _) = write_string_to_grid(
folders[&folder_idx].name(),
mailboxes[&mailbox_idx].name(),
grid,
att.fg,
att.bg,
@ -1145,7 +1145,7 @@ impl Listing {
},
(
(
/* Hide part of folder name if need be to fit the message count */
/* Hide part of mailbox name if need be to fit the message count */
std::cmp::min(x, get_x(bottom_right).saturating_sub(count_string.len())),
y,
),
@ -1167,27 +1167,27 @@ impl Listing {
fn change_account(&mut self, context: &mut Context) {
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
.list_folders()
.list_mailboxes()
.into_iter()
.filter(|folder_node| {
context.accounts[self.cursor_pos.0][&folder_node.hash]
.ref_folder
.filter(|mailbox_node| {
context.accounts[self.cursor_pos.0][&mailbox_node.hash]
.ref_mailbox
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
.collect::<_>();
/* Account might have no folders yet if it's offline */
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
/* Account might have no mailboxes yet if it's offline */
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1)
{
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
/* Check if per-folder configuration overrides general configuration */
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
/* Check if per-mailbox configuration overrides general configuration */
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
.and_then(|account| account[mailbox_hash].conf.conf_override.index_style)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context

View File

@ -48,9 +48,9 @@ macro_rules! address_list {
/// `ThreadView`.
#[derive(Debug)]
pub struct CompactListing {
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
cursor_pos: (usize, MailboxHash, usize),
new_cursor_pos: (usize, MailboxHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -100,7 +100,7 @@ impl MailListingTrait for CompactListing {
SmallVec::from_iter(iter.into_iter())
}
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
self.dirty = true;
@ -184,11 +184,11 @@ impl MailListingTrait for CompactListing {
}
impl ListingTrait for CompactListing {
fn coordinates(&self) -> (usize, FolderHash) {
fn coordinates(&self) -> (usize, MailboxHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = ThreadView::default();
@ -618,7 +618,7 @@ impl fmt::Display for CompactListing {
impl CompactListing {
const DESCRIPTION: &'static str = "compact listing";
pub fn new(coordinates: (usize, FolderHash)) -> Self {
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
CompactListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0),
@ -650,14 +650,14 @@ impl CompactListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
if let Some(t) = backend_lck.tags() {
let tags_lck = t.read().unwrap();
for t in e.labels().iter() {
if folder
if mailbox
.conf_override
.tags
.as_ref()
@ -669,7 +669,7 @@ impl CompactListing {
tags.push(' ');
tags.push_str(tags_lck.get(t).as_ref().unwrap());
tags.push(' ');
if let Some(&c) = folder
if let Some(&c) = mailbox
.conf_override
.tags
.as_ref()

View File

@ -27,9 +27,9 @@ use std::iter::FromIterator;
/// `ThreadView`.
#[derive(Debug)]
pub struct ConversationsListing {
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
cursor_pos: (usize, MailboxHash, usize),
new_cursor_pos: (usize, MailboxHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -79,11 +79,11 @@ impl MailListingTrait for ConversationsListing {
SmallVec::from_iter(iter.into_iter())
}
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
self.dirty = true;
let old_folder_hash = self.cursor_pos.1;
let old_mailbox_hash = self.cursor_pos.1;
let old_cursor_pos = self.cursor_pos;
if !(self.cursor_pos.0 == self.new_cursor_pos.0
&& self.cursor_pos.1 == self.new_cursor_pos.1)
@ -159,7 +159,8 @@ impl MailListingTrait for ConversationsListing {
Box::new(roots.into_iter()) as Box<dyn Iterator<Item = ThreadHash>>,
);
if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.cursor_pos.1 {
if !force && old_cursor_pos == self.new_cursor_pos && old_mailbox_hash == self.cursor_pos.1
{
self.view.update(context);
} else if self.unfocused {
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
@ -170,11 +171,11 @@ impl MailListingTrait for ConversationsListing {
}
impl ListingTrait for ConversationsListing {
fn coordinates(&self) -> (usize, FolderHash) {
fn coordinates(&self) -> (usize, MailboxHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
@ -559,7 +560,7 @@ impl fmt::Display for ConversationsListing {
impl ConversationsListing {
const DESCRIPTION: &'static str = "compact listing";
pub fn new(coordinates: (usize, FolderHash)) -> Self {
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
ConversationsListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0),
@ -592,14 +593,14 @@ impl ConversationsListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
if let Some(t) = backend_lck.tags() {
let tags_lck = t.read().unwrap();
for t in e.labels().iter() {
if folder
if mailbox
.conf_override
.tags
.as_ref()
@ -611,7 +612,7 @@ impl ConversationsListing {
tags.push(' ');
tags.push_str(tags_lck.get(t).as_ref().unwrap());
tags.push(' ');
if let Some(&c) = folder
if let Some(&c) = mailbox
.conf_override
.tags
.as_ref()

View File

@ -24,7 +24,7 @@ use crate::components::utilities::PageMovement;
#[derive(Debug)]
pub struct OfflineListing {
cursor_pos: (usize, FolderHash),
cursor_pos: (usize, MailboxHash),
_row_updates: SmallVec<[ThreadHash; 8]>,
id: ComponentId,
@ -44,11 +44,11 @@ impl MailListingTrait for OfflineListing {
}
impl ListingTrait for OfflineListing {
fn coordinates(&self) -> (usize, FolderHash) {
fn coordinates(&self) -> (usize, MailboxHash) {
self.cursor_pos
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
self.cursor_pos = coordinates;
}
@ -75,7 +75,7 @@ impl fmt::Display for OfflineListing {
}
impl OfflineListing {
pub fn new(cursor_pos: (usize, FolderHash)) -> Self {
pub fn new(cursor_pos: (usize, MailboxHash)) -> Self {
OfflineListing {
cursor_pos,
_row_updates: SmallVec::new(),

View File

@ -47,9 +47,9 @@ macro_rules! address_list {
/// `MailView`.
#[derive(Debug)]
pub struct PlainListing {
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
cursor_pos: (usize, MailboxHash, usize),
new_cursor_pos: (usize, MailboxHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -101,7 +101,7 @@ impl MailListingTrait for PlainListing {
*/
}
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
self.dirty = true;
@ -188,11 +188,11 @@ impl MailListingTrait for PlainListing {
}
impl ListingTrait for PlainListing {
fn coordinates(&self) -> (usize, FolderHash) {
fn coordinates(&self) -> (usize, MailboxHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = MailView::default();
@ -584,7 +584,7 @@ impl fmt::Display for PlainListing {
impl PlainListing {
const DESCRIPTION: &'static str = "plain listing";
pub fn new(coordinates: (usize, FolderHash)) -> Self {
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
PlainListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0),
@ -613,14 +613,14 @@ impl PlainListing {
}
}
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
if let Some(t) = backend_lck.tags() {
let tags_lck = t.read().unwrap();
for t in e.labels().iter() {
if folder
if mailbox
.conf_override
.tags
.as_ref()
@ -632,7 +632,7 @@ impl PlainListing {
tags.push(' ');
tags.push_str(tags_lck.get(t).as_ref().unwrap());
tags.push(' ');
if let Some(&c) = folder
if let Some(&c) = mailbox
.conf_override
.tags
.as_ref()

View File

@ -28,9 +28,9 @@ const MAX_COLS: usize = 500;
/// `MailView`.
#[derive(Debug)]
pub struct ThreadListing {
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
cursor_pos: (usize, MailboxHash, usize),
new_cursor_pos: (usize, MailboxHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -59,7 +59,7 @@ impl MailListingTrait for ThreadListing {
SmallVec::new()
}
/// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
/// Fill the `self.content` `CellBuffer` with the contents of the account mailbox the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, _force: bool) {
self.dirty = true;
@ -127,7 +127,7 @@ impl MailListingTrait for ThreadListing {
ret
};
if threads.len() == 0 {
let message = format!("Folder `{}` is empty.", account[&self.cursor_pos.1].name());
let message = format!("Mailbox `{}` is empty.", account[&self.cursor_pos.1].name());
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
&message,
@ -226,10 +226,10 @@ impl MailListingTrait for ThreadListing {
}
impl ListingTrait for ThreadListing {
fn coordinates(&self) -> (usize, FolderHash) {
fn coordinates(&self) -> (usize, MailboxHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = None;
@ -396,7 +396,7 @@ impl fmt::Display for ThreadListing {
}
impl ThreadListing {
pub fn new(coordinates: (usize, FolderHash)) -> Self {
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
ThreadListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0),

View File

@ -308,9 +308,9 @@ impl StatusPanel {
}
}
let count = a
.folder_entries
.mailbox_entries
.values()
.map(|entry| &entry.ref_folder)
.map(|entry| &entry.ref_mailbox)
.fold((0, 0), |acc, f| {
let count = f.count().unwrap_or((0, 0));
(acc.0 + count.0, acc.1 + count.1)
@ -361,9 +361,9 @@ impl StatusPanel {
None,
);
for (i, f) in a
.folder_entries
.mailbox_entries
.values()
.map(|entry| &entry.ref_folder)
.map(|entry| &entry.ref_mailbox)
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
.enumerate()
{
@ -479,9 +479,9 @@ impl Component for AccountStatus {
None,
);
for f in a
.folder_entries
.mailbox_entries
.values()
.map(|entry| &entry.ref_folder)
.map(|entry| &entry.ref_mailbox)
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
{
line += 1;
@ -497,7 +497,7 @@ impl Component for AccountStatus {
}
line += 2;
write_string_to_grid(
"Subscribed folders:",
"Subscribed mailboxes:",
&mut self.content,
self.theme_default.fg,
self.theme_default.bg,
@ -506,8 +506,8 @@ impl Component for AccountStatus {
None,
);
line += 2;
for folder_node in a.list_folders() {
let f: &Folder = &a[&folder_node.hash].ref_folder;
for mailbox_node in a.list_mailboxes() {
let f: &Mailbox = &a[&mailbox_node.hash].ref_mailbox;
if f.is_subscribed() {
write_string_to_grid(
f.path(),

View File

@ -86,7 +86,7 @@ impl ViewMode {
/// menus
#[derive(Debug, Default)]
pub struct MailView {
coordinates: (usize, FolderHash, EnvelopeHash),
coordinates: (usize, MailboxHash, EnvelopeHash),
pager: Pager,
subview: Option<Box<dyn Component>>,
dirty: bool,
@ -124,7 +124,7 @@ impl fmt::Display for MailView {
impl MailView {
const DESCRIPTION: &'static str = "view mail";
pub fn new(
coordinates: (usize, FolderHash, EnvelopeHash),
coordinates: (usize, MailboxHash, EnvelopeHash),
pager: Option<Pager>,
subview: Option<Box<dyn Component>>,
context: &Context,
@ -342,7 +342,7 @@ impl MailView {
}
}
pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) {
pub fn update(&mut self, new_coordinates: (usize, MailboxHash, EnvelopeHash)) {
self.coordinates = new_coordinates;
self.mode = ViewMode::Normal;
self.initialised = false;

View File

@ -51,7 +51,7 @@ pub struct ThreadView {
expanded_pos: usize,
new_expanded_pos: usize,
reversed: bool,
coordinates: (usize, FolderHash, usize),
coordinates: (usize, MailboxHash, usize),
thread_group: ThreadHash,
mailview: MailView,
show_mailview: bool,
@ -69,13 +69,13 @@ pub struct ThreadView {
impl ThreadView {
const DESCRIPTION: &'static str = "thread view";
/*
* coordinates: (account index, folder_hash, root set thread_node index)
* coordinates: (account index, mailbox_hash, root set thread_node index)
* expanded_hash: optional position of expanded entry when we render the threadview. Default
* expanded message is the last one.
* context: current context
*/
pub fn new(
coordinates: (usize, FolderHash, usize),
coordinates: (usize, MailboxHash, usize),
thread_group: ThreadHash,
expanded_hash: Option<ThreadNodeHash>,
context: &Context,

View File

@ -51,7 +51,7 @@ use self::notifications::NotificationsSettings;
use self::terminal::TerminalSettings;
use crate::pager::PagerSettings;
use crate::plugins::Plugin;
use melib::conf::{AccountSettings, FolderConf, ToggleFlag};
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
use melib::error::*;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@ -85,27 +85,27 @@ pub struct MailUIConf {
#[serde(default)]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FileFolderConf {
pub struct FileMailboxConf {
#[serde(flatten)]
pub conf_override: MailUIConf,
#[serde(flatten)]
pub folder_conf: FolderConf,
pub mailbox_conf: MailboxConf,
}
impl FileFolderConf {
impl FileMailboxConf {
pub fn conf_override(&self) -> &MailUIConf {
&self.conf_override
}
pub fn folder_conf(&self) -> &FolderConf {
&self.folder_conf
pub fn mailbox_conf(&self) -> &MailboxConf {
&self.mailbox_conf
}
}
use crate::conf::deserializers::extra_settings;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FileAccount {
root_folder: String,
root_mailbox: String,
format: String,
identity: String,
#[serde(default = "none")]
@ -115,9 +115,9 @@ pub struct FileAccount {
#[serde(default = "false_val")]
read_only: bool,
#[serde(default)]
subscribed_folders: Vec<String>,
subscribed_mailboxes: Vec<String>,
#[serde(default)]
folders: HashMap<String, FileFolderConf>,
mailboxes: HashMap<String, FileMailboxConf>,
#[serde(default)]
cache_type: CacheType,
#[serde(default)]
@ -132,44 +132,44 @@ pub struct FileAccount {
impl From<FileAccount> for AccountConf {
fn from(x: FileAccount) -> Self {
let format = x.format.to_lowercase();
let root_folder = x.root_folder.clone();
let root_mailbox = x.root_mailbox.clone();
let identity = x.identity.clone();
let display_name = x.display_name.clone();
let folders = x
.folders
let mailboxes = x
.mailboxes
.iter()
.map(|(k, v)| (k.clone(), v.folder_conf.clone()))
.map(|(k, v)| (k.clone(), v.mailbox_conf.clone()))
.collect();
let acc = AccountSettings {
name: String::new(),
root_folder,
root_mailbox,
format,
identity,
read_only: x.read_only,
display_name,
subscribed_folders: x.subscribed_folders.clone(),
folders,
subscribed_mailboxes: x.subscribed_mailboxes.clone(),
mailboxes,
manual_refresh: x.manual_refresh,
extra: x.extra.clone(),
};
let folder_confs = x.folders.clone();
let mailbox_confs = x.mailboxes.clone();
AccountConf {
account: acc,
conf: x,
folder_confs,
mailbox_confs,
}
}
}
impl FileAccount {
pub fn folders(&self) -> &HashMap<String, FileFolderConf> {
&self.folders
pub fn mailboxes(&self) -> &HashMap<String, FileMailboxConf> {
&self.mailboxes
}
pub fn folder(&self) -> &str {
&self.root_folder
pub fn mailbox(&self) -> &str {
&self.root_mailbox
}
pub fn index_style(&self) -> IndexStyle {
@ -207,7 +207,7 @@ pub struct FileSettings {
pub struct AccountConf {
pub(crate) account: AccountSettings,
pub(crate) conf: FileAccount,
pub(crate) folder_confs: HashMap<String, FileFolderConf>,
pub(crate) mailbox_confs: HashMap<String, FileMailboxConf>,
}
impl AccountConf {
@ -342,13 +342,13 @@ impl FileSettings {
s.terminal.themes.validate()?;
for (name, acc) in &s.accounts {
let FileAccount {
root_folder,
root_mailbox,
format,
identity,
read_only,
display_name,
subscribed_folders,
folders,
subscribed_mailboxes,
mailboxes,
extra,
manual_refresh,
refresh_command: _,
@ -359,16 +359,16 @@ impl FileSettings {
let lowercase_format = format.to_lowercase();
let s = AccountSettings {
name: name.to_string(),
root_folder,
root_mailbox,
format: format.clone(),
identity,
read_only,
display_name,
subscribed_folders,
subscribed_mailboxes,
manual_refresh,
folders: folders
mailboxes: mailboxes
.into_iter()
.map(|(k, v)| (k, v.folder_conf))
.map(|(k, v)| (k, v.mailbox_conf))
.collect(),
extra,
};
@ -714,8 +714,8 @@ mod pp {
let mut ret = pp_helper(&p_buf, 0)?;
drop(p_buf);
if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") {
for theme_folder in xdg_dirs.find_config_files("themes") {
let read_dir = std::fs::read_dir(theme_folder)?;
for theme_mailbox in xdg_dirs.find_config_files("themes") {
let read_dir = std::fs::read_dir(theme_mailbox)?;
for theme in read_dir {
ret.extend(pp_helper(&theme?.path(), 0)?.chars());
}

View File

@ -23,11 +23,11 @@
* Account management from user configuration.
*/
use super::{AccountConf, FileFolderConf};
use super::{AccountConf, FileMailboxConf};
use fnv::FnvHashMap;
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use melib::backends::{
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp, RefreshEvent,
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
};
use melib::email::*;
@ -84,15 +84,15 @@ impl MailboxStatus {
}
#[derive(Debug)]
pub struct FolderEntry {
pub struct MailboxEntry {
pub status: MailboxStatus,
pub name: String,
pub ref_folder: Folder,
pub conf: FileFolderConf,
pub ref_mailbox: Mailbox,
pub conf: FileMailboxConf,
pub worker: Worker,
}
impl FolderEntry {
impl MailboxEntry {
pub fn status(&self) -> String {
match self.status {
MailboxStatus::Available => self.name().to_string(),
@ -114,10 +114,10 @@ pub struct Account {
pub index: usize,
name: String,
pub is_online: bool,
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
pub(crate) folders_order: Vec<FolderHash>,
tree: Vec<FolderNode>,
sent_folder: Option<FolderHash>,
pub(crate) mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry>,
pub(crate) mailboxes_order: Vec<MailboxHash>,
tree: Vec<MailboxNode>,
sent_mailbox: Option<MailboxHash>,
pub(crate) collection: Collection,
pub(crate) address_book: AddressBook,
pub(crate) work_context: WorkContext,
@ -126,7 +126,7 @@ pub struct Account {
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
sender: Sender<ThreadEvent>,
event_queue: VecDeque<(FolderHash, RefreshEvent)>,
event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
notify_fn: Arc<NotifyFn>,
}
@ -177,10 +177,10 @@ impl Drop for Account {
}
#[derive(Serialize, Debug, Clone, Default)]
pub struct FolderNode {
pub hash: FolderHash,
pub struct MailboxNode {
pub hash: MailboxHash,
pub depth: usize,
pub children: Vec<FolderNode>,
pub children: Vec<MailboxNode>,
}
impl Account {
@ -197,11 +197,11 @@ impl Account {
let backend = map.get(settings.account().format())(
settings.account(),
Box::new(move |path: &str| {
s.account.subscribed_folders.is_empty()
|| (s.folder_confs.contains_key(path)
&& s.folder_confs[path].folder_conf().subscribe.is_true())
s.account.subscribed_mailboxes.is_empty()
|| (s.mailbox_confs.contains_key(path)
&& s.mailbox_confs[path].mailbox_conf().subscribe.is_true())
|| s.account
.subscribed_folders
.subscribed_mailboxes
.iter()
.any(|m| path.matches_glob(m))
}),
@ -233,11 +233,11 @@ impl Account {
index,
name,
is_online: false,
folder_entries: Default::default(),
folders_order: Default::default(),
mailbox_entries: Default::default(),
mailboxes_order: Default::default(),
tree: Default::default(),
address_book,
sent_folder: Default::default(),
sent_mailbox: Default::default(),
collection: Default::default(),
work_context,
runtime_settings: settings.clone(),
@ -251,22 +251,22 @@ impl Account {
}
fn init(&mut self) {
let mut ref_folders: FnvHashMap<FolderHash, Folder> =
match self.backend.read().unwrap().folders() {
let mut ref_mailboxes: FnvHashMap<MailboxHash, Mailbox> =
match self.backend.read().unwrap().mailboxes() {
Ok(f) => f,
Err(err) => {
debug!(&err);
return;
}
};
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
let mut mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry> =
FnvHashMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
let mut mailboxes_order: Vec<MailboxHash> = Vec::with_capacity(ref_mailboxes.len());
let mut sent_folder = None;
for f in ref_folders.values_mut() {
if let Some(conf) = self.settings.folder_confs.get_mut(f.path()) {
conf.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
let mut sent_mailbox = None;
for f in ref_mailboxes.values_mut() {
if let Some(conf) = self.settings.mailbox_confs.get_mut(f.path()) {
conf.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
Some(f.special_usage())
} else {
let tmp = SpecialUsageMailbox::detect_usage(f.name());
@ -275,21 +275,21 @@ impl Account {
}
tmp
};
match conf.folder_conf.usage {
match conf.mailbox_conf.usage {
Some(SpecialUsageMailbox::Sent) => {
sent_folder = Some(f.hash());
sent_mailbox = Some(f.hash());
}
None => {
if f.special_usage() == SpecialUsageMailbox::Sent {
sent_folder = Some(f.hash());
sent_mailbox = Some(f.hash());
}
}
_ => {}
}
folder_entries.insert(
mailbox_entries.insert(
f.hash(),
FolderEntry {
ref_folder: f.clone(),
MailboxEntry {
ref_mailbox: f.clone(),
name: f.path().to_string(),
status: MailboxStatus::None,
conf: conf.clone(),
@ -297,8 +297,8 @@ impl Account {
},
);
} else {
let mut new = FileFolderConf::default();
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
let mut new = FileMailboxConf::default();
new.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
Some(f.special_usage())
} else {
let tmp = SpecialUsageMailbox::detect_usage(f.name());
@ -307,14 +307,14 @@ impl Account {
}
tmp
};
if new.folder_conf.usage == Some(SpecialUsageMailbox::Sent) {
sent_folder = Some(f.hash());
if new.mailbox_conf.usage == Some(SpecialUsageMailbox::Sent) {
sent_mailbox = Some(f.hash());
}
folder_entries.insert(
mailbox_entries.insert(
f.hash(),
FolderEntry {
ref_folder: f.clone(),
MailboxEntry {
ref_mailbox: f.clone(),
name: f.path().to_string(),
status: MailboxStatus::None,
conf: new,
@ -324,14 +324,14 @@ impl Account {
}
}
let mut tree: Vec<FolderNode> = Vec::new();
let mut tree: Vec<MailboxNode> = Vec::new();
let mut collection: Collection = Collection::new(Default::default());
for (h, f) in ref_folders.iter() {
for (h, f) in ref_mailboxes.iter() {
if !f.is_subscribed() {
/* Skip unsubscribed folder */
/* Skip unsubscribed mailbox */
continue;
}
folder_entries.entry(*h).and_modify(|entry| {
mailbox_entries.entry(*h).and_modify(|entry| {
entry.status = MailboxStatus::Parsing(0, 0);
entry.worker = Account::new_worker(
f.clone(),
@ -344,32 +344,32 @@ impl Account {
collection.threads.insert(*h, Threads::default());
}
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
self.folders_order = folders_order;
self.folder_entries = folder_entries;
build_mailboxes_order(&mut tree, &mailbox_entries, &mut mailboxes_order);
self.mailboxes_order = mailboxes_order;
self.mailbox_entries = mailbox_entries;
self.tree = tree;
self.sent_folder = sent_folder;
self.sent_mailbox = sent_mailbox;
self.collection = collection;
}
fn new_worker(
folder: Folder,
mailbox: Mailbox,
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
work_context: &WorkContext,
notify_fn: Arc<NotifyFn>,
) -> Worker {
let mut mailbox_handle = backend.write().unwrap().get(&folder);
let mut mailbox_handle = backend.write().unwrap().get(&mailbox);
let mut builder = AsyncBuilder::new();
let our_tx = builder.tx();
let folder_hash = folder.hash();
let priority = match folder.special_usage() {
let mailbox_hash = mailbox.hash();
let priority = match mailbox.special_usage() {
SpecialUsageMailbox::Inbox => 0,
SpecialUsageMailbox::Sent => 1,
SpecialUsageMailbox::Drafts | SpecialUsageMailbox::Trash => 2,
_ => {
3 * folder
3 * mailbox
.path()
.split(if folder.path().contains('/') {
.split(if mailbox.path().contains('/') {
'/'
} else {
'.'
@ -385,7 +385,7 @@ impl Account {
*/
builder.set_priority(priority).set_is_static(true);
let mut w = builder.build(Box::new(move |work_context| {
let name = format!("Parsing {}", folder.path());
let name = format!("Parsing {}", mailbox.path());
let work = mailbox_handle.work().unwrap();
work_context.new_work.send(work).unwrap();
let thread_id = std::thread::current().id();
@ -399,12 +399,12 @@ impl Account {
match debug!(mailbox_handle.poll_block()) {
Ok(s @ AsyncStatus::Payload(_)) => {
our_tx.send(s).unwrap();
debug!("notifying for {}", folder_hash);
notify_fn.notify(folder_hash);
debug!("notifying for {}", mailbox_hash);
notify_fn.notify(mailbox_hash);
}
Ok(s @ AsyncStatus::Finished) => {
our_tx.send(s).unwrap();
notify_fn.notify(folder_hash);
notify_fn.notify(mailbox_hash);
debug!("exiting");
work_context.finished.send(thread_id).unwrap();
return;
@ -424,15 +424,15 @@ impl Account {
}
Some(w)
}
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
if !self.folder_entries[&folder_hash].status.is_available() {
self.event_queue.push_back((folder_hash, event));
pub fn reload(&mut self, event: RefreshEvent, mailbox_hash: MailboxHash) -> Option<UIEvent> {
if !self.mailbox_entries[&mailbox_hash].status.is_available() {
self.event_queue.push_back((mailbox_hash, event));
return None;
}
let kind = event.kind();
{
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
//let mailbox: &mut Mailbox = self.mailboxes[idx].as_mut().unwrap().as_mut().unwrap();
match kind {
RefreshEventKind::Update(old_hash, envelope) => {
#[cfg(feature = "sqlite3")]
@ -450,12 +450,12 @@ impl Account {
);
}
}
self.collection.update(old_hash, *envelope, folder_hash);
self.collection.update(old_hash, *envelope, mailbox_hash);
return Some(EnvelopeUpdate(old_hash));
}
RefreshEventKind::Rename(old_hash, new_hash) => {
debug!("rename {} to {}", old_hash, new_hash);
self.collection.rename(old_hash, new_hash, folder_hash);
self.collection.rename(old_hash, new_hash, mailbox_hash);
#[cfg(feature = "sqlite3")]
{
let envelopes = self.collection.envelopes.read();
@ -478,7 +478,7 @@ impl Account {
RefreshEventKind::Create(envelope) => {
let env_hash = envelope.hash();
if self.collection.contains_key(&env_hash)
&& self.collection[&folder_hash].contains(&env_hash)
&& self.collection[&mailbox_hash].contains(&env_hash)
{
return None;
}
@ -505,38 +505,30 @@ impl Account {
);
}
}
self.collection.insert(*envelope, folder_hash);
if self
.sent_folder
.as_ref()
.map(|h| *h == folder_hash)
.unwrap_or(false)
{
self.collection.insert_reply(env_hash);
}
self.collection.insert(*envelope, mailbox_hash);
if self.folder_entries[&folder_hash]
if self.mailbox_entries[&mailbox_hash]
.conf
.folder_conf
.mailbox_conf
.ignore
.is_true()
{
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
}
let thread = {
let thread_hash = self.collection.get_env(env_hash).thread();
self.collection.threads[&folder_hash]
.find_group(self.collection.threads[&folder_hash][&thread_hash].group)
self.collection.threads[&mailbox_hash]
.find_group(self.collection.threads[&mailbox_hash][&thread_hash].group)
};
if self.collection.threads[&folder_hash]
if self.collection.threads[&mailbox_hash]
.thread_ref(thread)
.snoozed()
{
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
}
if is_seen || is_draft {
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
}
return Some(Notification(
@ -545,7 +537,7 @@ impl Account {
"{}\n{} {}",
subject,
self.name,
self.folder_entries[&folder_hash].name()
self.mailbox_entries[&mailbox_hash].name()
),
Some(crate::types::NotificationType::NewMail),
));
@ -567,19 +559,21 @@ impl Account {
);
}
}
self.collection.remove(envelope_hash, folder_hash);
self.collection.remove(envelope_hash, mailbox_hash);
return Some(EnvelopeRemove(envelope_hash));
}
RefreshEventKind::Rescan => {
let handle = Account::new_worker(
self.folder_entries[&folder_hash].ref_folder.clone(),
self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
);
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.worker = handle;
});
self.mailbox_entries
.entry(mailbox_hash)
.and_modify(|entry| {
entry.worker = handle;
});
}
RefreshEventKind::Failure(e) => {
debug!("RefreshEvent Failure: {}", e.to_string());
@ -599,7 +593,7 @@ impl Account {
}
None
}
pub fn refresh(&mut self, folder_hash: FolderHash) -> Result<()> {
pub fn refresh(&mut self, mailbox_hash: MailboxHash) -> Result<()> {
if let Some(ref refresh_command) = self.settings.conf().refresh_command {
let parts = crate::split_command!(refresh_command);
let (cmd, args) = (parts[0], &parts[1..]);
@ -616,7 +610,7 @@ impl Account {
let r = RefreshEventConsumer::new(Box::new(move |r| {
sender_.send(ThreadEvent::from(r)).unwrap();
}));
let mut h = self.backend.write().unwrap().refresh(folder_hash, r)?;
let mut h = self.backend.write().unwrap().refresh(mailbox_hash, r)?;
self.work_context.new_work.send(h.work().unwrap()).unwrap();
Ok(())
}
@ -661,9 +655,9 @@ impl Account {
self.tree.is_empty()
}
pub fn list_folders(&self) -> Vec<FolderNode> {
let mut ret = Vec::with_capacity(self.folder_entries.len());
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
pub fn list_mailboxes(&self) -> Vec<MailboxNode> {
let mut ret = Vec::with_capacity(self.mailbox_entries.len());
fn rec(node: &MailboxNode, ret: &mut Vec<MailboxNode>) {
ret.push(node.clone());
for c in node.children.iter() {
rec(c, ret);
@ -675,20 +669,22 @@ impl Account {
ret
}
pub fn folders_order(&self) -> &Vec<FolderHash> {
&self.folders_order
pub fn mailboxes_order(&self) -> &Vec<MailboxHash> {
&self.mailboxes_order
}
pub fn name(&self) -> &str {
&self.name
}
fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
fn load_mailbox(&mut self, mailbox_hash: MailboxHash, payload: Result<Vec<Envelope>>) {
if payload.is_err() {
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.status = MailboxStatus::Failed(payload.unwrap_err());
});
self.mailbox_entries
.entry(mailbox_hash)
.and_modify(|entry| {
entry.status = MailboxStatus::Failed(payload.unwrap_err());
});
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
.unwrap();
return;
}
@ -697,37 +693,37 @@ impl Account {
.into_iter()
.map(|e| (e.hash(), e))
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
if let Some(updated_folders) =
if let Some(updated_mailboxes) =
self.collection
.merge(envelopes, folder_hash, self.sent_folder)
.merge(envelopes, mailbox_hash, self.sent_mailbox)
{
for f in updated_folders {
for f in updated_mailboxes {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
.unwrap();
}
}
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
.unwrap();
}
pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
if folder_hash == 0 {
pub fn status(&mut self, mailbox_hash: MailboxHash) -> result::Result<(), usize> {
if mailbox_hash == 0 {
return Err(0);
}
loop {
match self
.folder_entries
.get_mut(&folder_hash)
.mailbox_entries
.get_mut(&mailbox_hash)
.unwrap()
.worker
.as_mut()
{
None => {
return if self.folder_entries[&folder_hash].status.is_available()
|| (self.folder_entries[&folder_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&folder_hash))
return if self.mailbox_entries[&mailbox_hash].status.is_available()
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&mailbox_hash))
{
Ok(())
} else {
@ -739,31 +735,33 @@ impl Account {
break;
}
Ok(AsyncStatus::Payload(envs)) => {
debug!("got payload in status for {}", folder_hash);
self.load_mailbox(folder_hash, envs);
debug!("got payload in status for {}", mailbox_hash);
self.load_mailbox(mailbox_hash, envs);
}
Ok(AsyncStatus::Finished) => {
debug!("got finished in status for {}", folder_hash);
self.folder_entries.entry(folder_hash).and_modify(|entry| {
entry.status = MailboxStatus::Available;
entry.worker = None;
});
debug!("got finished in status for {}", mailbox_hash);
self.mailbox_entries
.entry(mailbox_hash)
.and_modify(|entry| {
entry.status = MailboxStatus::Available;
entry.worker = None;
});
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
self.index,
folder_hash,
mailbox_hash,
))))
.unwrap();
}
Ok(AsyncStatus::ProgressReport(n)) => {
self.folder_entries.entry(folder_hash).and_modify(|entry| {
match entry.status {
self.mailbox_entries
.entry(mailbox_hash)
.and_modify(|entry| match entry.status {
MailboxStatus::Parsing(ref mut d, _) => {
*d += n;
}
_ => {}
}
});
});
//return Err(n);
}
_ => {
@ -772,9 +770,9 @@ impl Account {
},
};
}
if self.folder_entries[&folder_hash].status.is_available()
|| (self.folder_entries[&folder_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&folder_hash))
if self.mailbox_entries[&mailbox_hash].status.is_available()
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
&& self.collection.mailboxes.contains_key(&mailbox_hash))
{
Ok(())
} else {
@ -785,23 +783,27 @@ impl Account {
pub fn save_special(
&self,
bytes: &[u8],
folder_type: SpecialUsageMailbox,
mailbox_type: SpecialUsageMailbox,
flags: Flag,
) -> Result<()> {
let mut failure = true;
for folder in &[
self.special_use_folder(folder_type),
self.special_use_folder(SpecialUsageMailbox::Inbox),
self.special_use_folder(SpecialUsageMailbox::Normal),
for mailbox in &[
self.special_use_mailbox(mailbox_type),
self.special_use_mailbox(SpecialUsageMailbox::Inbox),
self.special_use_mailbox(SpecialUsageMailbox::Normal),
] {
if folder.is_none() {
if mailbox.is_none() {
continue;
}
let folder = folder.unwrap();
if let Err(e) = self.save(bytes, folder, Some(flags)) {
let mailbox = mailbox.unwrap();
if let Err(e) = self.save(bytes, mailbox, Some(flags)) {
debug!("{:?} could not save msg", e);
melib::log(
format!("Could not save in '{}' folder: {}.", folder, e.to_string()),
format!(
"Could not save in '{}' mailbox: {}.",
mailbox,
e.to_string()
),
melib::ERROR,
);
} else {
@ -824,19 +826,19 @@ impl Account {
"Message was stored in {} so that you can restore it manually.",
file.path.display()
))
.set_summary("Could not save in any folder"));
.set_summary("Could not save in any mailbox"));
}
Ok(())
}
pub fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
pub fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
if self.settings.account.read_only() {
return Err(MeliError::new(format!(
"Account {} is read-only.",
self.name.as_str()
)));
}
self.backend.write().unwrap().save(bytes, folder, flags)
self.backend.write().unwrap().save(bytes, mailbox, flags)
}
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
@ -851,127 +853,132 @@ impl Account {
}
}
pub fn thread(&self, h: ThreadNodeHash, f: FolderHash) -> &ThreadNode {
pub fn thread(&self, h: ThreadNodeHash, f: MailboxHash) -> &ThreadNode {
&self.collection.threads[&f].thread_nodes()[&h]
}
pub fn folder_operation(
pub fn mailbox_operation(
&mut self,
op: crate::execute::actions::FolderOperation,
op: crate::execute::actions::MailboxOperation,
) -> Result<String> {
use crate::execute::actions::FolderOperation;
use crate::execute::actions::MailboxOperation;
if self.settings.account.read_only() {
return Err(MeliError::new("Account is read-only."));
}
match op {
FolderOperation::Create(path) => {
let (folder_hash, mut folders) = self
MailboxOperation::Create(path) => {
let (mailbox_hash, mut mailboxes) = self
.backend
.write()
.unwrap()
.create_folder(path.to_string())?;
.create_mailbox(path.to_string())?;
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
self.index,
folder_hash,
mailbox_hash,
))))
.unwrap();
let mut new = FileFolderConf::default();
new.folder_conf.subscribe = super::ToggleFlag::InternalVal(true);
new.folder_conf.usage =
if folders[&folder_hash].special_usage() != SpecialUsageMailbox::Normal {
Some(folders[&folder_hash].special_usage())
} else {
let tmp = SpecialUsageMailbox::detect_usage(folders[&folder_hash].name());
if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
folders.entry(folder_hash).and_modify(|entry| {
let _ = entry.set_special_usage(tmp.unwrap());
});
}
tmp
};
/* if new folder has parent, we need to update its children field */
if let Some(parent_hash) = folders[&folder_hash].parent() {
self.folder_entries.entry(parent_hash).and_modify(|parent| {
parent.ref_folder = folders.remove(&parent_hash).unwrap();
});
let mut new = FileMailboxConf::default();
new.mailbox_conf.subscribe = super::ToggleFlag::InternalVal(true);
new.mailbox_conf.usage = if mailboxes[&mailbox_hash].special_usage()
!= SpecialUsageMailbox::Normal
{
Some(mailboxes[&mailbox_hash].special_usage())
} else {
let tmp = SpecialUsageMailbox::detect_usage(mailboxes[&mailbox_hash].name());
if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
mailboxes.entry(mailbox_hash).and_modify(|entry| {
let _ = entry.set_special_usage(tmp.unwrap());
});
}
tmp
};
/* if new mailbox has parent, we need to update its children field */
if let Some(parent_hash) = mailboxes[&mailbox_hash].parent() {
self.mailbox_entries
.entry(parent_hash)
.and_modify(|parent| {
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
});
}
self.folder_entries.insert(
folder_hash,
FolderEntry {
name: folders[&folder_hash].path().to_string(),
self.mailbox_entries.insert(
mailbox_hash,
MailboxEntry {
name: mailboxes[&mailbox_hash].path().to_string(),
status: MailboxStatus::Parsing(0, 0),
conf: new,
worker: Account::new_worker(
folders[&folder_hash].clone(),
mailboxes[&mailbox_hash].clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
),
ref_folder: folders.remove(&folder_hash).unwrap(),
ref_mailbox: mailboxes.remove(&mailbox_hash).unwrap(),
},
);
self.collection
.threads
.insert(folder_hash, Threads::default());
.insert(mailbox_hash, Threads::default());
self.collection
.mailboxes
.insert(folder_hash, Default::default());
build_folders_order(
.insert(mailbox_hash, Default::default());
build_mailboxes_order(
&mut self.tree,
&self.folder_entries,
&mut self.folders_order,
&self.mailbox_entries,
&mut self.mailboxes_order,
);
Ok(format!("`{}` successfully created.", &path))
}
FolderOperation::Delete(path) => {
if self.folder_entries.len() == 1 {
MailboxOperation::Delete(path) => {
if self.mailbox_entries.len() == 1 {
return Err(MeliError::new("Cannot delete only mailbox."));
}
let folder_hash = if let Some((folder_hash, _)) = self
.folder_entries
let mailbox_hash = if let Some((mailbox_hash, _)) = self
.mailbox_entries
.iter()
.find(|(_, f)| f.ref_folder.path() == path)
.find(|(_, f)| f.ref_mailbox.path() == path)
{
*folder_hash
*mailbox_hash
} else {
return Err(MeliError::new("Mailbox with that path not found."));
};
let mut folders = self.backend.write().unwrap().delete_folder(folder_hash)?;
let mut mailboxes = self.backend.write().unwrap().delete_mailbox(mailbox_hash)?;
self.sender
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
self.index,
folder_hash,
mailbox_hash,
))))
.unwrap();
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
self.folders_order.remove(pos);
if let Some(pos) = self.mailboxes_order.iter().position(|&h| h == mailbox_hash) {
self.mailboxes_order.remove(pos);
}
if let Some(pos) = self.tree.iter().position(|n| n.hash == folder_hash) {
if let Some(pos) = self.tree.iter().position(|n| n.hash == mailbox_hash) {
self.tree.remove(pos);
}
if self.sent_folder == Some(folder_hash) {
self.sent_folder = None;
if self.sent_mailbox == Some(mailbox_hash) {
self.sent_mailbox = None;
}
self.collection.threads.remove(&folder_hash);
/* if deleted folder had parent, we need to update its children field */
self.collection.threads.remove(&mailbox_hash);
/* if deleted mailbox had parent, we need to update its children field */
if let Some(parent_hash) = self
.folder_entries
.remove(&folder_hash)
.mailbox_entries
.remove(&mailbox_hash)
.unwrap()
.ref_folder
.ref_mailbox
.parent()
{
self.folder_entries.entry(parent_hash).and_modify(|parent| {
parent.ref_folder = folders.remove(&parent_hash).unwrap();
});
self.mailbox_entries
.entry(parent_hash)
.and_modify(|parent| {
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
});
}
self.collection.mailboxes.remove(&folder_hash);
build_folders_order(
self.collection.mailboxes.remove(&mailbox_hash);
build_mailboxes_order(
&mut self.tree,
&self.folder_entries,
&mut self.folders_order,
&self.mailbox_entries,
&mut self.mailboxes_order,
);
// FIXME Kill worker as well
@ -979,18 +986,18 @@ impl Account {
Ok(format!("'`{}` has been deleted.", &path))
}
FolderOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
FolderOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
FolderOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
FolderOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")),
MailboxOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
MailboxOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
MailboxOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
MailboxOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")),
}
}
pub fn special_use_folder(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
pub fn special_use_mailbox(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
let ret = self
.folder_entries
.mailbox_entries
.iter()
.find(|(_, f)| f.conf.folder_conf().usage == Some(special_use));
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
if let Some(ret) = ret.as_ref() {
Some(ret.1.name())
} else {
@ -1017,10 +1024,10 @@ impl Account {
&self,
search_term: &str,
sort: (SortField, SortOrder),
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
if self.settings.account().format() == "imap" {
return crate::cache::imap_search(search_term, sort, folder_hash, &self.backend);
return crate::cache::imap_search(search_term, sort, mailbox_hash, &self.backend);
}
#[cfg(feature = "notmuch")]
@ -1049,7 +1056,7 @@ impl Account {
let mut ret = SmallVec::new();
let envelopes = self.collection.envelopes.read().unwrap();
for &env_hash in &self.collection[&folder_hash].iter() {
for &env_hash in &self.collection[&mailbox_hash].iter() {
let envelope = &envelopes[&env_hash];
if envelope.subject().contains(&search_term) {
ret.push(env_hash);
@ -1072,97 +1079,97 @@ impl Account {
}
}
impl Index<&FolderHash> for Account {
type Output = FolderEntry;
fn index(&self, index: &FolderHash) -> &FolderEntry {
&self.folder_entries[index]
impl Index<&MailboxHash> for Account {
type Output = MailboxEntry;
fn index(&self, index: &MailboxHash) -> &MailboxEntry {
&self.mailbox_entries[index]
}
}
impl IndexMut<&FolderHash> for Account {
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
self.folder_entries.get_mut(index).unwrap()
impl IndexMut<&MailboxHash> for Account {
fn index_mut(&mut self, index: &MailboxHash) -> &mut MailboxEntry {
self.mailbox_entries.get_mut(index).unwrap()
}
}
fn build_folders_order(
tree: &mut Vec<FolderNode>,
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
folders_order: &mut Vec<FolderHash>,
fn build_mailboxes_order(
tree: &mut Vec<MailboxNode>,
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
mailboxes_order: &mut Vec<MailboxHash>,
) {
tree.clear();
folders_order.clear();
for (h, f) in folder_entries.iter() {
if f.ref_folder.parent().is_none() {
mailboxes_order.clear();
for (h, f) in mailbox_entries.iter() {
if f.ref_mailbox.parent().is_none() {
fn rec(
h: FolderHash,
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
h: MailboxHash,
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
depth: usize,
) -> FolderNode {
let mut node = FolderNode {
) -> MailboxNode {
let mut node = MailboxNode {
hash: h,
children: Vec::new(),
depth,
};
for &c in folder_entries[&h].ref_folder.children() {
if folder_entries.contains_key(&c) {
node.children.push(rec(c, folder_entries, depth + 1));
for &c in mailbox_entries[&h].ref_mailbox.children() {
if mailbox_entries.contains_key(&c) {
node.children.push(rec(c, mailbox_entries, depth + 1));
}
}
node
};
tree.push(rec(*h, &folder_entries, 0));
tree.push(rec(*h, &mailbox_entries, 0));
}
}
tree.sort_unstable_by(|a, b| {
if folder_entries[&b.hash]
.ref_folder
if mailbox_entries[&b.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if folder_entries[&a.hash]
.ref_folder
} else if mailbox_entries[&a.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
folder_entries[&a.hash]
.ref_folder
mailbox_entries[&a.hash]
.ref_mailbox
.path()
.cmp(&folder_entries[&b.hash].ref_folder.path())
.cmp(&mailbox_entries[&b.hash].ref_mailbox.path())
}
});
let mut stack: SmallVec<[Option<&FolderNode>; 16]> = SmallVec::new();
let mut stack: SmallVec<[Option<&MailboxNode>; 16]> = SmallVec::new();
for n in tree.iter_mut() {
folders_order.push(n.hash);
mailboxes_order.push(n.hash);
n.children.sort_unstable_by(|a, b| {
if folder_entries[&b.hash]
.ref_folder
if mailbox_entries[&b.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if folder_entries[&a.hash]
.ref_folder
} else if mailbox_entries[&a.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
folder_entries[&a.hash]
.ref_folder
mailbox_entries[&a.hash]
.ref_mailbox
.path()
.cmp(&folder_entries[&b.hash].ref_folder.path())
.cmp(&mailbox_entries[&b.hash].ref_mailbox.path())
}
});
stack.extend(n.children.iter().rev().map(Some));
while let Some(Some(next)) = stack.pop() {
folders_order.push(next.hash);
mailboxes_order.push(next.hash);
stack.extend(next.children.iter().rev().map(Some));
}
}

View File

@ -109,13 +109,13 @@ shortcut_key_values! { "listing",
scroll_down |> "Scroll down list." |> Key::Down,
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
next_account |> "Go to next account." |> Key::Char('h'),
next_folder |> "Go to next folder." |> Key::Char('J'),
next_mailbox |> "Go to next mailbox." |> Key::Char('J'),
next_page |> "Go to next page." |> Key::PageDown,
prev_account |> "Go to previous account." |> Key::Char('l'),
prev_folder |> "Go to previous folder." |> Key::Char('K'),
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
prev_page |> "Go to previous page." |> Key::PageUp,
search |> "Search within list of e-mails." |> Key::Char('/'),
refresh |> "Manually request a folder refresh." |> Key::F(5),
refresh |> "Manually request a mailbox refresh." |> Key::F(5),
set_seen |> "Set thread as seen." |> Key::Char('n'),
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`')
}

View File

@ -25,7 +25,7 @@ pub use melib::thread::{SortField, SortOrder};
use nom::{digit, not_line_ending, IResult};
use std;
pub mod actions;
use actions::FolderOperation;
use actions::MailboxOperation;
pub mod history;
pub use crate::actions::AccountAction::{self, *};
pub use crate::actions::Action::{self, *};
@ -247,74 +247,74 @@ define_commands!([
);
)
},
{ tags: ["create-folder "],
desc: "create-folder ACCOUNT FOLDER_PATH",
{ tags: ["create-mailbox "],
desc: "create-mailbox ACCOUNT MAILBOX_PATH",
parser:(
named!( create_folder<Action>,
named!( create_mailbox<Action>,
do_parse!(
ws!(tag!("create-folder"))
ws!(tag!("create-mailbox"))
>> account: quoted_argument
>> is_a!(" ")
>> path: quoted_argument
>> (Folder(account.to_string(), FolderOperation::Create(path.to_string())))
>> (Mailbox(account.to_string(), MailboxOperation::Create(path.to_string())))
)
);
)
},
{ tags: ["subscribe-folder "],
desc: "subscribe-folder ACCOUNT FOLDER_PATH",
{ tags: ["subscribe-mailbox "],
desc: "subscribe-mailbox ACCOUNT MAILBOX_PATH",
parser:(
named!( sub_folder<Action>,
named!( sub_mailbox<Action>,
do_parse!(
ws!(tag!("subscribe-folder"))
ws!(tag!("subscribe-mailbox"))
>> account: quoted_argument
>> is_a!(" ")
>> path: quoted_argument
>> (Folder(account.to_string(), FolderOperation::Subscribe(path.to_string())))
>> (Mailbox(account.to_string(), MailboxOperation::Subscribe(path.to_string())))
)
);
)
},
{ tags: ["unsubscribe-folder "],
desc: "unsubscribe-folder ACCOUNT FOLDER_PATH",
{ tags: ["unsubscribe-mailbox "],
desc: "unsubscribe-mailbox ACCOUNT MAILBOX_PATH",
parser:(
named!( unsub_folder<Action>,
named!( unsub_mailbox<Action>,
do_parse!(
ws!(tag!("unsubscribe-folder"))
ws!(tag!("unsubscribe-mailbox"))
>> account: quoted_argument
>> is_a!(" ")
>> path: quoted_argument
>> (Folder(account.to_string(), FolderOperation::Unsubscribe(path.to_string())))
>> (Mailbox(account.to_string(), MailboxOperation::Unsubscribe(path.to_string())))
)
);
)
},
{ tags: ["rename-folder "],
desc: "rename-folder ACCOUNT FOLDER_PATH_SRC FOLDER_PATH_DEST",
{ tags: ["rename-mailbox "],
desc: "rename-mailbox ACCOUNT MAILBOX_PATH_SRC MAILBOX_PATH_DEST",
parser:(
named!( rename_folder<Action>,
named!( rename_mailbox<Action>,
do_parse!(
ws!(tag!("rename-folder"))
ws!(tag!("rename-mailbox"))
>> account: quoted_argument
>> is_a!(" ")
>> src: quoted_argument
>> is_a!(" ")
>> dest: quoted_argument
>> (Folder(account.to_string(), FolderOperation::Rename(src.to_string(), dest.to_string())))
>> (Mailbox(account.to_string(), MailboxOperation::Rename(src.to_string(), dest.to_string())))
)
);
)
},
{ tags: ["delete-folder "],
desc: "delete-folder ACCOUNT FOLDER_PATH",
{ tags: ["delete-mailbox "],
desc: "delete-mailbox ACCOUNT MAILBOX_PATH",
parser:(
named!( delete_folder<Action>,
named!( delete_mailbox<Action>,
do_parse!(
ws!(tag!("delete-folder"))
ws!(tag!("delete-mailbox"))
>> account: quoted_argument
>> is_a!(" ")
>> path: quoted_argument
>> (Folder(account.to_string(), FolderOperation::Delete(path.to_string())))
>> (Mailbox(account.to_string(), MailboxOperation::Delete(path.to_string())))
)
);
)
@ -437,5 +437,5 @@ named!(account_action<Action>, alt_complete!(reindex));
named!(view<Action>, alt_complete!(pipe | save_attachment));
named!(pub parse_command<Action>,
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | view | compose_action | create_folder | sub_folder | unsub_folder | delete_folder | rename_folder | account_action )
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | view | compose_action | create_mailbox | sub_mailbox | unsub_mailbox | delete_mailbox | rename_mailbox | account_action )
);

View File

@ -24,7 +24,7 @@
*/
use crate::components::Component;
use melib::backends::FolderHash;
use melib::backends::MailboxHash;
pub use melib::thread::{SortField, SortOrder};
use melib::{Draft, EnvelopeHash};
@ -55,7 +55,7 @@ pub enum ListingAction {
pub enum TabAction {
New(Option<Box<dyn Component>>),
NewDraft(usize, Option<Draft>),
Reply((usize, FolderHash), EnvelopeHash), // thread coordinates (account, mailbox) and envelope
Reply((usize, MailboxHash), EnvelopeHash), // thread coordinates (account, mailbox) and envelope
Close,
Edit(usize, EnvelopeHash), // account_position, envelope hash
Kill(Uuid),
@ -87,14 +87,14 @@ pub enum AccountAction {
}
#[derive(Debug)]
pub enum FolderOperation {
Create(NewFolderPath),
Delete(FolderPath),
Subscribe(FolderPath),
Unsubscribe(FolderPath),
Rename(FolderPath, NewFolderPath),
pub enum MailboxOperation {
Create(NewMailboxPath),
Delete(MailboxPath),
Subscribe(MailboxPath),
Unsubscribe(MailboxPath),
Rename(MailboxPath, NewMailboxPath),
// Placeholder
SetPermissions(FolderPath),
SetPermissions(MailboxPath),
}
#[derive(Debug)]
@ -110,7 +110,7 @@ pub enum Action {
SetEnv(String, String),
PrintEnv(String),
Compose(ComposeAction),
Folder(AccountName, FolderOperation),
Mailbox(AccountName, MailboxOperation),
AccountAction(AccountName, AccountAction),
}
@ -128,12 +128,12 @@ impl Action {
Action::SetEnv(_, _) => false,
Action::PrintEnv(_) => false,
Action::Compose(_) => false,
Action::Folder(_, _) => true,
Action::Mailbox(_, _) => true,
Action::AccountAction(_, _) => false,
}
}
}
type AccountName = String;
type FolderPath = String;
type NewFolderPath = String;
type MailboxPath = String;
type NewMailboxPath = String;

View File

@ -22,8 +22,8 @@
use super::*;
use fnv::FnvHashMap;
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use melib::backends::FolderHash;
use melib::backends::{Backend, BackendOp, Backends, Folder, MailBackend, RefreshEventConsumer};
use melib::backends::MailboxHash;
use melib::backends::{Backend, BackendOp, Backends, MailBackend, Mailbox, RefreshEventConsumer};
use melib::conf::AccountSettings;
use melib::email::{Envelope, EnvelopeHash, Flag};
use melib::error::{MeliError, Result};
@ -86,9 +86,9 @@ impl MailBackend for PluginBackend {
fn connect(&mut self) {}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let _folder_hash = folder.hash();
let _mailbox_hash = mailbox.hash();
let channel = self.channel.clone();
let handle = {
let tx = w.tx();
@ -180,7 +180,7 @@ impl MailBackend for PluginBackend {
fn refresh(
&mut self,
_folder_hash: FolderHash,
_mailbox_hash: MailboxHash,
_sender: RefreshEventConsumer,
) -> Result<Async<()>> {
Err(MeliError::new("Unimplemented."))
@ -193,9 +193,9 @@ impl MailBackend for PluginBackend {
Err(MeliError::new("Unimplemented."))
}
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
let mut ret: FnvHashMap<FolderHash, Folder> = Default::default();
ret.insert(0, Folder::default());
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
let mut ret: FnvHashMap<MailboxHash, Mailbox> = Default::default();
ret.insert(0, Mailbox::default());
Ok(ret)
}
@ -208,13 +208,13 @@ impl MailBackend for PluginBackend {
})
}
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
fn create_folder(
fn create_mailbox(
&mut self,
_name: String,
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
Err(MeliError::new("Unimplemented."))
}
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {

View File

@ -30,7 +30,7 @@ Input is received in the main loop from threads which listen on the stdin for us
use super::*;
use crate::plugins::PluginManager;
use melib::backends::{FolderHash, NotifyFn};
use melib::backends::{MailboxHash, NotifyFn};
use crossbeam::channel::{unbounded, Receiver, Sender};
use fnv::FnvHashMap;
@ -85,7 +85,7 @@ impl InputHandler {
/// A context container for loaded settings, accounts, UI changes, etc.
pub struct Context {
pub accounts: Vec<Account>,
pub mailbox_hashes: FnvHashMap<FolderHash, usize>,
pub mailbox_hashes: FnvHashMap<MailboxHash, usize>,
pub settings: Settings,
pub runtime_settings: Settings,
@ -126,12 +126,12 @@ impl Context {
pub fn account_status(
&mut self,
idx_a: usize,
folder_hash: FolderHash,
mailbox_hash: MailboxHash,
) -> result::Result<(), usize> {
match self.accounts[idx_a].status(folder_hash) {
match self.accounts[idx_a].status(mailbox_hash) {
Ok(()) => {
self.replies
.push_back(UIEvent::MailboxUpdate((idx_a, folder_hash)));
.push_back(UIEvent::MailboxUpdate((idx_a, mailbox_hash)));
Ok(())
}
Err(n) => Err(n),
@ -149,13 +149,13 @@ impl Context {
let ret = accounts[account_pos].is_online();
if ret.is_ok() {
if !was_online {
for folder_node in accounts[account_pos].list_folders() {
for mailbox_node in accounts[account_pos].list_mailboxes() {
debug!(
"hash & folder: {:?} {}",
folder_node.hash,
accounts[account_pos][&folder_node.hash].name()
"hash & mailbox: {:?} {}",
mailbox_node.hash,
accounts[account_pos][&mailbox_node.hash].name()
);
mailbox_hashes.insert(folder_node.hash, account_pos);
mailbox_hashes.insert(mailbox_node.hash, account_pos);
}
/* Account::watch() needs
* - work_controller to pass `work_context` to the watcher threads and then add them
@ -267,7 +267,7 @@ impl State {
&backends,
work_controller.get_context(),
sender.clone(),
NotifyFn::new(Box::new(move |f: FolderHash| {
NotifyFn::new(Box::new(move |f: MailboxHash| {
sender
.send(ThreadEvent::UIEvent(UIEvent::WorkerProgress(f)))
.unwrap();
@ -347,7 +347,7 @@ impl State {
for i in 0..s.context.accounts.len() {
if s.context.is_online(i).is_ok() && s.context.accounts[i].is_empty() {
return Err(MeliError::new(format!(
"Account {} has no folders configured.",
"Account {} has no mailboxes configured.",
s.context.accounts[i].name()
)));
}
@ -357,7 +357,7 @@ impl State {
}
/*
* When we receive a folder hash from a watcher thread,
* When we receive a mailbox hash from a watcher thread,
* we match the hash to the index of the mailbox, request a reload
* and startup a thread to remind us to poll it every now and then till it's finished.
*/
@ -796,14 +796,14 @@ impl State {
env::var(key.as_str()).unwrap_or_else(|e| e.to_string()),
)));
}
Folder(account_name, op) => {
Mailbox(account_name, op) => {
if let Some(account) = self
.context
.accounts
.iter_mut()
.find(|a| a.name() == account_name)
{
match account.folder_operation(op) {
match account.mailbox_operation(op) {
Err(err) => {
self.context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(err.to_string()),
@ -911,9 +911,9 @@ impl State {
self.child = Some(child);
return;
}
UIEvent::WorkerProgress(folder_hash) => {
if let Some(&account_idx) = self.context.mailbox_hashes.get(&folder_hash) {
let _ = self.context.accounts[account_idx].status(folder_hash);
UIEvent::WorkerProgress(mailbox_hash) => {
if let Some(&account_idx) = self.context.mailbox_hashes.get(&mailbox_hash) {
let _ = self.context.accounts[account_idx].status(mailbox_hash);
}
return;
}

View File

@ -38,7 +38,7 @@ pub use self::helpers::*;
use super::execute::Action;
use super::terminal::*;
use melib::backends::FolderHash;
use melib::backends::MailboxHash;
use melib::{EnvelopeHash, RefreshEvent};
use nix::unistd::Pid;
use std;
@ -63,7 +63,7 @@ pub enum ThreadEvent {
Input(Key),
/// User input and input as raw bytes.
InputRaw((Key, Vec<u8>)),
/// A watched folder has been refreshed.
/// A watched Mailbox has been refreshed.
RefreshMailbox(Box<RefreshEvent>),
UIEvent(UIEvent),
/// A thread has updated some of its information
@ -110,13 +110,13 @@ pub enum UIEvent {
Notification(Option<String>, String, Option<NotificationType>),
Action(Action),
StatusEvent(StatusEvent),
MailboxUpdate((usize, FolderHash)), // (account_idx, mailbox_idx)
MailboxDelete((usize, FolderHash)),
MailboxCreate((usize, FolderHash)),
MailboxUpdate((usize, MailboxHash)), // (account_idx, mailbox_idx)
MailboxDelete((usize, MailboxHash)),
MailboxCreate((usize, MailboxHash)),
AccountStatusChange(usize),
ComponentKill(Uuid),
WorkerProgress(FolderHash),
StartupCheck(FolderHash),
WorkerProgress(MailboxHash),
StartupCheck(MailboxHash),
RefreshEvent(Box<RefreshEvent>),
EnvelopeUpdate(EnvelopeHash),
EnvelopeRename(EnvelopeHash, EnvelopeHash), // old_hash, new_hash