use core::{fmt, mem};
use alloc::collections::BTreeSet;
use log::trace;
use thiserror::Error;
use crate::{
coroutine::*,
path::{FsPath, MaildirPath},
store::MaildirStore,
};
#[derive(Clone, Debug, Error)]
pub enum MaildirDeleteError {
#[error("Maildir delete failed: unexpected arg {0:?}")]
UnexpectedArg(Option<MaildirReply>),
}
#[derive(Debug)]
pub struct MaildirDelete {
state: State,
}
impl MaildirDelete {
pub fn new(store: &MaildirStore, name: MaildirPath) -> Self {
let paths = BTreeSet::from_iter([store.resolve(&name)]);
Self {
state: State::Start { paths },
}
}
}
impl MaildirCoroutine for MaildirDelete {
type Yield = MaildirYield;
type Return = Result<(), MaildirDeleteError>;
fn resume(
&mut self,
arg: Option<MaildirReply>,
) -> MaildirCoroutineState<Self::Yield, Self::Return> {
trace!("maildir delete: {}", self.state);
match (&mut self.state, arg) {
(State::Start { paths }, None) => {
let paths = mem::take(paths);
self.state = State::AwaitRemove;
MaildirCoroutineState::Yielded(MaildirYield::WantsDirRemove(paths))
}
(State::AwaitRemove, Some(MaildirReply::DirRemove)) => {
MaildirCoroutineState::Complete(Ok(()))
}
(_, arg) => {
let err = MaildirDeleteError::UnexpectedArg(arg);
MaildirCoroutineState::Complete(Err(err))
}
}
}
}
#[derive(Debug)]
enum State {
Start { paths: BTreeSet<FsPath> },
AwaitRemove,
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Start { .. } => f.write_str("start"),
Self::AwaitRemove => f.write_str("await remove reply"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fs_store() -> MaildirStore {
MaildirStore {
root: FsPath::from("root"),
maildirpp: false,
}
}
fn maildirpp_store() -> MaildirStore {
MaildirStore {
root: FsPath::from("root"),
maildirpp: true,
}
}
#[test]
fn fs_removes_resolved_path() {
let mut cor = MaildirDelete::new(&fs_store(), MaildirPath::from("inbox"));
let paths = expect_wants_dir_remove(&mut cor);
assert_eq!(paths.len(), 1);
assert!(paths.contains(&FsPath::from("root/inbox")));
expect_complete_ok(&mut cor, Some(MaildirReply::DirRemove));
}
#[test]
fn maildirpp_removes_dotted_flat_path() {
let mut cor = MaildirDelete::new(&maildirpp_store(), MaildirPath::from("Foo/Bar"));
let paths = expect_wants_dir_remove(&mut cor);
assert!(paths.contains(&FsPath::from("root/.Foo.Bar")));
}
#[test]
fn unexpected_reply_returns_error() {
let mut cor = MaildirDelete::new(&fs_store(), MaildirPath::from("inbox"));
let _ = expect_wants_dir_remove(&mut cor);
let err = expect_complete_err(&mut cor, Some(MaildirReply::DirCreate));
assert!(matches!(err, MaildirDeleteError::UnexpectedArg(_)));
}
fn expect_wants_dir_remove(cor: &mut MaildirDelete) -> BTreeSet<FsPath> {
match cor.resume(None) {
MaildirCoroutineState::Yielded(MaildirYield::WantsDirRemove(paths)) => paths,
state => panic!("expected WantsDirRemove, got {state:?}"),
}
}
fn expect_complete_ok(cor: &mut MaildirDelete, arg: Option<MaildirReply>) {
match cor.resume(arg) {
MaildirCoroutineState::Complete(Ok(())) => {}
state => panic!("expected Complete(Ok), got {state:?}"),
}
}
fn expect_complete_err(
cor: &mut MaildirDelete,
arg: Option<MaildirReply>,
) -> MaildirDeleteError {
match cor.resume(arg) {
MaildirCoroutineState::Complete(Err(err)) => err,
state => panic!("expected Complete(Err), got {state:?}"),
}
}
}