io_email/envelope/imap/
diff.rs1use core::num::NonZeroU32;
5
6use alloc::{
7 format,
8 string::{String, ToString},
9 vec,
10 vec::Vec,
11};
12
13use io_imap::types::{
14 fetch::{MacroOrMessageDataItemNames, MessageDataItem, MessageDataItemName},
15 flag::FlagFetch,
16};
17
18use crate::{
19 envelope::{
20 imap::list::envelope_from,
21 types::{Envelope, FlagUpdate},
22 },
23 flag::types::Flag,
24};
25
26const STATE_BYTES: usize = 4 + 8 + 4;
29
30#[derive(Clone, Copy, Debug, Default)]
34pub struct ImapState {
35 pub uid_validity: u32,
36 pub highest_mod_seq: u64,
37 pub highest_uid: u32,
38}
39
40impl ImapState {
41 pub fn decode(bytes: &[u8]) -> Option<Self> {
44 if bytes.len() != STATE_BYTES {
45 return None;
46 }
47
48 let uid_validity = u32::from_le_bytes(bytes[0..4].try_into().ok()?);
49 let highest_mod_seq = u64::from_le_bytes(bytes[4..12].try_into().ok()?);
50 let highest_uid = u32::from_le_bytes(bytes[12..16].try_into().ok()?);
51
52 Some(Self {
53 uid_validity,
54 highest_mod_seq,
55 highest_uid,
56 })
57 }
58
59 pub fn encode(&self) -> Vec<u8> {
61 let mut out = Vec::with_capacity(STATE_BYTES);
62 out.extend_from_slice(&self.uid_validity.to_le_bytes());
63 out.extend_from_slice(&self.highest_mod_seq.to_le_bytes());
64 out.extend_from_slice(&self.highest_uid.to_le_bytes());
65 out
66 }
67}
68
69pub fn new_message_item_names() -> MacroOrMessageDataItemNames<'static> {
72 MacroOrMessageDataItemNames::MessageDataItemNames(vec![
73 MessageDataItemName::Uid,
74 MessageDataItemName::Flags,
75 MessageDataItemName::Envelope,
76 MessageDataItemName::Rfc822Size,
77 ])
78}
79
80pub fn new_message_window(high: u32) -> Option<String> {
82 let start = high.checked_add(1)?;
83 Some(format!("{start}:*"))
84}
85
86pub fn flag_update_from_items(items: &[MessageDataItem<'static>]) -> Option<FlagUpdate> {
89 let mut uid: Option<NonZeroU32> = None;
90 let mut flags: Option<alloc::collections::BTreeSet<Flag>> = None;
91
92 for item in items {
93 match item {
94 MessageDataItem::Uid(u) => uid = Some(*u),
95 MessageDataItem::Flags(fs) => {
96 flags = Some(
97 fs.iter()
98 .filter_map(|f| match f {
99 FlagFetch::Flag(flag) => Some(Flag::from_raw(flag.to_string())),
100 _ => None,
101 })
102 .collect(),
103 );
104 }
105 _ => {}
106 }
107 }
108
109 Some(FlagUpdate {
110 id: uid?.get().to_string(),
111 flags: flags?,
112 })
113}
114
115pub fn envelope_from_items(items: Vec<MessageDataItem<'static>>) -> Envelope {
118 envelope_from(0, items)
119}