1use core::fmt;
36
37use alloc::collections::BTreeSet;
38
39use log::trace;
40use thiserror::Error;
41
42use crate::{coroutine::*, path::M2dirPath};
43
44#[derive(Clone, Debug, Error)]
46pub enum M2dirDeleteError {
47 #[error("M2DIR DELETE failed: unexpected coroutine arg")]
48 UnexpectedArg,
49 #[error("M2DIR DELETE failed: missing coroutine arg")]
50 MissingArg,
51}
52
53#[derive(Clone, Debug, Default, Eq, PartialEq)]
55pub struct M2dirDeleteOptions {}
56
57pub struct M2dirDelete {
59 path: M2dirPath,
60 state: State,
61 #[allow(dead_code)]
62 opts: M2dirDeleteOptions,
63}
64
65impl M2dirDelete {
66 pub fn new(path: impl Into<M2dirPath>, opts: M2dirDeleteOptions) -> Self {
69 Self {
70 path: path.into(),
71 state: State::Start,
72 opts,
73 }
74 }
75}
76
77impl M2dirCoroutine for M2dirDelete {
78 type Yield = M2dirYield;
79 type Return = Result<(), M2dirDeleteError>;
80
81 fn resume(&mut self, arg: Option<M2dirArg>) -> M2dirCoroutineState<Self::Yield, Self::Return> {
82 trace!("delete m2dir: {}", self.state);
83
84 match (&self.state, arg) {
85 (State::Start, None) => {
86 let paths = BTreeSet::from_iter([self.path.clone()]);
87 trace!("wants directory removal at {}", self.path);
88 self.state = State::Removed;
89 M2dirCoroutineState::Yielded(M2dirYield::WantsDirRemove(paths))
90 }
91 (State::Removed, Some(M2dirArg::DirRemove)) => {
92 trace!("m2dir removed at {}", self.path);
93 M2dirCoroutineState::Complete(Ok(()))
94 }
95 (_, Some(_)) => {
96 let err = M2dirDeleteError::UnexpectedArg;
97 M2dirCoroutineState::Complete(Err(err))
98 }
99 (_, None) => {
100 let err = M2dirDeleteError::MissingArg;
101 M2dirCoroutineState::Complete(Err(err))
102 }
103 }
104 }
105}
106
107enum State {
108 Start,
109 Removed,
110}
111
112impl fmt::Display for State {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 Self::Start => f.write_str("start"),
116 Self::Removed => f.write_str("removed"),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn success_returns_ok() {
127 let mut delete = M2dirDelete::new("/tmp/inbox", M2dirDeleteOptions::default());
128
129 let paths = expect_wants_dir_remove(&mut delete, None);
130 assert_eq!(paths.len(), 1);
131 assert!(paths.contains(&M2dirPath::from("/tmp/inbox")));
132
133 expect_complete_ok(&mut delete, Some(M2dirArg::DirRemove));
134 }
135
136 #[test]
137 fn missing_arg_at_removed_state_returns_missing_arg_error() {
138 let mut delete = M2dirDelete::new("/tmp/inbox", M2dirDeleteOptions::default());
139 let _ = expect_wants_dir_remove(&mut delete, None);
140
141 let err = expect_complete_err(&mut delete, None);
142 assert!(matches!(err, M2dirDeleteError::MissingArg));
143 }
144
145 #[test]
146 fn unexpected_arg_at_start_returns_unexpected_arg_error() {
147 let mut delete = M2dirDelete::new("/tmp/inbox", M2dirDeleteOptions::default());
148
149 let err = expect_complete_err(&mut delete, Some(M2dirArg::DirRemove));
150 assert!(matches!(err, M2dirDeleteError::UnexpectedArg));
151 }
152
153 #[test]
154 fn unexpected_arg_kind_at_removed_state_returns_unexpected_arg_error() {
155 let mut delete = M2dirDelete::new("/tmp/inbox", M2dirDeleteOptions::default());
156 let _ = expect_wants_dir_remove(&mut delete, None);
157
158 let err = expect_complete_err(&mut delete, Some(M2dirArg::FileCreate));
159 assert!(matches!(err, M2dirDeleteError::UnexpectedArg));
160 }
161
162 #[test]
163 fn first_yield_carries_the_target_path() {
164 let mut delete = M2dirDelete::new("/tmp/some/deep/inbox", M2dirDeleteOptions::default());
165
166 let paths = expect_wants_dir_remove(&mut delete, None);
167 assert_eq!(
168 paths.iter().next().map(M2dirPath::as_str),
169 Some("/tmp/some/deep/inbox")
170 );
171 }
172
173 fn expect_wants_dir_remove(
176 cor: &mut M2dirDelete,
177 arg: Option<M2dirArg>,
178 ) -> BTreeSet<M2dirPath> {
179 match cor.resume(arg) {
180 M2dirCoroutineState::Yielded(M2dirYield::WantsDirRemove(paths)) => paths,
181 state => panic!("expected WantsDirRemove, got {state:?}"),
182 }
183 }
184
185 fn expect_complete_ok(cor: &mut M2dirDelete, arg: Option<M2dirArg>) {
186 match cor.resume(arg) {
187 M2dirCoroutineState::Complete(Ok(())) => {}
188 state => panic!("expected Complete(Ok), got {state:?}"),
189 }
190 }
191
192 fn expect_complete_err(cor: &mut M2dirDelete, arg: Option<M2dirArg>) -> M2dirDeleteError {
193 match cor.resume(arg) {
194 M2dirCoroutineState::Complete(Err(err)) => err,
195 state => panic!("expected Complete(Err), got {state:?}"),
196 }
197 }
198}