jmap: move EmailObject state to Store

State is per account, not per mailbox, so move it to the per-account
store.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/495/head
Manos Pitsidianakis 3 weeks ago
parent 9865211076
commit 6d0d968040
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -25,9 +25,8 @@ use crate::{
backends::{BackendMailbox, LazyCountSet, MailboxPermissions, SpecialUsageMailbox},
error::Result,
jmap::{
email::EmailObject,
mailbox::{JmapRights, MailboxObject},
objects::{Id, State},
objects::Id,
},
Mailbox, MailboxHash,
};
@ -50,7 +49,6 @@ pub struct JmapMailbox {
pub unread_emails: Arc<Mutex<LazyCountSet>>,
pub unread_threads: u64,
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
pub email_state: Arc<Mutex<Option<State<EmailObject>>>>,
pub email_query_state: Arc<Mutex<Option<String>>>,
}

@ -439,18 +439,12 @@ impl JmapConnection {
}
pub async fn email_changes(&self, mailbox_hash: MailboxHash) -> Result<()> {
let mut current_state: State<EmailObject> = if let Some(s) = self
.store
.mailboxes
.read()
.unwrap()
.get(&mailbox_hash)
.and_then(|mbox| mbox.email_state.lock().unwrap().clone())
{
s
} else {
return Ok(());
};
let mut current_state: State<EmailObject> =
if let Some(s) = self.store.email_state.lock().await.clone() {
s
} else {
return Ok(());
};
let mail_account_id = self.session_guard().await?.mail_account_id();
loop {
let email_changes_call: EmailChanges = EmailChanges::new(
@ -658,37 +652,32 @@ impl JmapConnection {
}
let GetResponse::<EmailObject> { list, .. } =
GetResponse::<EmailObject>::try_from(v.method_responses.remove(0))?;
let mut mailboxes_lck = self.store.mailboxes.write().unwrap();
for envobj in list {
if let Some(env_hash) = reverse_id_store_lck.get(&envobj.id) {
let new_flags =
protocol::keywords_to_flags(envobj.keywords().keys().cloned().collect());
mailboxes_lck.entry(mailbox_hash).and_modify(|mbox| {
if new_flags.0.contains(Flag::SEEN) {
mbox.unread_emails.lock().unwrap().remove(*env_hash);
} else {
mbox.unread_emails.lock().unwrap().insert_new(*env_hash);
}
});
self.add_refresh_event(RefreshEvent {
account_hash: self.store.account_hash,
mailbox_hash,
kind: RefreshEventKind::NewFlags(*env_hash, new_flags),
});
{
let mut mailboxes_lck = self.store.mailboxes.write().unwrap();
for envobj in list {
if let Some(env_hash) = reverse_id_store_lck.get(&envobj.id) {
let new_flags = protocol::keywords_to_flags(
envobj.keywords().keys().cloned().collect(),
);
mailboxes_lck.entry(mailbox_hash).and_modify(|mbox| {
if new_flags.0.contains(Flag::SEEN) {
mbox.unread_emails.lock().unwrap().remove(*env_hash);
} else {
mbox.unread_emails.lock().unwrap().insert_new(*env_hash);
}
});
self.add_refresh_event(RefreshEvent {
account_hash: self.store.account_hash,
mailbox_hash,
kind: RefreshEventKind::NewFlags(*env_hash, new_flags),
});
}
}
}
drop(mailboxes_lck);
if changes_response.has_more_changes {
current_state = changes_response.new_state;
} else {
self.store
.mailboxes
.write()
.unwrap()
.entry(mailbox_hash)
.and_modify(|mbox| {
*mbox.email_state.lock().unwrap() = Some(changes_response.new_state);
});
*self.store.email_state.lock().await = Some(changes_response.new_state);
break;
}

@ -82,7 +82,7 @@ pub mod session;
use session::*;
pub mod objects;
use objects::{BlobObject, Id, Object, State};
use objects::{BlobObject, Id, State};
pub mod methods;
use methods::{
@ -291,7 +291,8 @@ pub struct Store {
pub collection: Collection,
pub mailboxes: Arc<RwLock<HashMap<MailboxHash, JmapMailbox>>>,
pub mailboxes_index: Arc<RwLock<HashMap<MailboxHash, HashSet<EnvelopeHash>>>>,
pub mailbox_state: Arc<FutureMutex<State<mailbox::MailboxObject>>>,
pub mailbox_state: Arc<FutureMutex<Option<State<mailbox::MailboxObject>>>>,
pub email_state: Arc<FutureMutex<Option<State<email::EmailObject>>>>,
pub online_status: OnlineStatus,
pub is_subscribed: IsSubscribedFn,
pub core_capabilities: Arc<Mutex<IndexMap<String, CapabilitiesObject>>>,
@ -703,7 +704,7 @@ impl MailBackend for JmapType {
let mut conn = connection.lock().await;
let mail_account_id = conn.session_guard().await?.mail_account_id();
let mailbox_set_call = mailbox::MailboxSet::new(
Set::<mailbox::MailboxObject>::new(Some(mailbox_state))
Set::<mailbox::MailboxObject>::new(mailbox_state)
.account_id(mail_account_id)
.update(Some({
indexmap! {
@ -744,7 +745,7 @@ impl MailBackend for JmapType {
let mut conn = connection.lock().await;
let mail_account_id = conn.session_guard().await?.mail_account_id();
let mailbox_set_call = mailbox::MailboxSet::new(
Set::<mailbox::MailboxObject>::new(Some(mailbox_state))
Set::<mailbox::MailboxObject>::new(mailbox_state)
.account_id(mail_account_id)
.create(Some({
let id: Id<mailbox::MailboxObject> = path.as_str().into();
@ -870,9 +871,10 @@ impl MailBackend for JmapType {
}
let conn = connection.lock().await;
let mail_account_id = conn.session_guard().await?.mail_account_id();
let state = conn.store.email_state.lock().await.clone();
let email_set_call = email::EmailSet::new(
Set::<email::EmailObject>::new(None)
Set::<email::EmailObject>::new(state)
.account_id(mail_account_id)
.update(Some(update_map)),
);
@ -894,8 +896,13 @@ impl MailBackend for JmapType {
Ok(s) => s,
};
store.online_status.update_timestamp(None).await;
let m = SetResponse::<email::EmailObject>::try_from(v.method_responses.remove(0))?;
if let Some(ids) = m.not_updated {
let SetResponse {
not_updated,
new_state,
..
} = SetResponse::<email::EmailObject>::try_from(v.method_responses.remove(0))?;
*conn.store.email_state.lock().await = Some(new_state);
if let Some(ids) = not_updated {
if !ids.is_empty() {
return Err(Error::new(format!(
"Could not update ids: {}",
@ -983,9 +990,10 @@ impl MailBackend for JmapType {
}
let conn = connection.lock().await;
let mail_account_id = conn.session_guard().await?.mail_account_id();
let state = conn.store.email_state.lock().await.clone();
let email_set_call = email::EmailSet::new(
Set::<email::EmailObject>::new(None)
Set::<email::EmailObject>::new(state)
.account_id(mail_account_id.clone())
.update(Some(update_map)),
);
@ -1015,8 +1023,13 @@ impl MailBackend for JmapType {
Ok(s) => s,
};
store.online_status.update_timestamp(None).await;
let m = SetResponse::<email::EmailObject>::try_from(v.method_responses.remove(0))?;
if let Some(ids) = m.not_updated {
let SetResponse {
not_updated,
new_state,
..
} = SetResponse::<email::EmailObject>::try_from(v.method_responses.remove(0))?;
*conn.store.email_state.lock().await = Some(new_state);
if let Some(ids) = not_updated {
return Err(Error::new(
ids.into_iter()
.map(|err| err.to_string())
@ -1035,31 +1048,7 @@ impl MailBackend for JmapType {
drop(tag_index_lck);
}
let e = GetResponse::<email::EmailObject>::try_from(v.method_responses.pop().unwrap())?;
let GetResponse::<email::EmailObject> { list, state, .. } = e;
{
let (is_empty, is_equal) = {
let mailboxes_lck = conn.store.mailboxes.read().unwrap();
mailboxes_lck
.get(&mailbox_hash)
.map(|mbox| {
let current_state_lck = mbox.email_state.lock().unwrap();
(
current_state_lck.is_some(),
current_state_lck.as_ref() != Some(&state),
)
})
.unwrap_or((true, true))
};
if is_empty {
let mut mailboxes_lck = conn.store.mailboxes.write().unwrap();
debug!("{:?}: inserting state {}", email::EmailObject::NAME, &state);
mailboxes_lck.entry(mailbox_hash).and_modify(|mbox| {
*mbox.email_state.lock().unwrap() = Some(state);
});
} else if !is_equal {
conn.email_changes(mailbox_hash).await?;
}
}
let GetResponse::<email::EmailObject> { list, .. } = e;
debug!(&list);
for envobj in list {
let env_hash = id_map[&envobj.id];
@ -1288,6 +1277,7 @@ impl JmapType {
mailboxes: Default::default(),
mailboxes_index: Default::default(),
mailbox_state: Default::default(),
email_state: Default::default(),
});
Ok(Box::new(Self {

@ -137,7 +137,7 @@ pub async fn get_mailboxes(
state,
..
} = m;
*conn.store.mailbox_state.lock().await = state;
*conn.store.mailbox_state.lock().await = Some(state);
conn.last_method_response = Some(res_text);
// Is account set as `personal`? (`isPersonal` property). Then, even if
// `isSubscribed` is false on a mailbox, it should be regarded as
@ -193,7 +193,6 @@ pub async fn get_mailboxes(
total_threads,
unread_emails: Arc::new(Mutex::new(unread_emails)),
unread_threads,
email_state: Arc::new(Mutex::new(None)),
email_query_state: Arc::new(Mutex::new(None)),
},
)
@ -268,24 +267,15 @@ impl EmailFetcher {
) -> Result<bool> {
{
let (is_empty, is_equal) = {
let mailboxes_lck = conn.store.mailboxes.read().unwrap();
mailboxes_lck
.get(&mailbox_hash)
.map(|mbox| {
let current_state_lck = mbox.email_state.lock().unwrap();
(
current_state_lck.is_none(),
current_state_lck.as_ref() == Some(&state),
)
})
.unwrap_or((true, true))
let current_state_lck = conn.store.email_state.lock().await;
(
current_state_lck.is_none(),
current_state_lck.as_ref() == Some(&state),
)
};
if is_empty {
let mut mailboxes_lck = conn.store.mailboxes.write().unwrap();
debug!("{:?}: inserting state {}", EmailObject::NAME, &state);
mailboxes_lck.entry(mailbox_hash).and_modify(|mbox| {
*mbox.email_state.lock().unwrap() = Some(state);
});
*conn.store.email_state.lock().await = Some(state);
} else if !is_equal {
conn.email_changes(mailbox_hash).await?;
}

Loading…
Cancel
Save