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