io_email/message/m2dir/
copy.rs1use alloc::{collections::VecDeque, string::String};
21use core::mem;
22use std::path::PathBuf;
23
24use io_m2dir::{
25 coroutine::*,
26 entry::{
27 get::{
28 M2dirEntryGet as InnerGet, M2dirEntryGetError as GetErr,
29 M2dirEntryGetOptions as GetOpts,
30 },
31 store::{
32 M2dirEntryStore as InnerStore, M2dirEntryStoreError as StoreErr,
33 M2dirEntryStoreOptions as StoreOpts,
34 },
35 },
36 m2dir::types::M2dir,
37};
38use log::trace;
39use thiserror::Error;
40
41use crate::m2dir::convert::{InvalidMailboxName, resolve_mailbox};
42
43#[derive(Debug, Error)]
45pub enum M2dirMessageCopyError {
46 #[error(transparent)]
47 Get(#[from] GetErr),
48 #[error(transparent)]
49 Store(#[from] StoreErr),
50 #[error(transparent)]
51 InvalidMailbox(#[from] InvalidMailboxName),
52}
53
54pub struct M2dirMessageCopy {
56 source: M2dir,
57 target: M2dir,
58 pending: VecDeque<String>,
59 stage: Stage,
60}
61
62impl M2dirMessageCopy {
63 pub fn new(
64 root: impl Into<PathBuf>,
65 from: &str,
66 to: &str,
67 ids: &[&str],
68 ) -> Result<Self, M2dirMessageCopyError> {
69 trace!("prepare m2dir message copy");
70 let root = root.into();
71 let source = resolve_mailbox(&root, from)?;
72 let target = resolve_mailbox(&root, to)?;
73 Ok(Self {
74 source,
75 target,
76 pending: ids.iter().map(|s| (*s).into()).collect(),
77 stage: Stage::Idle,
78 })
79 }
80}
81
82enum Stage {
83 Idle,
84 Getting(InnerGet),
85 Storing(InnerStore),
86}
87
88impl M2dirCoroutine for M2dirMessageCopy {
89 type Yield = M2dirYield;
90 type Return = Result<(), M2dirMessageCopyError>;
91
92 fn resume(&mut self, arg: Option<M2dirArg>) -> M2dirCoroutineState<Self::Yield, Self::Return> {
93 let mut arg = arg;
94 loop {
95 if matches!(self.stage, Stage::Idle) {
96 let Some(id) = self.pending.pop_front() else {
97 return M2dirCoroutineState::Complete(Ok(()));
98 };
99 self.stage =
100 Stage::Getting(InnerGet::new(self.source.clone(), id, GetOpts::default()));
101 }
102 match mem::replace(&mut self.stage, Stage::Idle) {
103 Stage::Idle => unreachable!(),
104 Stage::Getting(mut get) => match get.resume(arg.take()) {
105 M2dirCoroutineState::Yielded(y) => {
106 self.stage = Stage::Getting(get);
107 return M2dirCoroutineState::Yielded(y);
108 }
109 M2dirCoroutineState::Complete(Ok(ok)) => {
110 self.stage = Stage::Storing(InnerStore::new(
111 self.target.clone(),
112 ok.contents,
113 StoreOpts::default(),
114 ));
115 }
116 M2dirCoroutineState::Complete(Err(err)) => {
117 return M2dirCoroutineState::Complete(Err(err.into()));
118 }
119 },
120 Stage::Storing(mut store) => match store.resume(arg.take()) {
121 M2dirCoroutineState::Yielded(y) => {
122 self.stage = Stage::Storing(store);
123 return M2dirCoroutineState::Yielded(y);
124 }
125 M2dirCoroutineState::Complete(Ok(_entry)) => {
126 }
128 M2dirCoroutineState::Complete(Err(err)) => {
129 return M2dirCoroutineState::Complete(Err(err.into()));
130 }
131 },
132 }
133 }
134 }
135}