wasm-demo/melib/src/backends/demo.rs

398 lines
12 KiB
Rust

/*
* meli - mailbox 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 <http://www.gnu.org/licenses/>.
*/
/*!
* https://wiki2.dovecot.org/MailboxFormat/demo
*/
use crate::backends::*;
use crate::conf::AccountSettings;
use crate::email::*;
use crate::error::{MeliError, Result};
use crate::get_path_hash;
use std::collections::{hash_map::HashMap, BTreeMap};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
mod demo_corpus;
#[derive(Debug, Default)]
struct DemoMailbox {
hash: MailboxHash,
name: String,
path: PathBuf,
envs: Vec<EnvelopeHash>,
children: Vec<MailboxHash>,
parent: Option<MailboxHash>,
usage: Arc<RwLock<SpecialUsageMailbox>>,
is_subscribed: bool,
permissions: MailboxPermissions,
pub total: Arc<Mutex<usize>>,
pub unseen: Arc<Mutex<usize>>,
}
impl BackendMailbox for DemoMailbox {
fn hash(&self) -> MailboxHash {
self.hash
}
fn name(&self) -> &str {
self.name.as_str()
}
fn path(&self) -> &str {
self.path.to_str().unwrap()
}
fn change_name(&mut self, s: &str) {
self.name = s.to_string();
}
fn clone(&self) -> Mailbox {
Box::new(DemoMailbox {
hash: self.hash,
name: self.name.clone(),
path: self.path.clone(),
envs: self.envs.clone(),
children: self.children.clone(),
usage: self.usage.clone(),
is_subscribed: self.is_subscribed,
parent: self.parent,
permissions: self.permissions,
unseen: self.unseen.clone(),
total: self.total.clone(),
})
}
fn children(&self) -> &[MailboxHash] {
&self.children
}
fn parent(&self) -> Option<MailboxHash> {
self.parent
}
fn special_usage(&self) -> SpecialUsageMailbox {
*self.usage.read().unwrap()
}
fn permissions(&self) -> MailboxPermissions {
self.permissions
}
fn is_subscribed(&self) -> bool {
self.is_subscribed
}
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()> {
self.is_subscribed = new_val;
Ok(())
}
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()> {
*self.usage.write()? = new_val;
Ok(())
}
fn count(&self) -> Result<(usize, usize)> {
Ok((*self.unseen.lock()?, *self.total.lock()?))
}
}
/// `BackendOp` implementor for Demo
#[derive(Debug, Default)]
pub struct DemoOp {
hash: EnvelopeHash,
index: Arc<HashMap<EnvelopeHash, &'static str>>,
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
}
impl DemoOp {
pub fn new(
hash: EnvelopeHash,
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
index: Arc<HashMap<EnvelopeHash, &'static str>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
) -> Self {
DemoOp {
hash,
flags,
index,
tag_index,
}
}
}
impl BackendOp for DemoOp {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
let ret = self.index[&self.hash].as_bytes().to_vec();
Ok(Box::pin(async { Ok(ret) }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {
let ret = self.flags.read().unwrap()[&self.hash].0;
Ok(Box::pin(async move { Ok(ret) }))
}
}
/// Demo backend
#[derive(Debug)]
pub struct DemoType {
account_name: String,
account_hash: AccountHash,
root_mailbox: String,
mailboxes: Arc<Mutex<HashMap<MailboxHash, DemoMailbox>>>,
envelopes: HashMap<EnvelopeHash, Envelope>,
index: Arc<HashMap<EnvelopeHash, &'static str>>,
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
event_consumer: BackendEventConsumer,
}
impl MailBackend for DemoType {
fn capabilities(&self) -> MailBackendCapabilities {
MailBackendCapabilities {
is_async: false,
is_remote: false,
extensions: None,
supports_search: false,
supports_tags: true,
supports_submission: false,
}
}
fn is_online(&self) -> ResultFuture<()> {
Ok(Box::pin(async { Ok(()) }))
}
fn fetch(
&mut self,
mailbox_hash: MailboxHash,
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
let mailboxes = self.mailboxes.clone();
let mut mailbox_lock = mailboxes.lock().unwrap();
let mailbox_entry = mailbox_lock.entry(mailbox_hash).or_default();
let mut payload = Vec::with_capacity(mailbox_entry.envs.len());
for env_hash in mailbox_entry.envs.iter() {
payload.push(self.envelopes[env_hash].clone());
}
Ok(Box::pin(async_stream::try_stream! {
yield payload;
}))
}
fn watch(&self) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
Ok(Box::pin(async { Ok(()) }))
}
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
debug!("entering mailboxes");
let ret = Ok(self
.mailboxes
.lock()
.unwrap()
.iter()
.map(|(h, f)| (*h, f.clone() as Mailbox))
.collect());
debug!("returning mailboxes");
Ok(Box::pin(async { ret }))
}
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
Ok(Box::new(DemoOp::new(
hash,
self.flags.clone(),
self.index.clone(),
self.tag_index.clone(),
)))
}
fn save(
&self,
_bytes: Vec<u8>,
_mailbox_hash: MailboxHash,
_flags: Option<Flag>,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn copy_messages(
&mut self,
_env_hashes: EnvelopeHashBatch,
_source_mailbox_hash: MailboxHash,
_destination_mailbox_hash: MailboxHash,
_move_: bool,
) -> ResultFuture<()> {
Err(MeliError::new("Unimplemented."))
}
fn set_flags(
&mut self,
env_hashes: EnvelopeHashBatch,
mailbox_hash: MailboxHash,
flags: SmallVec<[(std::result::Result<Flag, String>, bool); 8]>,
) -> ResultFuture<()> {
let mut tag_lck = self.tag_index.write().unwrap();
let mut flags_lck = self.flags.write().unwrap();
for env_hash in env_hashes.iter() {
let mut env_flags = flags_lck.get_mut(&env_hash).unwrap();
for set in flags.iter() {
match set {
(Ok(flag), value) => {
let mut flags = env_flags.0;
flags.set(*flag, *value);
env_flags.0 = flags;
}
(Err(tag), value) => {
let hash = tag_hash!(tag);
if *value {
tag_lck.insert(hash, tag.into());
if !env_flags.1.contains(&hash) {
env_flags.1.push(hash);
}
} else {
if let Some(pos) = env_flags.1.iter().position(|h| *h == hash) {
env_flags.1.remove(pos);
}
}
}
}
}
(self.event_consumer)(
self.account_hash,
BackendEvent::Refresh(RefreshEvent {
mailbox_hash,
account_hash: self.account_hash,
kind: RefreshEventKind::NewFlags(
env_hash,
(
env_flags.0,
env_flags
.1
.iter()
.map(|tag_hash| tag_lck[tag_hash].to_string())
.collect::<Vec<String>>(),
),
),
}),
);
}
Ok(Box::pin(async { Ok(()) }))
}
fn as_any(&self) -> &dyn ::core::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
self
}
}
impl DemoType {
pub fn new(
s: &AccountSettings,
_is_subscribed: Box<dyn Fn(&str) -> bool>,
event_consumer: BackendEventConsumer,
) -> Result<Box<dyn MailBackend>> {
let account_hash = {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
let mut hasher = DefaultHasher::new();
hasher.write(s.name().as_bytes());
hasher.finish()
};
let mut ret = DemoType {
account_name: s.name().to_string(),
account_hash,
root_mailbox: s.root_mailbox.to_string(),
event_consumer,
mailboxes: Default::default(),
envelopes: Default::default(),
index: Default::default(),
flags: Default::default(),
tag_index: Default::default(),
};
{
let mut mbox_lck = ret.mailboxes.lock().unwrap();
let mut flags_lck = ret.flags.write().unwrap();
let index_lck = Arc::get_mut(&mut ret.index).unwrap();
//let tag_lck = ret.tag_index.write().unwrap();
for (path, name, envelopes) in demo_corpus::DEMO_MAILBOXES {
let hash = get_path_hash!(name);
let mut mbox = DemoMailbox {
hash,
path: path.into(),
name: name.to_string(),
envs: vec![],
children: Vec::new(),
parent: None,
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
is_subscribed: true,
permissions: MailboxPermissions {
create_messages: false,
remove_messages: false,
set_flags: false,
create_child: false,
rename_messages: false,
delete_messages: false,
delete_mailbox: false,
change_permissions: false,
},
unseen: Arc::new(Mutex::new(0)),
total: Arc::new(Mutex::new(0)),
};
for bytes in *envelopes {
let env = Envelope::from_bytes(bytes.as_bytes(), None).unwrap();
index_lck.insert(env.hash(), bytes);
flags_lck.insert(env.hash(), (env.flags(), vec![]));
mbox.envs.push(env.hash());
ret.envelopes.insert(env.hash(), env);
}
mbox_lck.insert(hash, mbox);
}
}
Ok(Box::new(ret))
}
pub fn validate_config(_s: &AccountSettings) -> Result<()> {
Ok(())
}
}
static _DEMO_MAILBOXES: &'static [(&'static str, &'static str, &'static [&'static str])] = &[
(
"INBOX",
"INBOX",
&[include_str!(
"../../../demo/ermis-f/cur/6813190.1075861340968.JavaMail.evans@thyme:2,S"
)],
),
("INBOX/All", "All", &[]),
("INBOX/Discussion Threads", "Discussion Threads", &[]),
("INBOX/Sent", "Sent", &[]),
("INBOX/Notes", "Notes", &[]),
("INBOX/Trash", "Trash", &[]),
];