2024-01-11 22:02:03 +00:00
|
|
|
use std::num::{NonZeroU32, NonZeroU64};
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 13:02:52 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
2024-01-10 13:45:36 +00:00
|
|
|
use imap_codec::imap_types::sequence::{SeqOrUid, Sequence, SequenceSet};
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-10 17:08:44 +00:00
|
|
|
use crate::mail::uidindex::{ImapUid, ModSeq, UidIndex};
|
2024-01-06 10:07:53 +00:00
|
|
|
use crate::mail::unique_ident::UniqueIdent;
|
|
|
|
|
2024-01-08 10:14:34 +00:00
|
|
|
pub struct Index<'a> {
|
2024-01-08 10:13:13 +00:00
|
|
|
pub imap_index: Vec<MailIndex<'a>>,
|
|
|
|
pub internal: &'a UidIndex,
|
|
|
|
}
|
2024-01-06 10:07:53 +00:00
|
|
|
impl<'a> Index<'a> {
|
2024-01-08 10:13:13 +00:00
|
|
|
pub fn new(internal: &'a UidIndex) -> Result<Self> {
|
|
|
|
let imap_index = internal
|
2024-01-06 10:07:53 +00:00
|
|
|
.idx_by_uid
|
|
|
|
.iter()
|
2024-01-08 10:13:13 +00:00
|
|
|
.enumerate()
|
|
|
|
.map(|(i_enum, (&uid, &uuid))| {
|
2024-01-10 17:08:44 +00:00
|
|
|
let (_, modseq, flags) = internal
|
2024-01-08 10:14:34 +00:00
|
|
|
.table
|
|
|
|
.get(&uuid)
|
2024-01-10 17:08:44 +00:00
|
|
|
.ok_or(anyhow!("mail is missing from index"))?;
|
2024-01-08 10:13:13 +00:00
|
|
|
let i_int: u32 = (i_enum + 1).try_into()?;
|
|
|
|
let i: NonZeroU32 = i_int.try_into()?;
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 10:14:34 +00:00
|
|
|
Ok(MailIndex {
|
|
|
|
i,
|
|
|
|
uid,
|
|
|
|
uuid,
|
2024-01-10 17:08:44 +00:00
|
|
|
modseq: *modseq,
|
2024-01-08 10:14:34 +00:00
|
|
|
flags,
|
|
|
|
})
|
2024-01-08 10:13:13 +00:00
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>>>()?;
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 10:14:34 +00:00
|
|
|
Ok(Self {
|
|
|
|
imap_index,
|
|
|
|
internal,
|
|
|
|
})
|
2024-01-08 10:13:13 +00:00
|
|
|
}
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
pub fn last(&'a self) -> Option<&'a MailIndex<'a>> {
|
2024-01-08 10:14:34 +00:00
|
|
|
self.imap_index.last()
|
2024-01-08 10:13:13 +00:00
|
|
|
}
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
/// Fetch mail descriptors based on a sequence of UID
|
|
|
|
///
|
|
|
|
/// Complexity analysis:
|
|
|
|
/// - Sort is O(n * log n) where n is the number of uid generated by the sequence
|
|
|
|
/// - Finding the starting point in the index O(log m) where m is the size of the mailbox
|
|
|
|
/// While n =< m, it's not clear if the difference is big or not.
|
|
|
|
///
|
|
|
|
/// For now, the algorithm tries to be fast for small values of n,
|
|
|
|
/// as it is what is expected by clients.
|
|
|
|
///
|
|
|
|
/// So we assume for our implementation that : n << m.
|
|
|
|
/// It's not true for full mailbox searches for example...
|
|
|
|
pub fn fetch_on_uid(&'a self, sequence_set: &SequenceSet) -> Vec<&'a MailIndex<'a>> {
|
|
|
|
if self.imap_index.is_empty() {
|
|
|
|
return vec![];
|
|
|
|
}
|
2024-01-10 13:45:36 +00:00
|
|
|
let largest = self.last().expect("The mailbox is not empty").uid;
|
|
|
|
let mut unroll_seq = sequence_set.iter(largest).collect::<Vec<_>>();
|
2024-01-08 10:13:13 +00:00
|
|
|
unroll_seq.sort();
|
|
|
|
|
|
|
|
let start_seq = match unroll_seq.iter().next() {
|
|
|
|
Some(elem) => elem,
|
|
|
|
None => return vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
// Quickly jump to the right point in the mailbox vector O(log m) instead
|
|
|
|
// of iterating one by one O(m). Works only because both unroll_seq & imap_index are sorted per uid.
|
|
|
|
let mut imap_idx = {
|
2024-01-08 10:14:34 +00:00
|
|
|
let start_idx = self
|
|
|
|
.imap_index
|
|
|
|
.partition_point(|mail_idx| &mail_idx.uid < start_seq);
|
2024-01-08 10:13:13 +00:00
|
|
|
&self.imap_index[start_idx..]
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut acc = vec![];
|
|
|
|
for wanted_uid in unroll_seq.iter() {
|
|
|
|
// Slide the window forward as long as its first element is lower than our wanted uid.
|
|
|
|
let start_idx = match imap_idx.iter().position(|midx| &midx.uid >= wanted_uid) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => break,
|
2024-01-06 10:07:53 +00:00
|
|
|
};
|
2024-01-08 10:13:13 +00:00
|
|
|
imap_idx = &imap_idx[start_idx..];
|
2024-01-06 10:07:53 +00:00
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
// If the beginning of our new window is the uid we want, we collect it
|
|
|
|
if &imap_idx[0].uid == wanted_uid {
|
|
|
|
acc.push(&imap_idx[0]);
|
2024-01-06 10:07:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
acc
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fetch_on_id(&'a self, sequence_set: &SequenceSet) -> Result<Vec<&'a MailIndex<'a>>> {
|
2024-01-08 13:02:52 +00:00
|
|
|
if self.imap_index.is_empty() {
|
|
|
|
return Ok(vec![]);
|
|
|
|
}
|
2024-01-10 13:45:36 +00:00
|
|
|
let largest = NonZeroU32::try_from(self.imap_index.len() as u32)?;
|
2024-01-08 20:18:45 +00:00
|
|
|
let mut acc = sequence_set
|
2024-01-10 13:45:36 +00:00
|
|
|
.iter(largest)
|
2024-01-08 10:14:34 +00:00
|
|
|
.map(|wanted_id| {
|
|
|
|
self.imap_index
|
|
|
|
.get((wanted_id.get() as usize) - 1)
|
|
|
|
.ok_or(anyhow!("Mail not found"))
|
|
|
|
})
|
2024-01-08 20:18:45 +00:00
|
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
|
|
|
|
// Sort the result to be consistent with UID
|
|
|
|
acc.sort_by(|a, b| a.i.cmp(&b.i));
|
|
|
|
|
|
|
|
Ok(acc)
|
2024-01-08 10:13:13 +00:00
|
|
|
}
|
2024-01-08 10:14:34 +00:00
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
pub fn fetch(
|
|
|
|
self: &'a Index<'a>,
|
|
|
|
sequence_set: &SequenceSet,
|
|
|
|
by_uid: bool,
|
|
|
|
) -> Result<Vec<&'a MailIndex<'a>>> {
|
|
|
|
match by_uid {
|
|
|
|
true => Ok(self.fetch_on_uid(sequence_set)),
|
|
|
|
_ => self.fetch_on_id(sequence_set),
|
|
|
|
}
|
2024-01-06 10:07:53 +00:00
|
|
|
}
|
2024-01-11 22:02:03 +00:00
|
|
|
|
|
|
|
pub fn fetch_changed_since(
|
|
|
|
self: &'a Index<'a>,
|
|
|
|
sequence_set: &SequenceSet,
|
|
|
|
maybe_modseq: Option<NonZeroU64>,
|
|
|
|
by_uid: bool,
|
|
|
|
) -> Result<Vec<&'a MailIndex<'a>>> {
|
|
|
|
let raw = self.fetch(sequence_set, by_uid)?;
|
|
|
|
let res = match maybe_modseq {
|
|
|
|
Some(pit) => raw.into_iter().filter(|midx| midx.modseq > pit).collect(),
|
|
|
|
None => raw,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fetch_unchanged_since(
|
|
|
|
self: &'a Index<'a>,
|
|
|
|
sequence_set: &SequenceSet,
|
|
|
|
maybe_modseq: Option<NonZeroU64>,
|
|
|
|
by_uid: bool,
|
|
|
|
) -> Result<(Vec<&'a MailIndex<'a>>, Vec<&'a MailIndex<'a>>)> {
|
|
|
|
let raw = self.fetch(sequence_set, by_uid)?;
|
|
|
|
let res = match maybe_modseq {
|
|
|
|
Some(pit) => raw.into_iter().partition(|midx| midx.modseq <= pit),
|
|
|
|
None => (raw, vec![]),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
}
|
2024-01-06 10:07:53 +00:00
|
|
|
}
|
|
|
|
|
2024-01-08 10:13:13 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2024-01-06 10:07:53 +00:00
|
|
|
pub struct MailIndex<'a> {
|
|
|
|
pub i: NonZeroU32,
|
|
|
|
pub uid: ImapUid,
|
|
|
|
pub uuid: UniqueIdent,
|
2024-01-10 17:08:44 +00:00
|
|
|
pub modseq: ModSeq,
|
2024-01-06 10:33:56 +00:00
|
|
|
pub flags: &'a Vec<String>,
|
2024-01-06 10:07:53 +00:00
|
|
|
}
|
2024-01-06 17:01:44 +00:00
|
|
|
|
|
|
|
impl<'a> MailIndex<'a> {
|
2024-01-06 22:35:23 +00:00
|
|
|
// The following functions are used to implement the SEARCH command
|
2024-01-06 17:01:44 +00:00
|
|
|
pub fn is_in_sequence_i(&self, seq: &Sequence) -> bool {
|
|
|
|
match seq {
|
|
|
|
Sequence::Single(SeqOrUid::Asterisk) => true,
|
|
|
|
Sequence::Single(SeqOrUid::Value(target)) => target == &self.i,
|
2024-01-06 22:35:23 +00:00
|
|
|
Sequence::Range(SeqOrUid::Asterisk, SeqOrUid::Value(x))
|
|
|
|
| Sequence::Range(SeqOrUid::Value(x), SeqOrUid::Asterisk) => x <= &self.i,
|
|
|
|
Sequence::Range(SeqOrUid::Value(x1), SeqOrUid::Value(x2)) => {
|
|
|
|
if x1 < x2 {
|
|
|
|
x1 <= &self.i && &self.i <= x2
|
|
|
|
} else {
|
|
|
|
x1 >= &self.i && &self.i >= x2
|
|
|
|
}
|
|
|
|
}
|
2024-01-06 17:01:44 +00:00
|
|
|
Sequence::Range(SeqOrUid::Asterisk, SeqOrUid::Asterisk) => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_in_sequence_uid(&self, seq: &Sequence) -> bool {
|
|
|
|
match seq {
|
|
|
|
Sequence::Single(SeqOrUid::Asterisk) => true,
|
|
|
|
Sequence::Single(SeqOrUid::Value(target)) => target == &self.uid,
|
|
|
|
Sequence::Range(SeqOrUid::Asterisk, SeqOrUid::Value(x))
|
2024-01-06 22:35:23 +00:00
|
|
|
| Sequence::Range(SeqOrUid::Value(x), SeqOrUid::Asterisk) => x <= &self.uid,
|
|
|
|
Sequence::Range(SeqOrUid::Value(x1), SeqOrUid::Value(x2)) => {
|
|
|
|
if x1 < x2 {
|
|
|
|
x1 <= &self.uid && &self.uid <= x2
|
|
|
|
} else {
|
|
|
|
x1 >= &self.uid && &self.uid >= x2
|
|
|
|
}
|
|
|
|
}
|
2024-01-06 17:01:44 +00:00
|
|
|
Sequence::Range(SeqOrUid::Asterisk, SeqOrUid::Asterisk) => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_flag_set(&self, flag: &str) -> bool {
|
2024-01-06 22:35:23 +00:00
|
|
|
self.flags
|
|
|
|
.iter()
|
|
|
|
.any(|candidate| candidate.as_str() == flag)
|
2024-01-06 17:01:44 +00:00
|
|
|
}
|
|
|
|
}
|