aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-01-05 17:46:16 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-01-05 17:46:16 +0100
commitadf4d33f226a745330a3bb802fe9b96f263a0895 (patch)
tree28ba9871f00752998437c511f093b60c1b310bab
parent335750a29a83edba9bce2fb7e1452001e4962d1f (diff)
downloadaerogramme-adf4d33f226a745330a3bb802fe9b96f263a0895.tar.gz
aerogramme-adf4d33f226a745330a3bb802fe9b96f263a0895.zip
added some utility structures
-rw-r--r--src/mail/mailbox.rs4
-rw-r--r--src/mail/query.rs81
-rw-r--r--src/mail/snapshot.rs45
-rw-r--r--src/mail/uidindex.rs3
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>,