Aerogramme refactoring #57

Merged
quentin merged 16 commits from feat/more-imap-qol into main 2024-01-06 10:38:37 +00:00
4 changed files with 130 additions and 3 deletions
Showing only changes of commit adf4d33f22 - Show all commits

View file

@ -82,6 +82,10 @@ impl Mailbox {
self.mbox.read().await.fetch_full(id, message_key).await 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 ---- // ---- Functions for changing the mailbox ----
/// Add flags to message /// Add flags to message

View file

@ -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>,
}

View file

@ -1,11 +1,52 @@
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result;
use super::mailbox::Mailbox; use super::mailbox::Mailbox;
use super::uidindex::UidIndex; 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 mailbox: Arc<Mailbox>,
pub snapshot: UidIndex, 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
}
} }

View file

@ -9,6 +9,7 @@ use crate::mail::unique_ident::UniqueIdent;
pub type ImapUid = NonZeroU32; pub type ImapUid = NonZeroU32;
pub type ImapUidvalidity = NonZeroU32; pub type ImapUidvalidity = NonZeroU32;
pub type Flag = String; pub type Flag = String;
pub type IndexEntry = (ImapUid, Vec<Flag>);
/// A UidIndex handles the mutable part of a mailbox /// A UidIndex handles the mutable part of a mailbox
/// It is built by running the event log on it /// It is built by running the event log on it
@ -18,7 +19,7 @@ pub type Flag = String;
#[derive(Clone)] #[derive(Clone)]
pub struct UidIndex { pub struct UidIndex {
// Source of trust // Source of trust
pub table: OrdMap<UniqueIdent, (ImapUid, Vec<Flag>)>, pub table: OrdMap<UniqueIdent, IndexEntry>,
// Indexes optimized for queries // Indexes optimized for queries
pub idx_by_uid: OrdMap<ImapUid, UniqueIdent>, pub idx_by_uid: OrdMap<ImapUid, UniqueIdent>,