From ca7eb7928439e3f42031677f7e7cbb1b1fdb4013 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 29 Sep 2024 10:23:53 +0300 Subject: [PATCH] jmap: Implement deleting email Finally! Signed-off-by: Manos Pitsidianakis --- melib/src/backends.rs | 2 +- melib/src/imap/connection.rs | 7 ++- melib/src/jmap/connection.rs | 9 +++- melib/src/jmap/mod.rs | 85 +++++++++++++++++++++++++++++++++--- 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/melib/src/backends.rs b/melib/src/backends.rs index 1ca1071c..7f14e9af 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -282,10 +282,10 @@ pub enum BackendEvent { level: LogLevel, }, Refresh(RefreshEvent), + RefreshBatch(Vec), AccountStateChange { message: Cow<'static, str>, }, - //Job(Box> + Send + 'static>) } impl From for BackendEvent { diff --git a/melib/src/imap/connection.rs b/melib/src/imap/connection.rs index c698a29d..403d7056 100644 --- a/melib/src/imap/connection.rs +++ b/melib/src/imap/connection.rs @@ -1270,8 +1270,13 @@ impl ImapConnection { Ok(()) } + #[inline] pub fn add_refresh_event(&self, ev: RefreshEvent) { - (self.uid_store.event_consumer)(self.uid_store.account_hash, BackendEvent::Refresh(ev)); + self.add_backend_event(BackendEvent::Refresh(ev)); + } + + pub fn add_backend_event(&self, ev: BackendEvent) { + (self.uid_store.event_consumer)(self.uid_store.account_hash, ev); } async fn create_uid_msn_cache( diff --git a/melib/src/jmap/connection.rs b/melib/src/jmap/connection.rs index ce7ef8c7..7eecf5b1 100644 --- a/melib/src/jmap/connection.rs +++ b/melib/src/jmap/connection.rs @@ -434,8 +434,13 @@ impl JmapConnection { self.store.online_status.session_guard().await } - pub fn add_refresh_event(&self, event: RefreshEvent) { - (self.store.event_consumer)(self.store.account_hash, BackendEvent::Refresh(event)); + #[inline] + pub fn add_refresh_event(&self, ev: RefreshEvent) { + self.add_backend_event(BackendEvent::Refresh(ev)); + } + + pub fn add_backend_event(&self, ev: BackendEvent) { + (self.store.event_consumer)(self.store.account_hash, ev); } pub async fn email_changes(&self, mailbox_hash: MailboxHash) -> Result<()> { diff --git a/melib/src/jmap/mod.rs b/melib/src/jmap/mod.rs index b13ee002..d8fbe46b 100644 --- a/melib/src/jmap/mod.rs +++ b/melib/src/jmap/mod.rs @@ -1105,13 +1105,86 @@ impl MailBackend for JmapType { fn delete_messages( &mut self, - _env_hashes: EnvelopeHashBatch, - _mailbox_hash: MailboxHash, + env_hashes: EnvelopeHashBatch, + mailbox_hash: MailboxHash, ) -> ResultFuture<()> { - Err( - Error::new("Deleting messages is currently unimplemented for the JMAP backend.") - .set_kind(ErrorKind::NotImplemented), - ) + { + let mailboxes_lck = self.store.mailboxes.read().unwrap(); + if !mailboxes_lck.contains_key(&mailbox_hash) { + return Err(Error::new(format!( + "Could not find source mailbox with hash {}", + mailbox_hash + ))); + }; + } + let store = self.store.clone(); + let connection = self.connection.clone(); + Ok(Box::pin(async move { + let mut destroy_vec: Vec>> = + Vec::with_capacity(env_hashes.len()); + for env_hash in env_hashes.iter() { + if let Some(id) = store.id_store.lock().await.get(&env_hash) { + destroy_vec.push(Argument::Value(id.clone())); + } + } + let batch_len = destroy_vec.len(); + 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::::new(state) + .account_id(mail_account_id) + .destroy(Some(destroy_vec)), + ); + + let mut req = Request::new(conn.request_no.clone()); + let _prev_seq = req.add_call(&email_set_call).await; + + let res_text = conn + .post_async(None, serde_json::to_string(&req)?) + .await? + .text() + .await?; + + let mut v: MethodResponse = match deserialize_from_str(&res_text) { + Err(err) => { + _ = conn.store.online_status.set(None, Err(err.clone())).await; + return Err(err); + } + Ok(s) => s, + }; + store.online_status.update_timestamp(None).await; + let SetResponse { + not_destroyed, + new_state, + .. + } = SetResponse::::try_from(v.method_responses.remove(0))?; + *conn.store.email_state.lock().await = Some(new_state); + conn.add_backend_event(BackendEvent::RefreshBatch( + env_hashes + .iter() + .map(|h| RefreshEvent { + account_hash: store.account_hash, + mailbox_hash, + kind: RefreshEventKind::Remove(h), + }) + .collect(), + )); + if let Some(ids) = not_destroyed { + if !ids.is_empty() { + return Err(Error::new(format!( + "Could not destroy {}ids: {}", + if ids.len() == batch_len { "" } else { "some " }, + ids.iter() + .map(|err| err.to_string()) + .collect::>() + .join(",") + ))); + } + } + Ok(()) + })) } // [ref:TODO] add support for BLOB extension