use super::mailbox::MailMeta; use super::snapshot::FrozenMailbox; use super::unique_ident::UniqueIdent; use anyhow::Result; use futures::future::FutureExt; use futures::stream::{BoxStream, Stream, 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, } #[derive(Debug)] pub enum QueryScope { Index, Partial, Full, } impl QueryScope { pub fn union(&self, other: &QueryScope) -> QueryScope { match (self, other) { (QueryScope::Full, _) | (_, QueryScope::Full) => QueryScope::Full, (QueryScope::Partial, _) | (_, QueryScope::Partial) => QueryScope::Partial, (QueryScope::Index, QueryScope::Index) => QueryScope::Index, } } } //type QueryResultStream = Box>>; impl<'a, 'b> Query<'a, 'b> { pub fn fetch(&self) -> BoxStream> { match self.scope { QueryScope::Index => Box::pin( futures::stream::iter(self.emails) .map(|&uuid| Ok(QueryResult::IndexResult { uuid })), ), QueryScope::Partial => Box::pin(self.partial()), QueryScope::Full => Box::pin(self.full()), } } // --- functions below are private *for reasons* fn partial<'d>(&'d self) -> impl Stream> + 'd + Send { async move { let maybe_meta_list: Result> = self.frozen.mailbox.fetch_meta(self.emails).await; let list_res = maybe_meta_list .map(|meta_list| { meta_list .into_iter() .zip(self.emails) .map(|(metadata, &uuid)| Ok(QueryResult::PartialResult { uuid, metadata })) .collect() }) .unwrap_or_else(|e| vec![Err(e)]); futures::stream::iter(list_res) } .flatten_stream() } fn full<'d>(&'d self) -> impl Stream> + 'd + Send { self.partial().then(move |maybe_meta| async move { let meta = maybe_meta?; 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")) }) } } #[derive(Debug, Clone)] pub enum QueryResult { IndexResult { uuid: UniqueIdent, }, PartialResult { uuid: UniqueIdent, metadata: MailMeta, }, FullResult { uuid: UniqueIdent, metadata: MailMeta, content: Vec, }, } impl QueryResult { pub fn uuid(&self) -> &UniqueIdent { match self { Self::IndexResult { uuid, .. } => uuid, Self::PartialResult { uuid, .. } => uuid, Self::FullResult { uuid, .. } => uuid, } } pub fn metadata(&self) -> Option<&MailMeta> { match self { Self::IndexResult { .. } => None, Self::PartialResult { metadata, .. } => Some(metadata), Self::FullResult { metadata, .. } => Some(metadata), } } #[allow(dead_code)] pub fn content(&self) -> Option<&[u8]> { match self { Self::FullResult { content, .. } => Some(content), _ => None, } } fn into_full(self, content: Vec) -> Option { match self { Self::PartialResult { uuid, metadata } => Some(Self::FullResult { uuid, metadata, content, }), _ => None, } } }