use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
use chrono::{DateTime, FixedOffset};
use io_jmap::{
rfc8620::JmapSession,
rfc8621::{
MAIL_CAPABILITY,
email::{JmapEmail, JmapEmailAddress, JmapEmailProperty},
},
};
use crate::{
address::Address,
envelope::types::{Envelope, normalize_message_id},
flag::types::{Flag, IanaFlag},
};
pub(crate) fn keyword_from(flag: &Flag) -> String {
match flag.iana() {
Some(IanaFlag::Seen) => "$seen".into(),
Some(IanaFlag::Answered) => "$answered".into(),
Some(IanaFlag::Flagged) => "$flagged".into(),
Some(IanaFlag::Draft) => "$draft".into(),
Some(IanaFlag::Deleted) => "$deleted".into(),
Some(IanaFlag::Forwarded) => "$forwarded".into(),
Some(IanaFlag::Junk) => "$junk".into(),
Some(IanaFlag::NotJunk) => "$notjunk".into(),
Some(IanaFlag::Phishing) => "$phishing".into(),
Some(IanaFlag::Important) => "$important".into(),
Some(IanaFlag::MdnSent) => "$mdnsent".into(),
None => flag.raw().to_string(),
}
}
pub(crate) fn compute_position_limit(
page: Option<u32>,
page_size: Option<u32>,
) -> (Option<u64>, Option<u64>) {
let Some(size) = page_size else {
return (None, None);
};
let page = page.unwrap_or(1).max(1);
let position = ((page - 1) as u64).saturating_mul(size as u64);
(Some(position), Some(size as u64))
}
pub(crate) fn account_id_of(session: &JmapSession) -> String {
session
.primary_accounts
.get(MAIL_CAPABILITY)
.cloned()
.unwrap_or_default()
}
pub(crate) fn envelope_properties() -> Vec<JmapEmailProperty> {
vec![
JmapEmailProperty::Id,
JmapEmailProperty::Keywords,
JmapEmailProperty::Subject,
JmapEmailProperty::From,
JmapEmailProperty::To,
JmapEmailProperty::SentAt,
JmapEmailProperty::Size,
JmapEmailProperty::HasAttachment,
JmapEmailProperty::MessageId,
]
}
pub(crate) fn envelope_from(email: JmapEmail) -> Envelope {
let id = email.id.unwrap_or_default();
let flags = email
.keywords
.unwrap_or_default()
.into_iter()
.filter_map(|(k, v)| if v { Some(Flag::from_raw(k)) } else { None })
.collect();
let subject = email.subject.unwrap_or_default();
let from = email
.from
.unwrap_or_default()
.into_iter()
.map(address_from)
.collect();
let to = email
.to
.unwrap_or_default()
.into_iter()
.map(address_from)
.collect();
let date = email.sent_at.as_deref().and_then(parse_rfc3339);
let size = email.size.unwrap_or(0);
let has_attachment = email.has_attachment;
let message_id = email
.message_id
.and_then(|ids| ids.into_iter().find_map(|s| normalize_message_id(&s)));
Envelope {
id,
message_id,
flags,
subject,
from,
to,
date,
size,
has_attachment,
}
}
fn address_from(addr: JmapEmailAddress) -> Address {
Address {
name: addr.name,
email: addr.email,
}
}
fn parse_rfc3339(raw: &str) -> Option<DateTime<FixedOffset>> {
let trimmed = raw.trim();
if trimmed.is_empty() {
return None;
}
DateTime::parse_from_rfc3339(trimmed).ok()
}