io_email/message/maildir/
copy.rs1use alloc::{collections::VecDeque, string::String};
14
15use io_maildir::{
16 coroutine::*,
17 entry::copy::{MaildirEntryCopy as InnerCopy, MaildirEntryCopyError as InnerErr},
18 maildir::types::Maildir,
19 store::MaildirStore,
20};
21use log::trace;
22use thiserror::Error;
23
24use crate::maildir::convert::{InvalidMailboxName, mailbox_path};
25
26#[derive(Debug, Error)]
28pub enum MaildirMessageCopyError {
29 #[error(transparent)]
30 Copy(#[from] InnerErr),
31 #[error(transparent)]
32 InvalidMailbox(#[from] InvalidMailboxName),
33}
34
35pub struct MaildirMessageCopy {
37 source: Maildir,
38 target: Maildir,
39 pending: VecDeque<String>,
40 current: Option<InnerCopy>,
41}
42
43impl MaildirMessageCopy {
44 pub fn new(
45 store: &MaildirStore,
46 from: &str,
47 to: &str,
48 ids: &[&str],
49 ) -> Result<Self, MaildirMessageCopyError> {
50 trace!("prepare Maildir message copy");
51 let source = Maildir::from_path(store.resolve(&mailbox_path(from)?));
52 let target = Maildir::from_path(store.resolve(&mailbox_path(to)?));
53 Ok(Self {
54 source,
55 target,
56 pending: ids.iter().map(|s| (*s).into()).collect(),
57 current: None,
58 })
59 }
60}
61
62impl MaildirCoroutine for MaildirMessageCopy {
63 type Yield = MaildirYield;
64 type Return = Result<(), MaildirMessageCopyError>;
65
66 fn resume(
67 &mut self,
68 arg: Option<MaildirReply>,
69 ) -> MaildirCoroutineState<Self::Yield, Self::Return> {
70 let mut arg = arg;
71 loop {
72 if self.current.is_none() {
73 let Some(id) = self.pending.pop_front() else {
74 return MaildirCoroutineState::Complete(Ok(()));
75 };
76 self.current = Some(InnerCopy::new(
77 id,
78 self.source.clone(),
79 self.target.clone(),
80 None,
81 ));
82 }
83
84 let inner = self.current.as_mut().unwrap();
85 match inner.resume(arg.take()) {
86 MaildirCoroutineState::Complete(Ok(())) => {
87 self.current = None;
88 }
89 MaildirCoroutineState::Yielded(y) => return MaildirCoroutineState::Yielded(y),
90 MaildirCoroutineState::Complete(Err(err)) => {
91 return MaildirCoroutineState::Complete(Err(err.into()));
92 }
93 }
94 }
95 }
96}