Aerogramme refactoring #57

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

80
src/imap/index.rs Normal file
View file

@ -0,0 +1,80 @@
use std::num::NonZeroU32;
use anyhow::{anyhow, bail, Result};
use imap_codec::imap_types::sequence::{self, SequenceSet};
use crate::mail::uidindex::{ImapUid, UidIndex};
use crate::mail::unique_ident::UniqueIdent;
pub struct Index<'a>(pub &'a UidIndex);
impl<'a> Index<'a> {
pub fn fetch(self: &Index<'a>, sequence_set: &SequenceSet, by_uid: bool) -> Result<Vec<MailIndex<'a>>> {
let mail_vec = self
.0
.idx_by_uid
.iter()
.map(|(uid, uuid)| (*uid, *uuid))
.collect::<Vec<_>>();
let mut mails = vec![];
if by_uid {
if mail_vec.is_empty() {
return Ok(vec![]);
}
let iter_strat = sequence::Strategy::Naive {
largest: mail_vec.last().unwrap().0,
};
let mut i = 0;
for uid in sequence_set.iter(iter_strat) {
while mail_vec.get(i).map(|mail| mail.0 < uid).unwrap_or(false) {
i += 1;
}
if let Some(mail) = mail_vec.get(i) {
if mail.0 == uid {
mails.push(MailIndex {
i: NonZeroU32::try_from(i as u32 + 1).unwrap(),
uid: mail.0,
uuid: mail.1,
flags: self.0.table.get(&mail.1).ok_or(anyhow!("mail is missing from index"))?.1.as_ref(),
});
}
} else {
break;
}
}
} else {
if mail_vec.is_empty() {
bail!("No such message (mailbox is empty)");
}
let iter_strat = sequence::Strategy::Naive {
largest: NonZeroU32::try_from((mail_vec.len()) as u32).unwrap(),
};
for i in sequence_set.iter(iter_strat) {
if let Some(mail) = mail_vec.get(i.get() as usize - 1) {
mails.push(MailIndex {
i,
uid: mail.0,
uuid: mail.1,
flags: self.0.table.get(&mail.1).ok_or(anyhow!("mail is missing from index"))?.1.as_ref(),
});
} else {
bail!("No such mail: {}", i);
}
}
}
Ok(mails)
}
}
pub struct MailIndex<'a> {
pub i: NonZeroU32,
pub uid: ImapUid,
pub uuid: UniqueIdent,
pub flags: &'a Vec<String>
}

View file

