diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-01-05 17:46:16 +0100 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-01-05 17:46:16 +0100 |
commit | adf4d33f226a745330a3bb802fe9b96f263a0895 (patch) | |
tree | 28ba9871f00752998437c511f093b60c1b310bab | |
parent | 335750a29a83edba9bce2fb7e1452001e4962d1f (diff) | |
download | aerogramme-adf4d33f226a745330a3bb802fe9b96f263a0895.tar.gz aerogramme-adf4d33f226a745330a3bb802fe9b96f263a0895.zip |
added some utility structures
-rw-r--r-- | src/mail/mailbox.rs | 4 | ||||
-rw-r--r-- | src/mail/query.rs | 81 | ||||
-rw-r--r-- | src/mail/snapshot.rs | 45 | ||||
-rw-r--r-- | src/mail/uidindex.rs | 3 |
4 files changed, 130 insertions, 3 deletions
diff --git a/src/mail/mailbox.rs b/src/mail/mailbox.rs index b011110..306fd7d 100644 --- a/src/mail/mailbox.rs +++ b/src/mail/mailbox.rs @@ -82,6 +82,10 @@ impl Mailbox { self.mbox.read().await.fetch_full(id, message_key).await } + async fn frozen(self: &std::sync::Arc<Self>) -> super::snapshot::FrozenMailbox { + super::snapshot::FrozenMailbox::new(self.clone()).await + } + // ---- Functions for changing the mailbox ---- /// Add flags to message diff --git a/src/mail/query.rs b/src/mail/query.rs index e69de29..631ad56 100644 --- a/src/mail/query.rs +++ b/src/mail/query.rs @@ -0,0 +1,81 @@ +use anyhow::{Result, anyhow}; +use super::mailbox::MailMeta; +use super::snapshot::FrozenMailbox; +use super::unique_ident::UniqueIdent; +use super::uidindex::IndexEntry; +use futures::stream::{FuturesUnordered, StreamExt}; + +/// Query is in charge of fetching efficiently +/// requested data for a list of emails +pub struct Query<'a,'b> { + pub frozen: &'a FrozenMailbox, + pub emails: &'b [UniqueIdent], +} + +impl<'a,'b> Query<'a,'b> { + pub fn index(&self) -> Result<Vec<IndexResult>> { + self + .emails + .iter() + .map(|uuid| { + self + .frozen + .snapshot + .table + .get(uuid) + .map(|index| IndexResult { uuid: *uuid, index }) + .ok_or(anyhow!("missing email in index")) + }) + .collect::<Result<Vec<_>, _>>() + } + + pub async fn partial(&self) -> Result<Vec<PartialResult>> { + let meta = self.frozen.mailbox.fetch_meta(self.emails).await?; + let result = meta + .into_iter() + .zip(self.index()?) + .map(|(metadata, index)| PartialResult { uuid: index.uuid, index: index.index, metadata }) + .collect::<Vec<_>>(); + Ok(result) + } + + /// @FIXME WARNING: THIS CAN ALLOCATE A LOT OF MEMORY + /// AND GENERATE SO MUCH NETWORK TRAFFIC. + /// THIS FUNCTION SHOULD BE REWRITTEN, FOR EXAMPLE WITH + /// SOMETHING LIKE AN ITERATOR + pub async fn full(&self) -> Result<Vec<FullResult>> { + let meta_list = self.partial().await?; + meta_list + .into_iter() + .map(|meta| async move { + let content = self.frozen.mailbox.fetch_full(meta.uuid, &meta.metadata.message_key).await?; + Ok(FullResult { + uuid: meta.uuid, + index: meta.index, + metadata: meta.metadata, + content, + }) + }) + .collect::<FuturesUnordered<_>>() + .collect::<Vec<_>>() + .await + .into_iter() + .collect::<Result<Vec<_>, _>>() + } +} + +pub struct IndexResult<'a> { + pub uuid: UniqueIdent, + pub index: &'a IndexEntry, +} +pub struct PartialResult<'a> { + pub uuid: UniqueIdent, + pub index: &'a IndexEntry, + pub metadata: MailMeta, +} +pub struct FullResult<'a> { + pub uuid: UniqueIdent, + pub index: &'a IndexEntry, + pub metadata: MailMeta, + pub content: Vec<u8>, +} diff --git a/src/mail/snapshot.rs b/src/mail/snapshot.rs index 7256d50..54bec64 100644 --- a/src/mail/snapshot.rs +++ b/src/mail/snapshot.rs @@ -1,11 +1,52 @@ use std::sync::Arc; + +use anyhow::Result; + use super::mailbox::Mailbox; use super::uidindex::UidIndex; -pub struct Snapshot { +/// A Frozen Mailbox has a snapshot of the current mailbox +/// state that is desynchronized with the real mailbox state. +/// It's up to the user to choose when their snapshot must be updated +/// to give useful information to their clients +/// +/// +pub struct FrozenMailbox { pub mailbox: Arc<Mailbox>, pub snapshot: UidIndex, } -impl Snapshot { +impl FrozenMailbox { + /// Create a snapshot from a mailbox, the mailbox + the snapshot + /// becomes the "Frozen Mailbox". + pub async fn new(mailbox: Arc<Mailbox>) -> Self { + let state = mailbox.current_uid_index().await; + + Self { + mailbox, + snapshot: state, + } + } + + /// Force the synchronization of the inner mailbox + /// but do not update the local snapshot + pub async fn sync(&self) -> Result<()> { + self.mailbox.opportunistic_sync().await + } + + /// Peek snapshot without updating the frozen mailbox + /// Can be useful if you want to plan some writes + /// while sending a diff to the client later + pub async fn peek(&self) -> UidIndex { + self.mailbox.current_uid_index().await + } + + /// Update the FrozenMailbox local snapshot. + /// Returns the old snapshot, so you can build a diff + pub async fn update(&mut self) -> UidIndex { + let old_snapshot = self.snapshot.clone(); + self.snapshot = self.mailbox.current_uid_index().await; + + old_snapshot + } } diff --git a/src/mail/uidindex.rs b/src/mail/uidindex.rs index 956b194..01f8c9c 100644 --- a/src/mail/uidindex.rs +++ b/src/mail/uidindex.rs @@ -9,6 +9,7 @@ use crate::mail::unique_ident::UniqueIdent; pub type ImapUid = NonZeroU32; pub type ImapUidvalidity = NonZeroU32; pub type Flag = String; +pub type IndexEntry = (ImapUid, Vec<Flag>); /// A UidIndex handles the mutable part of a mailbox /// It is built by running the event log on it @@ -18,7 +19,7 @@ pub type Flag = String; #[derive(Clone)] pub struct UidIndex { // Source of trust - pub table: OrdMap<UniqueIdent, (ImapUid, Vec<Flag>)>, + pub table: OrdMap<UniqueIdent, IndexEntry>, // Indexes optimized for queries pub idx_by_uid: OrdMap<ImapUid, UniqueIdent>, |