io_email/message/imap/
move.rs1use alloc::string::String;
13use core::mem;
14
15use io_imap::{
16 codec::fragmentizer::Fragmentizer,
17 coroutine::{ImapCoroutine, ImapCoroutineState, ImapYield},
18 rfc3501::select::{ImapMailboxSelect, ImapMailboxSelectError, ImapMailboxSelectOptions},
19 rfc6851::r#move::{
20 ImapMessageMove as InnerImapMessageMove, ImapMessageMoveError as InnerImapMessageMoveError,
21 ImapMessageMoveOptions,
22 },
23};
24use log::trace;
25use thiserror::Error;
26
27use crate::imap::convert::{InvalidMailboxName, InvalidUidSet, parse_mailbox, parse_uids};
28
29#[derive(Debug, Error)]
31pub enum ImapMessageMoveError {
32 #[error(transparent)]
33 Select(#[from] ImapMailboxSelectError),
34 #[error(transparent)]
35 Move(#[from] InnerImapMessageMoveError),
36 #[error("invalid IMAP mailbox `{0}`")]
37 InvalidMailbox(String),
38 #[error("invalid message UID `{0}`")]
39 InvalidUid(String),
40 #[error("empty UID set")]
41 EmptyUidSet,
42 #[error("coroutine was resumed after completion")]
43 ResumedAfterDone,
44}
45
46impl From<InvalidMailboxName> for ImapMessageMoveError {
47 fn from(err: InvalidMailboxName) -> Self {
48 Self::InvalidMailbox(err.0)
49 }
50}
51
52impl From<InvalidUidSet> for ImapMessageMoveError {
53 fn from(err: InvalidUidSet) -> Self {
54 match err {
55 InvalidUidSet::Empty => Self::EmptyUidSet,
56 InvalidUidSet::Invalid(s) => Self::InvalidUid(s),
57 }
58 }
59}
60
61pub struct ImapMessageMove {
63 state: State,
64}
65
66impl ImapMessageMove {
67 pub fn new(
68 from: &str,
69 to: &str,
70 ids: &[&str],
71 auto_select: bool,
72 ) -> Result<Self, ImapMessageMoveError> {
73 trace!("prepare IMAP message move (auto_select={auto_select})");
74 let src = parse_mailbox(from)?;
75 let dst = parse_mailbox(to)?;
76 let sequence_set = parse_uids(ids)?;
77 let mv = InnerImapMessageMove::new(sequence_set, dst, ImapMessageMoveOptions { uid: true });
78 let state = if auto_select {
79 State::Selecting {
80 select: ImapMailboxSelect::new(src, ImapMailboxSelectOptions::default()),
81 mv,
82 }
83 } else {
84 State::Moving(mv)
85 };
86 Ok(Self { state })
87 }
88}
89
90#[allow(clippy::large_enum_variant)] enum State {
92 Selecting {
93 select: ImapMailboxSelect,
94 mv: InnerImapMessageMove,
95 },
96 Moving(InnerImapMessageMove),
97 Done,
98}
99
100impl ImapCoroutine for ImapMessageMove {
101 type Yield = ImapYield;
102 type Return = Result<(), ImapMessageMoveError>;
103
104 fn resume(
105 &mut self,
106 fragmentizer: &mut Fragmentizer,
107 mut bytes: Option<&[u8]>,
108 ) -> ImapCoroutineState<Self::Yield, Self::Return> {
109 loop {
110 match mem::replace(&mut self.state, State::Done) {
111 State::Selecting { mut select, mv } => {
112 match select.resume(fragmentizer, bytes.take()) {
113 ImapCoroutineState::Yielded(yielded) => {
114 self.state = State::Selecting { select, mv };
115 return ImapCoroutineState::Yielded(yielded);
116 }
117 ImapCoroutineState::Complete(Ok(_)) => {
118 self.state = State::Moving(mv);
119 }
120 ImapCoroutineState::Complete(Err(err)) => {
121 return ImapCoroutineState::Complete(Err(err.into()));
122 }
123 }
124 }
125 State::Moving(mut mv) => match mv.resume(fragmentizer, bytes.take()) {
126 ImapCoroutineState::Yielded(yielded) => {
127 self.state = State::Moving(mv);
128 return ImapCoroutineState::Yielded(yielded);
129 }
130 ImapCoroutineState::Complete(Ok(_copyuid)) => {
131 return ImapCoroutineState::Complete(Ok(()));
132 }
133 ImapCoroutineState::Complete(Err(err)) => {
134 return ImapCoroutineState::Complete(Err(err.into()));
135 }
136 },
137 State::Done => {
138 return ImapCoroutineState::Complete(Err(
139 ImapMessageMoveError::ResumedAfterDone,
140 ));
141 }
142 }
143 }
144 }
145}