Aerogramme refactoring #57
6 changed files with 143 additions and 259 deletions
80
src/imap/index.rs
Normal file
80
src/imap/index.rs
Normal 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>
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
use std::num::NonZeroU32;
|
||||
|
||||
use anyhow::{anyhow, bail, Result, Context};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
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::fetch::{
|
||||
MessageDataItem, MessageDataItemName, Section as FetchSection,
|
||||
|
@ -16,87 +16,73 @@ use eml_codec::{
|
|||
part::{composite::Message, AnyPart},
|
||||
};
|
||||
|
||||
|
||||
use crate::mail::query::QueryResult;
|
||||
|
||||
use crate::imap::attributes::AttributesProxy;
|
||||
use crate::imap::flags;
|
||||
use crate::imap::imf_view::message_envelope;
|
||||
use crate::imap::mailbox_view::MailIdentifiers;
|
||||
use crate::imap::mime_view;
|
||||
use crate::imap::response::Body;
|
||||
use crate::mail::query::QueryResult;
|
||||
use crate::imap::index::MailIndex;
|
||||
|
||||
pub struct MailView<'a> {
|
||||
pub in_idx: MailIndex<'a>,
|
||||
pub query_result: &'a QueryResult<'a>,
|
||||
pub content: FetchedMail<'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 {
|
||||
in_idx,
|
||||
query_result,
|
||||
content: match query_result {
|
||||
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)
|
||||
},
|
||||
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)
|
||||
}
|
||||
QueryResult::IndexResult { .. } => FetchedMail::None,
|
||||
QueryResult::IndexResult { .. } => FetchedMail::IndexOnly,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn uid(&self) -> MessageDataItem<'static> {
|
||||
MessageDataItem::Uid(self.ids.uid.clone())
|
||||
MessageDataItem::Uid(self.in_idx.uid.clone())
|
||||
}
|
||||
|
||||
fn flags(&self) -> MessageDataItem<'static> {
|
||||
MessageDataItem::Flags(
|
||||
self.flags
|
||||
self.in_idx
|
||||
.flags
|
||||
.iter()
|
||||
.filter_map(|f| flags::from_str(f))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn rfc_822_size(&self) -> MessageDataItem<'static> {
|
||||
MessageDataItem::Rfc822Size(self.meta.rfc822_size as u32)
|
||||
fn rfc_822_size(&self) -> Result<MessageDataItem<'static>> {
|
||||
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> {
|
||||
MessageDataItem::Rfc822Header(NString(
|
||||
self.meta
|
||||
.headers
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.ok()
|
||||
.map(IString::Literal),
|
||||
))
|
||||
fn rfc_822_header(&self) -> Result<MessageDataItem<'static>> {
|
||||
let hdrs: NString = self.query_result.metadata().ok_or(anyhow!("mail metadata are required"))?.headers.to_vec().try_into()?;
|
||||
Ok(MessageDataItem::Rfc822Header(hdrs))
|
||||
}
|
||||
|
||||
fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> {
|
||||
Ok(MessageDataItem::Rfc822Text(NString(
|
||||
self.content
|
||||
.as_full()?
|
||||
.raw_body
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.ok()
|
||||
.map(IString::Literal),
|
||||
)))
|
||||
let txt: NString = self.content.as_full()?.raw_body.to_vec().try_into()?;
|
||||
Ok(MessageDataItem::Rfc822Text(txt))
|
||||
}
|
||||
|
||||
fn rfc822(&self) -> Result<MessageDataItem<'static>> {
|
||||
Ok(MessageDataItem::Rfc822(NString(
|
||||
self.content
|
||||
.as_full()?
|
||||
.raw_part
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.ok()
|
||||
.map(IString::Literal),
|
||||
)))
|
||||
let full: NString = self.content.as_full()?.raw_part.to_vec().try_into()?;
|
||||
Ok(MessageDataItem::Rfc822(full))
|
||||
}
|
||||
|
||||
fn envelope(&self) -> MessageDataItem<'static> {
|
||||
|
@ -119,16 +105,16 @@ impl<'a> MailView<'a> {
|
|||
/// peek does not implicitly set the \Seen flag
|
||||
/// eg. BODY[HEADER.FIELDS (DATE FROM)]
|
||||
/// eg. BODY[]<0.2048>
|
||||
fn body_ext<'b>(
|
||||
fn body_ext(
|
||||
&self,
|
||||
section: &Option<FetchSection<'b>>,
|
||||
section: &Option<FetchSection<'static>>,
|
||||
partial: &Option<(u32, NonZeroU32)>,
|
||||
peek: &bool,
|
||||
) -> Result<(MessageDataItem<'b>, SeenFlag)> {
|
||||
) -> Result<(MessageDataItem<'static>, SeenFlag)> {
|
||||
// Manage Seen flag
|
||||
let mut seen = SeenFlag::DoNothing;
|
||||
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
|
||||
//self.mailbox.add_flags(uuid, &[seen_flag]).await?;
|
||||
seen = SeenFlag::MustAdd;
|
||||
|
@ -141,7 +127,7 @@ impl<'a> MailView<'a> {
|
|||
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((
|
||||
MessageDataItem::BodyExt {
|
||||
|
@ -156,13 +142,13 @@ impl<'a> MailView<'a> {
|
|||
fn internal_date(&self) -> Result<MessageDataItem<'static>> {
|
||||
let dt = Utc
|
||||
.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()
|
||||
.ok_or(anyhow!("Unable to parse internal date"))?;
|
||||
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 res_attrs = ap
|
||||
.attrs
|
||||
|
@ -170,8 +156,8 @@ impl<'a> MailView<'a> {
|
|||
.map(|attr| match attr {
|
||||
MessageDataItemName::Uid => Ok(self.uid()),
|
||||
MessageDataItemName::Flags => Ok(self.flags()),
|
||||
MessageDataItemName::Rfc822Size => Ok(self.rfc_822_size()),
|
||||
MessageDataItemName::Rfc822Header => Ok(self.rfc_822_header()),
|
||||
MessageDataItemName::Rfc822Size => self.rfc_822_size(),
|
||||
MessageDataItemName::Rfc822Header => self.rfc_822_header(),
|
||||
MessageDataItemName::Rfc822Text => self.rfc_822_text(),
|
||||
MessageDataItemName::Rfc822 => self.rfc822(),
|
||||
MessageDataItemName::Envelope => Ok(self.envelope()),
|
||||
|
@ -192,7 +178,7 @@ impl<'a> MailView<'a> {
|
|||
|
||||
Ok((
|
||||
Body::Data(Data::Fetch {
|
||||
seq: self.ids.i,
|
||||
seq: self.in_idx.i,
|
||||
items: res_attrs.try_into()?,
|
||||
}),
|
||||
seen,
|
||||
|
@ -208,19 +194,15 @@ pub enum SeenFlag {
|
|||
// -------------------
|
||||
|
||||
pub enum FetchedMail<'a> {
|
||||
None,
|
||||
IndexOnly,
|
||||
Partial(imf::Imf<'a>),
|
||||
Full(AnyPart<'a>),
|
||||
}
|
||||
impl<'a> FetchedMail<'a> {
|
||||
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>> {
|
||||
match self {
|
||||
FetchedMail::Full(x) => Ok(&x),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::num::NonZeroU32;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, bail, Error, Result};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
|
||||
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::response::{Code, Data, Status};
|
||||
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::snapshot::FrozenMailbox;
|
||||
use crate::mail::query::QueryScope;
|
||||
use crate::mail::uidindex::{ImapUid, ImapUidvalidity};
|
||||
use crate::mail::unique_ident::UniqueIdent;
|
||||
|
||||
use crate::imap::attributes::AttributesProxy;
|
||||
use crate::imap::flags;
|
||||
use crate::imap::mail_view::{MailView, SeenFlag};
|
||||
use crate::imap::response::Body;
|
||||
use crate::imap::search;
|
||||
//use crate::imap::search;
|
||||
use crate::imap::index::Index;
|
||||
|
||||
|
||||
const DEFAULT_FLAGS: [Flag; 5] = [
|
||||
|
@ -147,7 +147,7 @@ impl MailboxView {
|
|||
|
||||
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() {
|
||||
match kind {
|
||||
StoreType::Add => {
|
||||
|
@ -190,7 +190,7 @@ impl MailboxView {
|
|||
to: Arc<Mailbox>,
|
||||
is_uid_copy: &bool,
|
||||
) -> 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![];
|
||||
for mi in mails.iter() {
|
||||
|
@ -217,7 +217,7 @@ impl MailboxView {
|
|||
to: Arc<Mailbox>,
|
||||
is_uid_copy: &bool,
|
||||
) -> 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() {
|
||||
to.move_from(&self.0.mailbox, mi.uuid).await?;
|
||||
|
@ -255,16 +255,17 @@ impl MailboxView {
|
|||
true => QueryScope::Full,
|
||||
_ => QueryScope::Partial,
|
||||
};
|
||||
let mids = MailIdentifiersList(self.get_mail_ids(sequence_set, *is_uid_fetch)?);
|
||||
let uuids = mids.uuids();
|
||||
let mail_idx_list = self.index().fetch(sequence_set, *is_uid_fetch)?;
|
||||
|
||||
// [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_result = query.fetch().await?;
|
||||
|
||||
// [3/6] Derive an IMAP-specific view from the results, apply the filters
|
||||
let views = query_result.iter()
|
||||
.map(MailView::new)
|
||||
.zip(mail_idx_list.into_iter())
|
||||
.map(|(qr, midx)| MailView::new(qr, midx))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// [4/6] Apply the IMAP transformation to keep only relevant fields
|
||||
|
@ -296,9 +297,10 @@ impl MailboxView {
|
|||
pub async fn search<'a>(
|
||||
&self,
|
||||
_charset: &Option<Charset<'a>>,
|
||||
search_key: &SearchKey<'a>,
|
||||
uid: bool,
|
||||
_search_key: &SearchKey<'a>,
|
||||
_uid: bool,
|
||||
) -> Result<Vec<Body<'static>>> {
|
||||
/*
|
||||
// 1. Compute the subset of sequence identifiers we need to fetch
|
||||
let query = search::Criteria(search_key);
|
||||
let (seq_set, seq_type) = query.to_sequence_set();
|
||||
|
@ -313,79 +315,15 @@ impl MailboxView {
|
|||
let _need_body = query.need_body();
|
||||
|
||||
Ok(vec![Body::Data(Data::Search(mail_u32))])
|
||||
*/
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
// Gets the IMAP ID, the IMAP UIDs and, the Aerogramme UUIDs of mails identified by a SequenceSet of
|
||||
// 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)
|
||||
fn index<'a>(&'a self) -> Index<'a> {
|
||||
Index(&self.0.snapshot)
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
|
||||
fn uidvalidity_status(&self) -> Result<Body<'static>> {
|
||||
let uid_validity = Status::ok(
|
||||
|
@ -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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -558,7 +477,7 @@ mod tests {
|
|||
message_key: key,
|
||||
rfc822_size: 8usize,
|
||||
};
|
||||
let ids = MailIdentifiers {
|
||||
let ids = MailIndex {
|
||||
i: NonZeroU32::MIN,
|
||||
uid: NonZeroU32::MIN,
|
||||
uuid: unique_ident::gen_ident(),
|
||||
|
|
|
@ -4,12 +4,12 @@ mod command;
|
|||
mod flags;
|
||||
mod flow;
|
||||
mod imf_view;
|
||||
mod index;
|
||||
mod mail_view;
|
||||
mod mailbox_view;
|
||||
mod mime_view;
|
||||
mod response;
|
||||
mod search;
|
||||
mod selectors;
|
||||
mod session;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ pub struct Query<'a,'b> {
|
|||
pub scope: QueryScope,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum QueryScope {
|
||||
Index,
|
||||
Partial,
|
||||
|
@ -106,6 +107,7 @@ impl<'a> QueryResult<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn index(&self) -> &IndexEntry {
|
||||
match self {
|
||||
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 {
|
||||
Self::IndexResult { .. } => None,
|
||||
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 {
|
||||
Self::FullResult { content, .. } => Some(content),
|
||||
_ => None,
|
||||
|
|
Loading…
Reference in a new issue