Implement Recent in Select
This commit is contained in:
parent
8a0df56cde
commit
22d0f11173
3 changed files with 62 additions and 33 deletions
|
@ -106,8 +106,6 @@ impl<'a> StateContext<'a> {
|
||||||
let sum = mb.summary().await?;
|
let sum = mb.summary().await?;
|
||||||
tracing::trace!(summary=%sum, "mailbox.summary");
|
tracing::trace!(summary=%sum, "mailbox.summary");
|
||||||
|
|
||||||
let body = vec![Data::Exists(sum.exists.try_into()?), Data::Recent(0)];
|
|
||||||
|
|
||||||
let r_unseen = Status::ok(
|
let r_unseen = Status::ok(
|
||||||
None,
|
None,
|
||||||
Some(Code::Unseen(
|
Some(Code::Unseen(
|
||||||
|
@ -118,33 +116,47 @@ impl<'a> StateContext<'a> {
|
||||||
.map_err(Error::msg)?;
|
.map_err(Error::msg)?;
|
||||||
//let r_permanentflags = Status::ok(None, Some(Code::
|
//let r_permanentflags = Status::ok(None, Some(Code::
|
||||||
|
|
||||||
let tr = flow::Transition::Select(mb);
|
let mut res = Vec::<ImapRes>::new();
|
||||||
|
|
||||||
Ok((
|
res.push(ImapRes::Data(Data::Exists(sum.exists)));
|
||||||
vec![
|
|
||||||
ImapRes::Data(Data::Exists(0)),
|
res.push(ImapRes::Data(Data::Recent(sum.recent)));
|
||||||
ImapRes::Data(Data::Recent(0)),
|
|
||||||
ImapRes::Data(Data::Flags(vec![])),
|
res.push(ImapRes::Data(Data::Flags(vec![])));
|
||||||
ImapRes::Status(
|
|
||||||
Status::ok(
|
let uid_validity = Status::ok(
|
||||||
None,
|
None,
|
||||||
Some(Code::UidValidity(sum.validity)),
|
Some(Code::UidValidity(sum.validity)),
|
||||||
"UIDs valid"
|
"UIDs valid"
|
||||||
)
|
)
|
||||||
.map_err(Error::msg)?,
|
.map_err(Error::msg)?;
|
||||||
),
|
res.push(ImapRes::Status(uid_validity));
|
||||||
/*ImapRes::Status(),
|
|
||||||
ImapRes::Status(),*/
|
let next_uid = Status::ok(
|
||||||
ImapRes::Status(
|
None,
|
||||||
Status::ok(
|
Some(Code::UidNext(sum.next)),
|
||||||
|
"Predict next UID"
|
||||||
|
).map_err(Error::msg)?;
|
||||||
|
res.push(ImapRes::Status(next_uid));
|
||||||
|
|
||||||
|
if let Some(unseen) = sum.unseen {
|
||||||
|
let status_unseen = Status::ok(
|
||||||
|
None,
|
||||||
|
Some(Code::Unseen(unseen.clone())),
|
||||||
|
"First unseen UID",
|
||||||
|
)
|
||||||
|
.map_err(Error::msg)?;
|
||||||
|
res.push(ImapRes::Status(status_unseen));
|
||||||
|
}
|
||||||
|
|
||||||
|
let last = Status::ok(
|
||||||
Some(self.tag.clone()),
|
Some(self.tag.clone()),
|
||||||
Some(Code::ReadWrite),
|
Some(Code::ReadWrite),
|
||||||
"Select completed",
|
"Select completed",
|
||||||
)
|
).map_err(Error::msg)?;
|
||||||
.map_err(Error::msg)?,
|
res.push(ImapRes::Status(last));
|
||||||
),
|
|
||||||
],
|
let tr = flow::Transition::Select(mb);
|
||||||
tr,
|
Ok((res, tr))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use k2v_client::K2vClient;
|
use k2v_client::K2vClient;
|
||||||
use rusoto_s3::S3Client;
|
use rusoto_s3::S3Client;
|
||||||
|
@ -8,12 +10,14 @@ use crate::login::Credentials;
|
||||||
use crate::mail_ident::*;
|
use crate::mail_ident::*;
|
||||||
use crate::uidindex::*;
|
use crate::uidindex::*;
|
||||||
|
|
||||||
pub struct Summary {
|
pub struct Summary<'a> {
|
||||||
pub validity: ImapUidvalidity,
|
pub validity: ImapUidvalidity,
|
||||||
pub next: ImapUid,
|
pub next: ImapUid,
|
||||||
pub exists: usize,
|
pub exists: u32,
|
||||||
|
pub recent: u32,
|
||||||
|
pub unseen: Option<&'a ImapUid>,
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Summary {
|
impl std::fmt::Display for Summary<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -23,6 +27,9 @@ impl std::fmt::Display for Summary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Non standard but common flags:
|
||||||
|
// https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml
|
||||||
pub struct Mailbox {
|
pub struct Mailbox {
|
||||||
bucket: String,
|
bucket: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -34,6 +41,8 @@ pub struct Mailbox {
|
||||||
uid_index: Bayou<UidIndex>,
|
uid_index: Bayou<UidIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDEA: We store a specific flag named $unseen.
|
||||||
|
// If it is not present, we add the virtual flag \Seen
|
||||||
impl Mailbox {
|
impl Mailbox {
|
||||||
pub fn new(creds: &Credentials, name: String) -> Result<Self> {
|
pub fn new(creds: &Credentials, name: String) -> Result<Self> {
|
||||||
let uid_index = Bayou::<UidIndex>::new(creds, name.clone())?;
|
let uid_index = Bayou::<UidIndex>::new(creds, name.clone())?;
|
||||||
|
@ -52,10 +61,15 @@ impl Mailbox {
|
||||||
self.uid_index.sync().await?;
|
self.uid_index.sync().await?;
|
||||||
let state = self.uid_index.state();
|
let state = self.uid_index.state();
|
||||||
|
|
||||||
|
let unseen = state.idx_by_flag.get(&"$unseen".to_string()).and_then(|os| os.get_min());
|
||||||
|
let recent = state.idx_by_flag.get(&"\\Recent".to_string()).map(|os| os.len()).unwrap_or(0);
|
||||||
|
|
||||||
return Ok(Summary {
|
return Ok(Summary {
|
||||||
validity: state.uidvalidity,
|
validity: state.uidvalidity,
|
||||||
next: state.uidnext,
|
next: state.uidnext,
|
||||||
exists: state.idx_by_uid.len(),
|
exists: u32::try_from(state.idx_by_uid.len())?,
|
||||||
|
recent: u32::try_from(recent)?,
|
||||||
|
unseen,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,9 @@ impl FlagIndex {
|
||||||
self.0.get_mut(flag).and_then(|set| set.remove(&uid));
|
self.0.get_mut(flag).and_then(|set| set.remove(&uid));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
pub fn get(&self, f: &Flag) -> Option<&OrdSet<ImapUid>> {
|
||||||
|
self.0.get(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- CUSTOM SERIALIZATION AND DESERIALIZATION ----
|
// ---- CUSTOM SERIALIZATION AND DESERIALIZATION ----
|
||||||
|
|
Loading…
Reference in a new issue