aboutsummaryrefslogtreecommitdiff
path: root/src/mail/query.rs
diff options
context:
space:
mode:
authorQuentin <quentin@dufour.io>2024-01-06 10:38:37 +0000
committerQuentin <quentin@dufour.io>2024-01-06 10:38:37 +0000
commit44ca458c5cf666246e472dea9be70b745a130e8c (patch)
treeb78cca747e5c2bc004cb93b93536623f7abb6ef5 /src/mail/query.rs
parentbcf6de83419b405fea95b740869f98d43586ea7c (diff)
parent53dbf82cbce3cb17cbcffd09558677faf8702f54 (diff)
downloadaerogramme-44ca458c5cf666246e472dea9be70b745a130e8c.tar.gz
aerogramme-44ca458c5cf666246e472dea9be70b745a130e8c.zip
Merge pull request 'Aerogramme refactoring' (#57) from feat/more-imap-qol into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/aerogramme/pulls/57
Diffstat (limited to 'src/mail/query.rs')
-rw-r--r--src/mail/query.rs170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/mail/query.rs b/src/mail/query.rs
new file mode 100644
index 0000000..8de73e6
--- /dev/null
+++ b/src/mail/query.rs
@@ -0,0 +1,170 @@
+use super::mailbox::MailMeta;
+use super::snapshot::FrozenMailbox;
+use super::uidindex::IndexEntry;
+use super::unique_ident::UniqueIdent;
+use anyhow::{anyhow, Result};
+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],
+ pub scope: QueryScope,
+}
+
+#[allow(dead_code)]
+pub enum QueryScope {
+ Index,
+ Partial,
+ Full,
+}
+
+impl<'a, 'b> Query<'a, 'b> {
+ pub async fn fetch(&self) -> Result<Vec<QueryResult<'a>>> {
+ match self.scope {
+ QueryScope::Index => self.index(),
+ QueryScope::Partial => self.partial().await,
+ QueryScope::Full => self.full().await,
+ }
+ }
+
+ // --- functions below are private *for reasons*
+
+ fn index(&self) -> Result<Vec<QueryResult<'a>>> {
+ self.emails
+ .iter()
+ .map(|uuid| {
+ self.frozen
+ .snapshot
+ .table
+ .get(uuid)
+ .map(|index| QueryResult::IndexResult { uuid: *uuid, index })
+ .ok_or(anyhow!("missing email in index"))
+ })
+ .collect::<Result<Vec<_>, _>>()
+ }
+
+ async fn partial(&self) -> Result<Vec<QueryResult<'a>>> {
+ let meta = self.frozen.mailbox.fetch_meta(self.emails).await?;
+ let result = meta
+ .into_iter()
+ .zip(self.index()?)
+ .map(|(metadata, index)| {
+ index
+ .into_partial(metadata)
+ .expect("index to be IndexResult")
+ })
+ .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
+ async fn full(&self) -> Result<Vec<QueryResult<'a>>> {
+ 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()
+ .expect("meta to be PartialResult")
+ .message_key,
+ )
+ .await?;
+
+ Ok(meta.into_full(content).expect("meta to be PartialResult"))
+ })
+ .collect::<FuturesUnordered<_>>()
+ .collect::<Vec<_>>()
+ .await
+ .into_iter()
+ .collect::<Result<Vec<_>, _>>()
+ }
+}
+
+pub enum QueryResult<'a> {
+ IndexResult {
+ uuid: UniqueIdent,
+ index: &'a IndexEntry,
+ },
+ PartialResult {
+ uuid: UniqueIdent,
+ index: &'a IndexEntry,
+ metadata: MailMeta,
+ },
+ FullResult {
+ uuid: UniqueIdent,
+ index: &'a IndexEntry,
+ metadata: MailMeta,
+ content: Vec<u8>,
+ },
+}
+impl<'a> QueryResult<'a> {
+ pub fn uuid(&self) -> &UniqueIdent {
+ match self {
+ Self::IndexResult { uuid, .. } => uuid,
+ Self::PartialResult { uuid, .. } => uuid,
+ Self::FullResult { uuid, .. } => uuid,
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn index(&self) -> &IndexEntry {
+ match self {
+ Self::IndexResult { index, .. } => index,
+ Self::PartialResult { index, .. } => index,
+ Self::FullResult { index, .. } => index,
+ }
+ }
+
+ pub fn metadata(&'a self) -> Option<&'a MailMeta> {
+ match self {
+ Self::IndexResult { .. } => None,
+ Self::PartialResult { metadata, .. } => Some(metadata),
+ Self::FullResult { metadata, .. } => Some(metadata),
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn content(&'a self) -> Option<&'a [u8]> {
+ match self {
+ Self::FullResult { content, .. } => Some(content),
+ _ => None,
+ }
+ }
+
+ fn into_partial(self, metadata: MailMeta) -> Option<Self> {
+ match self {
+ Self::IndexResult { uuid, index } => Some(Self::PartialResult {
+ uuid,
+ index,
+ metadata,
+ }),
+ _ => None,
+ }
+ }
+
+ fn into_full(self, content: Vec<u8>) -> Option<Self> {
+ match self {
+ Self::PartialResult {
+ uuid,
+ index,
+ metadata,
+ } => Some(Self::FullResult {
+ uuid,
+ index,
+ metadata,
+ content,
+ }),
+ _ => None,
+ }
+ }
+}