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
|
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
|
||||||
|
|
|
@ -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 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
Loading…
Reference in a new issue