From a1ca6d9defc844fee52d966951701a57727050c7 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 13 Jul 2022 11:00:35 +0200 Subject: [PATCH] "set flags" as a bayou op --- src/imap/command/selected.rs | 7 ++++++- src/imap/mailbox_view.rs | 28 +++++----------------------- src/mail/mailbox.rs | 10 ++++++++++ src/mail/uidindex.rs | 24 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index a6fa645..1978729 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -62,7 +62,12 @@ impl<'a> SelectedContext<'a> { } async fn expunge(self) -> Result<(Response, flow::Transition)> { - Ok((Response::bad("Not implemented")?, flow::Transition::None)) + let data = self.mailbox.expunge().await?; + + Ok(( + Response::ok("EXPUNGE completed")?.with_body(data), + flow::Transition::None, + )) } async fn store( diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index 93d3b4d..f293bfa 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -169,29 +169,7 @@ impl MailboxView { self.mailbox.del_flags(*uuid, &flags[..]).await?; } StoreType::Replace => { - let old_flags = &self - .known_state - .table - .get(uuid) - .ok_or(anyhow!( - "Missing message: {} (UID {}, UUID {})", - i, - uid, - uuid - ))? - .1; - let to_remove = old_flags - .iter() - .filter(|x| !flags.contains(&x)) - .cloned() - .collect::>(); - let to_add = flags - .iter() - .filter(|x| !old_flags.contains(&x)) - .cloned() - .collect::>(); - self.mailbox.add_flags(*uuid, &to_add[..]).await?; - self.mailbox.del_flags(*uuid, &to_remove[..]).await?; + self.mailbox.set_flags(*uuid, &flags[..]).await?; } } } @@ -199,6 +177,10 @@ impl MailboxView { self.update().await } + pub async fn expunge(&mut self) -> Result> { + unimplemented!() + } + /// Looks up state changes in the mailbox and produces a set of IMAP /// responses describing the new state. pub async fn fetch( diff --git a/src/mail/mailbox.rs b/src/mail/mailbox.rs index 0e8af70..4b84cf2 100644 --- a/src/mail/mailbox.rs +++ b/src/mail/mailbox.rs @@ -95,6 +95,11 @@ impl Mailbox { self.mbox.write().await.del_flags(id, flags).await } + /// Define the new flags for this message + pub async fn set_flags<'a>(&self, id: UniqueIdent, flags: &[Flag]) -> Result<()> { + self.mbox.write().await.set_flags(id, flags).await + } + /// Insert an email into the mailbox pub async fn append<'a>( &self, @@ -265,6 +270,11 @@ impl MailboxInternal { self.uid_index.push(del_flag_op).await } + async fn set_flags(&mut self, ident: UniqueIdent, flags: &[Flag]) -> Result<()> { + let set_flag_op = self.uid_index.state().op_flag_set(ident, flags.to_vec()); + self.uid_index.push(set_flag_op).await + } + async fn append( &mut self, mail: IMF<'_>, diff --git a/src/mail/uidindex.rs b/src/mail/uidindex.rs index 6da08c1..3a3e252 100644 --- a/src/mail/uidindex.rs +++ b/src/mail/uidindex.rs @@ -36,6 +36,7 @@ pub enum UidIndexOp { MailDel(UniqueIdent), FlagAdd(UniqueIdent, Vec), FlagDel(UniqueIdent, Vec), + FlagSet(UniqueIdent, Vec), BumpUidvalidity(u32), } @@ -60,6 +61,11 @@ impl UidIndex { UidIndexOp::FlagDel(ident, flags) } + #[must_use] + pub fn op_flag_set(&self, ident: UniqueIdent, flags: Vec) -> UidIndexOp { + UidIndexOp::FlagSet(ident, flags) + } + #[must_use] pub fn op_bump_uidvalidity(&self, count: u32) -> UidIndexOp { UidIndexOp::BumpUidvalidity(count) @@ -162,6 +168,24 @@ impl BayouState for UidIndex { new.idx_by_flag.remove(*uid, rm_flags); } } + UidIndexOp::FlagSet(ident, new_flags) => { + if let Some((uid, existing_flags)) = new.table.get_mut(ident) { + // Remove flags from the source of trust and the cache + let (keep_flags, rm_flags): (Vec, Vec) = existing_flags + .iter() + .cloned() + .partition(|x| new_flags.contains(x)); + *existing_flags = keep_flags; + let mut to_add: Vec = new_flags + .iter() + .filter(|f| !existing_flags.contains(f)) + .cloned() + .collect(); + existing_flags.append(&mut to_add); + new.idx_by_flag.remove(*uid, &rm_flags); + new.idx_by_flag.insert(*uid, &to_add); + } + } UidIndexOp::BumpUidvalidity(count) => { new.uidvalidity = ImapUidvalidity::new(new.uidvalidity.get() + *count) .unwrap_or(ImapUidvalidity::new(u32::MAX).unwrap());