Aerogramme refactoring #57
4 changed files with 130 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
Loading…
Reference in a new issue