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