io_email/message/imap/
delete.rs1use alloc::{string::String, vec};
16use core::mem;
17
18use io_imap::{
19 codec::fragmentizer::Fragmentizer,
20 coroutine::{ImapCoroutine, ImapCoroutineState, ImapYield},
21 rfc3501::{
22 expunge::{ImapMailboxExpunge, ImapMailboxExpungeError},
23 select::{ImapMailboxSelect, ImapMailboxSelectError, ImapMailboxSelectOptions},
24 store::{ImapMessageStore, ImapMessageStoreError, ImapMessageStoreOptions},
25 },
26 types::flag::StoreType,
27};
28use log::trace;
29use thiserror::Error;
30
31use crate::{
32 flag::types::{Flag, IanaFlag},
33 imap::convert::{InvalidMailboxName, InvalidUidSet, flag_from, parse_mailbox, parse_uids},
34};
35
36#[derive(Debug, Error)]
38pub enum ImapMessageDeleteError {
39 #[error(transparent)]
40 Select(#[from] ImapMailboxSelectError),
41 #[error(transparent)]
42 Store(#[from] ImapMessageStoreError),
43 #[error(transparent)]
44 Expunge(#[from] ImapMailboxExpungeError),
45 #[error("invalid IMAP mailbox `{0}`")]
46 InvalidMailbox(String),
47 #[error("invalid message UID `{0}`")]
48 InvalidUid(String),
49 #[error("empty UID set")]
50 EmptyUidSet,
51 #[error("coroutine was resumed after completion")]
52 ResumedAfterDone,
53}
54
55impl From<InvalidMailboxName> for ImapMessageDeleteError {
56 fn from(err: InvalidMailboxName) -> Self {
57 Self::InvalidMailbox(err.0)
58 }
59}
60
61impl From<InvalidUidSet> for ImapMessageDeleteError {
62 fn from(err: InvalidUidSet) -> Self {
63 match err {
64 InvalidUidSet::Empty => Self::EmptyUidSet,
65 InvalidUidSet::Invalid(s) => Self::InvalidUid(s),
66 }
67 }
68}
69
70pub struct ImapMessageDelete {
72 state: State,
73}
74
75impl ImapMessageDelete {
76 pub fn new(mailbox: &str, id: &str, auto_select: bool) -> Result<Self, ImapMessageDeleteError> {
77 trace!("prepare IMAP message delete (auto_select={auto_select})");
78 let mbox = parse_mailbox(mailbox)?;
79 let sequence_set = parse_uids(&[id])?;
80 let imap_flags = vec![flag_from(&Flag::from_iana(IanaFlag::Deleted))];
81 let store = ImapMessageStore::new(
82 sequence_set,
83 StoreType::Add,
84 imap_flags,
85 ImapMessageStoreOptions { uid: true },
86 );
87 let state = if auto_select {
88 State::Selecting {
89 select: ImapMailboxSelect::new(mbox, ImapMailboxSelectOptions::default()),
90 store,
91 }
92 } else {
93 State::Storing(store)
94 };
95 Ok(Self { state })
96 }
97}
98
99#[allow(clippy::large_enum_variant)] enum State {
101 Selecting {
102 select: ImapMailboxSelect,
103 store: ImapMessageStore,
104 },
105 Storing(ImapMessageStore),
106 Expunging(ImapMailboxExpunge),
107 Done,
108}
109
110impl ImapCoroutine for ImapMessageDelete {
111 type Yield = ImapYield;
112 type Return = Result<(), ImapMessageDeleteError>;
113
114 fn resume(
115 &mut self,
116 fragmentizer: &mut Fragmentizer,
117 mut bytes: Option<&[u8]>,
118 ) -> ImapCoroutineState<Self::Yield, Self::Return> {
119 loop {
120 match mem::replace(&mut self.state, State::Done) {
121 State::Selecting { mut select, store } => {
122 match select.resume(fragmentizer, bytes.take()) {
123 ImapCoroutineState::Yielded(yielded) => {
124 self.state = State::Selecting { select, store };
125 return ImapCoroutineState::Yielded(yielded);
126 }
127 ImapCoroutineState::Complete(Ok(_)) => {
128 self.state = State::Storing(store);
129 }
130 ImapCoroutineState::Complete(Err(err)) => {
131 return ImapCoroutineState::Complete(Err(err.into()));
132 }
133 }
134 }
135 State::Storing(mut store) => match store.resume(fragmentizer, bytes.take()) {
136 ImapCoroutineState::Yielded(yielded) => {
137 self.state = State::Storing(store);
138 return ImapCoroutineState::Yielded(yielded);
139 }
140 ImapCoroutineState::Complete(Ok(_)) => {
141 self.state = State::Expunging(ImapMailboxExpunge::new());
142 }
143 ImapCoroutineState::Complete(Err(err)) => {
144 return ImapCoroutineState::Complete(Err(err.into()));
145 }
146 },
147 State::Expunging(mut expunge) => match expunge.resume(fragmentizer, bytes.take()) {
148 ImapCoroutineState::Yielded(yielded) => {
149 self.state = State::Expunging(expunge);
150 return ImapCoroutineState::Yielded(yielded);
151 }
152 ImapCoroutineState::Complete(Ok(_expunged)) => {
153 return ImapCoroutineState::Complete(Ok(()));
154 }
155 ImapCoroutineState::Complete(Err(err)) => {
156 return ImapCoroutineState::Complete(Err(err.into()));
157 }
158 },
159 State::Done => {
160 return ImapCoroutineState::Complete(Err(
161 ImapMessageDeleteError::ResumedAfterDone,
162 ));
163 }
164 }
165 }
166 }
167}