@ -1,9 +1,9 @@
use std::num::NonZeroU32; use std::num::NonZeroU32;
use anyhow::{anyhow, bail, Result, Context}; use anyhow::{anyhow, bail, Result};
use chrono::{Offset, TimeZone, Utc}; use chrono::{Offset, TimeZone, Utc};
use imap_codec::imap_types::core::{IString, NString}; use imap_codec::imap_types::core::NString;
use imap_codec::imap_types::datetime::DateTime; use imap_codec::imap_types::datetime::DateTime;
use imap_codec::imap_types::fetch::{ use imap_codec::imap_types::fetch::{
MessageDataItem, MessageDataItemName, Section as FetchSection, MessageDataItem, MessageDataItemName, Section as FetchSection,
@ -16,87 +16,73 @@ use eml_codec::{
part::{composite::Message, AnyPart}, part::{composite::Message, AnyPart},
}; };
use crate::mail::query::QueryResult;
use crate::imap::attributes::AttributesProxy; use crate::imap::attributes::AttributesProxy;
use crate::imap::flags; use crate::imap::flags;
use crate::imap::imf_view::message_envelope; use crate::imap::imf_view::message_envelope;
use crate::imap::mailbox_view::MailIdentifiers;
use crate::imap::mime_view; use crate::imap::mime_view;
use crate::imap::response::Body; use crate::imap::response::Body;
use crate::mail::query::QueryResult; use crate::imap::index::MailIndex;
pub struct MailView<'a> { pub struct MailView<'a> {
pub in_idx: MailIndex<'a>,
pub query_result: &'a QueryResult<'a>, pub query_result: &'a QueryResult<'a>,
pub content: FetchedMail<'a>, pub content: FetchedMail<'a>,
} }
impl<'a> MailView<'a> { impl<'a> MailView<'a> {
pub fn new(query_result: &'a QueryResult<'a>) -> Result<Self> { pub fn new(query_result: &'a QueryResult<'a>, in_idx: MailIndex<'a>) -> Result<MailView<'a>> {
Ok(Self { Ok(Self {
in_idx,
query_result, query_result,
content: match query_result { content: match query_result {
QueryResult::FullResult { content, .. } => { QueryResult::FullResult { content, .. } => {
let (_, parsed) = eml_codec::parse_message(content).context("Invalid mail body")?; let (_, parsed) = eml_codec::parse_message(&content).or(Err(anyhow!("Invalid mail body")))?;
FetchedMail::new_from_message(parsed) FetchedMail::new_from_message(parsed)
}, },
QueryResult::PartialResult { metadata, .. } => { QueryResult::PartialResult { metadata, .. } => {
let (_, parsed) = eml_codec::parse_imf(&metadata.headers).context("Invalid mail headers")?; let (_, parsed) = eml_codec::parse_imf(&metadata.headers).or(Err(anyhow!("unable to parse email headers")))?;
FetchedMail::Partial(parsed) FetchedMail::Partial(parsed)
} }
QueryResult::IndexResult { .. } => FetchedMail::None, QueryResult::IndexResult { .. } => FetchedMail::IndexOnly,
} }
}) })
} }
fn uid(&self) -> MessageDataItem<'static> { fn uid(&self) -> MessageDataItem<'static> {
MessageDataItem::Uid(self.ids.uid.clone()) MessageDataItem::Uid(self.in_idx.uid.clone())
} }
fn flags(&self) -> MessageDataItem<'static> { fn flags(&self) -> MessageDataItem<'static> {
MessageDataItem::Flags( MessageDataItem::Flags(
self.flags self.in_idx
.flags
.iter() .iter()
.filter_map(|f| flags::from_str(f)) .filter_map(|f| flags::from_str(f))
.collect(), .collect(),
) )
} }
fn rfc_822_size(&self) -> MessageDataItem<'static> { fn rfc_822_size(&self) -> Result<MessageDataItem<'static>> {
MessageDataItem::Rfc822Size(self.meta.rfc822_size as u32) let sz = self.query_result.metadata().ok_or(anyhow!("mail metadata are required"))?.rfc822_size;
Ok(MessageDataItem::Rfc822Size(sz as u32))
} }
fn rfc_822_header(&self) -> MessageDataItem<'static> { fn rfc_822_header(&self) -> Result<MessageDataItem<'static>> {
MessageDataItem::Rfc822Header(NString( let hdrs: NString = self.query_result.metadata().ok_or(anyhow!("mail metadata are required"))?.headers.to_vec().try_into()?;
self.meta Ok(MessageDataItem::Rfc822Header(hdrs))
.headers
.to_vec()
.try_into()
.ok()
.map(IString::Literal),
))
} }
fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> { fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> {
Ok(MessageDataItem::Rfc822Text(NString( let txt: NString = self.content.as_full()?.raw_body.to_vec().try_into()?;
self.content Ok(MessageDataItem::Rfc822Text(txt))
.as_full()?
.raw_body
.to_vec()
.try_into()
.ok()
.map(IString::Literal),
)))
} }
fn rfc822(&self) -> Result<MessageDataItem<'static>> { fn rfc822(&self) -> Result<MessageDataItem<'static>> {
Ok(MessageDataItem::Rfc822(NString( let full: NString = self.content.as_full()?.raw_part.to_vec().try_into()?;
self.content Ok(MessageDataItem::Rfc822(full))
.as_full()?
.raw_part
.to_vec()
.try_into()
.ok()
.map(IString::Literal),
)))
} }
fn envelope(&self) -> MessageDataItem<'static> { fn envelope(&self) -> MessageDataItem<'static> {
@ -119,16 +105,16 @@ impl<'a> MailView<'a> {
/// peek does not implicitly set the \Seen flag /// peek does not implicitly set the \Seen flag
/// eg. BODY[HEADER.FIELDS (DATE FROM)] /// eg. BODY[HEADER.FIELDS (DATE FROM)]
/// eg. BODY[]<0.2048> /// eg. BODY[]<0.2048>
fn body_ext<'b>( fn body_ext(
&self, &self,
section: &Option<FetchSection<'b>>, section: &Option<FetchSection<'static>>,
partial: &Option<(u32, NonZeroU32)>, partial: &Option<(u32, NonZeroU32)>,
peek: &bool, peek: &bool,
) -> Result<(MessageDataItem<'b>, SeenFlag)> { ) -> Result<(MessageDataItem<'static>, SeenFlag)> {
// Manage Seen flag // Manage Seen flag
let mut seen = SeenFlag::DoNothing; let mut seen = SeenFlag::DoNothing;
let seen_flag = Flag::Seen.to_string(); let seen_flag = Flag::Seen.to_string();
if !peek && !self.flags.iter().any(|x| *x == seen_flag) { if !peek && !self.in_idx.flags.iter().any(|x| *x == seen_flag) {
// Add \Seen flag // Add \Seen flag
//self.mailbox.add_flags(uuid, &[seen_flag]).await?; //self.mailbox.add_flags(uuid, &[seen_flag]).await?;
seen = SeenFlag::MustAdd; seen = SeenFlag::MustAdd;
@ -141,7 +127,7 @@ impl<'a> MailView<'a> {
mime_view::BodySection::Slice { body, origin_octet } => (body, Some(origin_octet)), mime_view::BodySection::Slice { body, origin_octet } => (body, Some(origin_octet)),
}; };
let data = NString(text.to_vec().try_into().ok().map(IString::Literal)); let data: NString = text.to_vec().try_into()?;
return Ok(( return Ok((
MessageDataItem::BodyExt { MessageDataItem::BodyExt {
@ -156,13 +142,13 @@ impl<'a> MailView<'a> {
fn internal_date(&self) -> Result<MessageDataItem<'static>> { fn internal_date(&self) -> Result<MessageDataItem<'static>> {
let dt = Utc let dt = Utc
.fix() .fix()
.timestamp_opt(i64::try_from(self.meta.internaldate / 1000)?, 0) .timestamp_opt(i64::try_from(self.query_result.metadata().ok_or(anyhow!("mail metadata were not fetched"))?.internaldate / 1000)?, 0)
.earliest() .earliest()
.ok_or(anyhow!("Unable to parse internal date"))?; .ok_or(anyhow!("Unable to parse internal date"))?;
Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt))) Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt)))
} }
pub fn filter<'b>(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> { pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> {
let mut seen = SeenFlag::DoNothing; let mut seen = SeenFlag::DoNothing;
let res_attrs = ap let res_attrs = ap
.attrs .attrs
@ -170,8 +156,8 @@ impl<'a> MailView<'a> {
.map(|attr| match attr { .map(|attr| match attr {
MessageDataItemName::Uid => Ok(self.uid()), MessageDataItemName::Uid => Ok(self.uid()),
MessageDataItemName::Flags => Ok(self.flags()), MessageDataItemName::Flags => Ok(self.flags()),
MessageDataItemName::Rfc822Size => Ok(self.rfc_822_size()), MessageDataItemName::Rfc822Size => self.rfc_822_size(),
MessageDataItemName::Rfc822Header => Ok(self.rfc_822_header()), MessageDataItemName::Rfc822Header => self.rfc_822_header(),
MessageDataItemName::Rfc822Text => self.rfc_822_text(), MessageDataItemName::Rfc822Text => self.rfc_822_text(),
MessageDataItemName::Rfc822 => self.rfc822(), MessageDataItemName::Rfc822 => self.rfc822(),
MessageDataItemName::Envelope => Ok(self.envelope()), MessageDataItemName::Envelope => Ok(self.envelope()),
@ -192,7 +178,7 @@ impl<'a> MailView<'a> {
Ok(( Ok((
Body::Data(Data::Fetch { Body::Data(Data::Fetch {
seq: self.ids.i, seq: self.in_idx.i,
items: res_attrs.try_into()?, items: res_attrs.try_into()?,
}), }),
seen, seen,
@ -208,19 +194,15 @@ pub enum SeenFlag {
// ------------------- // -------------------
pub enum FetchedMail<'a> { pub enum FetchedMail<'a> {
None, IndexOnly,
Partial(imf::Imf<'a>), Partial(imf::Imf<'a>),
Full(AnyPart<'a>), Full(AnyPart<'a>),
} }
impl<'a> FetchedMail<'a> { impl<'a> FetchedMail<'a> {
pub fn new_from_message(msg: Message<'a>) -> Self { pub fn new_from_message(msg: Message<'a>) -> Self {
FetchedMail::Full(AnyPart::Msg(msg)) Self::Full(AnyPart::Msg(msg))
} }
/*fn new_from_header(hdr: imf::Imf<'a>) -> Self {
FetchedMail::Partial(hdr)
}*/
fn as_anypart(&self) -> Result<&AnyPart<'a>> { fn as_anypart(&self) -> Result<&AnyPart<'a>> {
match self { match self {
FetchedMail::Full(x) => Ok(&x), FetchedMail::Full(x) => Ok(&x),

View file

@ -1,7 +1,7 @@
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{anyhow, bail, Error, Result}; use anyhow::{anyhow, Error, Result};
use futures::stream::{FuturesOrdered, StreamExt}; use futures::stream::{FuturesOrdered, StreamExt};
@ -10,19 +10,19 @@ use imap_codec::imap_types::fetch::{MacroOrMessageDataItemNames, MessageDataItem
use imap_codec::imap_types::flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType}; use imap_codec::imap_types::flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType};
use imap_codec::imap_types::response::{Code, Data, Status}; use imap_codec::imap_types::response::{Code, Data, Status};
use imap_codec::imap_types::search::SearchKey; use imap_codec::imap_types::search::SearchKey;
use imap_codec::imap_types::sequence::{self, SequenceSet}; use imap_codec::imap_types::sequence::SequenceSet;
use crate::mail::mailbox::Mailbox; use crate::mail::mailbox::Mailbox;
use crate::mail::snapshot::FrozenMailbox; use crate::mail::snapshot::FrozenMailbox;
use crate::mail::query::QueryScope; use crate::mail::query::QueryScope;
use crate::mail::uidindex::{ImapUid, ImapUidvalidity}; use crate::mail::uidindex::{ImapUid, ImapUidvalidity};
use crate::mail::unique_ident::UniqueIdent;
use crate::imap::attributes::AttributesProxy; use crate::imap::attributes::AttributesProxy;
use crate::imap::flags; use crate::imap::flags;
use crate::imap::mail_view::{MailView, SeenFlag}; use crate::imap::mail_view::{MailView, SeenFlag};
use crate::imap::response::Body; use crate::imap::response::Body;
use crate::imap::search; //use crate::imap::search;
use crate::imap::index::Index;
const DEFAULT_FLAGS: [Flag; 5] = [ const DEFAULT_FLAGS: [Flag; 5] = [
@ -147,7 +147,7 @@ impl MailboxView {
let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>(); let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
let mails = self.get_mail_ids(sequence_set, *is_uid_store)?; let mails = self.index().fetch(sequence_set, *is_uid_store)?;
for mi in mails.iter() { for mi in mails.iter() {
match kind { match kind {
StoreType::Add => { StoreType::Add => {
@ -190,7 +190,7 @@ impl MailboxView {
to: Arc<Mailbox>, to: Arc<Mailbox>,
is_uid_copy: &bool, is_uid_copy: &bool,
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>)> { ) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>)> {
let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?; let mails = self.index().fetch(sequence_set, *is_uid_copy)?;
let mut new_uuids = vec![]; let mut new_uuids = vec![];
for mi in mails.iter() { for mi in mails.iter() {
@ -217,7 +217,7 @@ impl MailboxView {
to: Arc<Mailbox>, to: Arc<Mailbox>,
is_uid_copy: &bool, is_uid_copy: &bool,
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>, Vec<Body<'static>>)> { ) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>, Vec<Body<'static>>)> {
let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?; let mails = self.index().fetch(sequence_set, *is_uid_copy)?;
for mi in mails.iter() { for mi in mails.iter() {
to.move_from(&self.0.mailbox, mi.uuid).await?; to.move_from(&self.0.mailbox, mi.uuid).await?;
@ -255,16 +255,17 @@ impl MailboxView {
true => QueryScope::Full, true => QueryScope::Full,
_ => QueryScope::Partial, _ => QueryScope::Partial,
}; };
let mids = MailIdentifiersList(self.get_mail_ids(sequence_set, *is_uid_fetch)?); let mail_idx_list = self.index().fetch(sequence_set, *is_uid_fetch)?;
let uuids = mids.uuids();
// [2/6] Fetch the emails // [2/6] Fetch the emails
let uuids = mail_idx_list.iter().map(|midx| midx.uuid).collect::<Vec<_>>();
let query = self.0.query(&uuids, query_scope); let query = self.0.query(&uuids, query_scope);
let query_result = query.fetch().await?; let query_result = query.fetch().await?;
// [3/6] Derive an IMAP-specific view from the results, apply the filters // [3/6] Derive an IMAP-specific view from the results, apply the filters
let views = query_result.iter() let views = query_result.iter()
.map(MailView::new) .zip(mail_idx_list.into_iter())
.map(|(qr, midx)| MailView::new(qr, midx))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// [4/6] Apply the IMAP transformation to keep only relevant fields // [4/6] Apply the IMAP transformation to keep only relevant fields
@ -296,9 +297,10 @@ impl MailboxView {
pub async fn search<'a>( pub async fn search<'a>(
&self, &self,
_charset: &Option<Charset<'a>>, _charset: &Option<Charset<'a>>,
search_key: &SearchKey<'a>, _search_key: &SearchKey<'a>,
uid: bool, _uid: bool,
) -> Result<Vec<Body<'static>>> { ) -> Result<Vec<Body<'static>>> {
/*
// 1. Compute the subset of sequence identifiers we need to fetch // 1. Compute the subset of sequence identifiers we need to fetch
let query = search::Criteria(search_key); let query = search::Criteria(search_key);
let (seq_set, seq_type) = query.to_sequence_set(); let (seq_set, seq_type) = query.to_sequence_set();
@ -313,78 +315,14 @@ impl MailboxView {
let _need_body = query.need_body(); let _need_body = query.need_body();
Ok(vec![Body::Data(Data::Search(mail_u32))]) Ok(vec![Body::Data(Data::Search(mail_u32))])
*/
unimplemented!()
} }
// ---- // ----
fn index<'a>(&'a self) -> Index<'a> {
// Gets the IMAP ID, the IMAP UIDs and, the Aerogramme UUIDs of mails identified by a SequenceSet of Index(&self.0.snapshot)
// sequence numbers (~ IMAP selector)
fn get_mail_ids(
&self,
sequence_set: &SequenceSet,
by_uid: bool,
) -> Result<Vec<MailIdentifiers>> {
let mail_vec = self
.0
.snapshot
.idx_by_uid
.iter()
.map(|(uid, uuid)| (*uid, *uuid))
.collect::<Vec<_>>();
let mut mails = vec![];
if by_uid {
if mail_vec.is_empty() {
return Ok(vec![]);
} }
let iter_strat = sequence::Strategy::Naive {
largest: mail_vec.last().unwrap().0,
};
let mut i = 0;
for uid in sequence_set.iter(iter_strat) {
while mail_vec.get(i).map(|mail| mail.0 < uid).unwrap_or(false) {
i += 1;
}
if let Some(mail) = mail_vec.get(i) {
if mail.0 == uid {
mails.push(MailIdentifiers {
i: NonZeroU32::try_from(i as u32 + 1).unwrap(),
uid: mail.0,
uuid: mail.1,
});
}
} else {
break;
}
}
} else {
if mail_vec.is_empty() {
bail!("No such message (mailbox is empty)");
}
let iter_strat = sequence::Strategy::Naive {
largest: NonZeroU32::try_from((mail_vec.len()) as u32).unwrap(),
};
for i in sequence_set.iter(iter_strat) {
if let Some(mail) = mail_vec.get(i.get() as usize - 1) {
mails.push(MailIdentifiers {
i,
uid: mail.0,
uuid: mail.1,
});
} else {
bail!("No such mail: {}", i);
}
}
}
Ok(mails)
}
// ----
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state` /// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
fn uidvalidity_status(&self) -> Result<Body<'static>> { fn uidvalidity_status(&self) -> Result<Body<'static>> {
@ -501,25 +439,6 @@ impl MailboxView {
} }
} }
pub struct MailIdentifiers {
pub i: NonZeroU32,
pub uid: ImapUid,
pub uuid: UniqueIdent,
}
pub struct MailIdentifiersList(Vec<MailIdentifiers>);
impl MailIdentifiersList {
fn ids(&self) -> Vec<NonZeroU32> {
self.0.iter().map(|mi| mi.i).collect()
}
fn uids(&self) -> Vec<ImapUid> {
self.0.iter().map(|mi| mi.uid).collect()
}
fn uuids(&self) -> Vec<UniqueIdent> {
self.0.iter().map(|mi| mi.uuid).collect()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -558,7 +477,7 @@ mod tests {
message_key: key, message_key: key,
rfc822_size: 8usize, rfc822_size: 8usize,
}; };
let ids = MailIdentifiers { let ids = MailIndex {
i: NonZeroU32::MIN, i: NonZeroU32::MIN,
uid: NonZeroU32::MIN, uid: NonZeroU32::MIN,
uuid: unique_ident::gen_ident(), uuid: unique_ident::gen_ident(),

View file

@ -4,12 +4,12 @@ mod command;
mod flags; mod flags;
mod flow; mod flow;
mod imf_view; mod imf_view;
mod index;
mod mail_view; mod mail_view;
mod mailbox_view; mod mailbox_view;
mod mime_view; mod mime_view;
mod response; mod response;
mod search; mod search;
mod selectors;
mod session; mod session;
use std::net::SocketAddr; use std::net::SocketAddr;

View file

@ -1,100 +0,0 @@
use std::iter::zip;
use anyhow::{anyhow, Result};
use crate::cryptoblob::Key;
use crate::imap::mail_view::{FetchedMail, MailView};
use crate::imap::mailbox_view::MailIdentifiers;
use crate::mail::mailbox::MailMeta;
use crate::mail::unique_ident::UniqueIdent;
pub struct BodyIdentifier<'a> {
pub msg_uuid: &'a UniqueIdent,
pub msg_key: &'a Key,
}
#[derive(Default)]
pub struct MailSelectionBuilder<'a> {
//attrs: AttributeProxy,
mail_count: usize,
need_body: bool,
mi: &'a [MailIdentifiers],
meta: &'a [MailMeta],
flags: &'a [&'a Vec<String>],
bodies: &'a [Vec<u8>],
}
impl<'a> MailSelectionBuilder<'a> {
pub fn new(need_body: bool, mail_count: usize) -> Self {
Self {
mail_count,
need_body,
..MailSelectionBuilder::default()
}
}
pub fn with_mail_identifiers(&mut self, mi: &'a [MailIdentifiers]) -> &mut Self {
self.mi = mi;
self
}
pub fn with_metadata(&mut self, meta: &'a [MailMeta]) -> &mut Self {
self.meta = meta;
self
}
pub fn with_flags(&mut self, flags: &'a [&'a Vec<String>]) -> &mut Self {
self.flags = flags;
self
}
pub fn bodies_to_collect(&self) -> Vec<BodyIdentifier> {
if !self.need_body {
return vec![];
}
zip(self.mi, self.meta)
.map(|(mi, meta)| BodyIdentifier {
msg_uuid: &mi.uuid,
msg_key: &meta.message_key,
})
.collect::<Vec<_>>()
}
pub fn with_bodies(&mut self, rbodies: &'a [Vec<u8>]) -> &mut Self {
self.bodies = rbodies;
self
}
pub fn build(&self) -> Result<Vec<MailView<'a>>> {
let mut bodies = vec![];
if !self.need_body {
for m in self.meta.iter() {
let (_, hdrs) =
eml_codec::parse_imf(&m.headers).or(Err(anyhow!("Invalid mail headers")))?;
bodies.push(FetchedMail::Partial(hdrs));
}
} else {
for rb in self.bodies.iter() {
let (_, p) = eml_codec::parse_message(&rb).or(Err(anyhow!("Invalid mail body")))?;
bodies.push(FetchedMail::new_from_message(p));
}
}
if self.mi.len() != self.mail_count && self.meta.len() != self.mail_count
|| self.flags.len() != self.mail_count
|| bodies.len() != self.mail_count
{
return Err(anyhow!("Can't build a mail view selection as parts were not correctly registered into the builder."));
}
Ok(zip(self.mi, zip(self.meta, zip(self.flags, bodies)))
.map(|(ids, (meta, (flags, content)))| MailView {
ids,
meta,
flags,
content,
})
.collect())
}
}

View file

@ -13,6 +13,7 @@ pub struct Query<'a,'b> {
pub scope: QueryScope, pub scope: QueryScope,
} }
#[allow(dead_code)]
pub enum QueryScope { pub enum QueryScope {
Index, Index,
Partial, Partial,
@ -106,6 +107,7 @@ impl<'a> QueryResult<'a> {
} }
} }
#[allow(dead_code)]
pub fn index(&self) -> &IndexEntry { pub fn index(&self) -> &IndexEntry {
match self { match self {
Self::IndexResult { index, .. } => index, Self::IndexResult { index, .. } => index,
@ -114,7 +116,7 @@ impl<'a> QueryResult<'a> {
} }
} }
pub fn metadata(&self) -> Option<&MailMeta> { pub fn metadata(&'a self) -> Option<&'a MailMeta> {
match self { match self {
Self::IndexResult { .. } => None, Self::IndexResult { .. } => None,
Self::PartialResult { metadata, .. } => Some(metadata), Self::PartialResult { metadata, .. } => Some(metadata),
@ -122,7 +124,8 @@ impl<'a> QueryResult<'a> {
} }
} }
pub fn content(&self) -> Option<&[u8]> { #[allow(dead_code)]
pub fn content(&'a self) -> Option<&'a [u8]> {
match self { match self {
Self::FullResult { content, .. } => Some(content), Self::FullResult { content, .. } => Some(content),
_ => None, _ => None,