/*
* meli - accounts module.
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see .
*/
/*!
* Account management from user configuration.
*/
use super::{AccountConf, FolderConf};
use fnv::FnvHashMap;
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus};
use melib::backends::{
BackendOp, Backends, Folder, FolderHash, FolderOperation, MailBackend, NotifyFn, ReadOnlyOp,
RefreshEvent, RefreshEventConsumer, RefreshEventKind, SpecialUseMailbox,
};
use melib::error::{MeliError, Result};
use melib::mailbox::*;
use melib::thread::{ThreadHash, ThreadNode, Threads};
use melib::AddressBook;
use melib::StackVec;
use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
use crate::{workers::WorkController, StatusEvent, ThreadEvent};
use crossbeam::Sender;
use std::collections::VecDeque;
use std::fs;
use std::io;
use std::ops::{Index, IndexMut};
use std::result;
use std::sync::Arc;
pub type Worker = Option>>>;
macro_rules! mailbox {
($idx:expr, $folders:expr) => {
$folders.get_mut(&$idx).unwrap().unwrap_mut()
};
}
#[derive(Serialize, Debug)]
pub enum MailboxEntry {
Available(Mailbox),
Failed(MeliError),
/// first argument is done work, and second is total work
Parsing(Mailbox, usize, usize),
}
impl Default for MailboxEntry {
fn default() -> Self {
MailboxEntry::Parsing(Mailbox::default(), 0, 0)
}
}
impl std::fmt::Display for MailboxEntry {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
MailboxEntry::Available(ref m) => m.name().to_string(),
MailboxEntry::Failed(ref e) => e.to_string(),
MailboxEntry::Parsing(_, done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
}
)
}
}
impl MailboxEntry {
pub fn is_available(&self) -> bool {
if let MailboxEntry::Available(_) = self {
true
} else {
false
}
}
pub fn is_parsing(&self) -> bool {
if let MailboxEntry::Parsing(_, _, _) = self {
true
} else {
false
}
}
pub fn unwrap_mut(&mut self) -> &mut Mailbox {
match self {
MailboxEntry::Available(ref mut m) => m,
MailboxEntry::Parsing(ref mut m, _, _) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
}
pub fn unwrap(&self) -> &Mailbox {
match self {
MailboxEntry::Available(ref m) => m,
MailboxEntry::Parsing(ref m, _, _) => m,
e => panic!(format!("mailbox is not available! {:#}", e)),
}
}
}
#[derive(Debug)]
pub struct Account {
name: String,
pub(crate) folders: FnvHashMap,
pub(crate) folder_confs: FnvHashMap,
pub(crate) folders_order: Vec,
pub(crate) folder_names: FnvHashMap,
tree: Vec,
sent_folder: Option,
pub(crate) collection: Collection,
pub(crate) address_book: AddressBook,
pub(crate) workers: FnvHashMap,
pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf,
pub(crate) backend: Box,
event_queue: VecDeque<(FolderHash, RefreshEvent)>,
notify_fn: Arc,
}
impl Drop for Account {
fn drop(&mut self) {
//TODO: Avoid panics
let data_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
if let Ok(data) = data_dir.place_data_file("addressbook") {
/* place result in cache directory */
let f = match fs::File::create(data) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
serde_json::to_writer(writer, &self.address_book).unwrap();
};
if let Ok(data) = data_dir.place_data_file("mailbox") {
/* place result in cache directory */
let f = match fs::File::create(data) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &self.folders).unwrap();
};
}
}
pub struct MailboxIterator<'a> {
folders_order: &'a [FolderHash],
folders: &'a FnvHashMap,
pos: usize,
}
impl<'a> Iterator for MailboxIterator<'a> {
type Item = &'a MailboxEntry;
fn next(&mut self) -> Option<&'a MailboxEntry> {
if self.pos == self.folders.len() {
return None;
}
let fh = &self.folders_order[self.pos];
self.pos += 1;
Some(&self.folders[&fh])
}
}
#[derive(Serialize, Debug, Default)]
struct FolderNode {
hash: FolderHash,
kids: Vec,
}
impl Account {
pub fn new(name: String, settings: AccountConf, map: &Backends, notify_fn: NotifyFn) -> Self {
let s = settings.clone();
let mut backend = map.get(settings.account().format())(
settings.account(),
Box::new(move |path: &str| {
s.folder_confs.contains_key(path) && s.folder_confs[path].subscribe.is_true()
}),
);
let mut ref_folders: FnvHashMap = backend.folders();
let mut folders: FnvHashMap =
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
let mut folders_order: Vec = Vec::with_capacity(ref_folders.len());
let mut workers: FnvHashMap = FnvHashMap::default();
let notify_fn = Arc::new(notify_fn);
let mut folder_names = FnvHashMap::default();
let mut folder_confs = FnvHashMap::default();
let mut sent_folder = None;
for f in ref_folders.values_mut() {
if !settings.folder_confs.contains_key(f.path())
|| settings.folder_confs[f.path()].subscribe.is_false()
{
/* Skip unsubscribed folder */
continue;
}
match settings.folder_confs[f.path()].usage {
Some(SpecialUseMailbox::Sent) => {
sent_folder = Some(f.hash());
}
_ => {}
}
folder_confs.insert(f.hash(), settings.folder_confs[f.path()].clone());
folder_names.insert(f.hash(), f.path().to_string());
}
let mut stack: StackVec = StackVec::new();
let mut tree: Vec = Vec::new();
let mut collection: Collection = Collection::new(Default::default());
for (h, f) in ref_folders.iter() {
if !settings.folder_confs.contains_key(f.path())
|| settings.folder_confs[f.path()].subscribe.is_false()
{
/* Skip unsubscribed folder */
continue;
}
if f.parent().is_none() {
fn rec(h: FolderHash, ref_folders: &FnvHashMap) -> FolderNode {
let mut node = FolderNode {
hash: h,
kids: Vec::new(),
};
for &c in ref_folders[&h].children() {
node.kids.push(rec(c, ref_folders));
}
node
};
tree.push(rec(*h, &ref_folders));
for &c in f.children() {
stack.push(c);
}
while let Some(next) = stack.pop() {
for c in ref_folders[&next].children() {
stack.push(*c);
}
}
}
folders.insert(
*h,
MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
);
workers.insert(
*h,
Account::new_worker(&settings, f.clone(), &mut backend, notify_fn.clone()),
);
collection.threads.insert(*h, Threads::default());
}
tree.sort_unstable_by_key(|f| ref_folders[&f.hash].path());
let mut stack: StackVec