use core::{fmt, mem};
use alloc::{string::ToString, vec::Vec};
use thiserror::Error;
use crate::{coroutine::*, path::VdirPath};
#[derive(Clone, Debug, Error)]
pub enum VdirCollectionRenameError {
#[error("Vdir collection rename failed: unexpected arg {0:?}")]
UnexpectedArg(Option<VdirReply>),
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct VdirCollectionRenameOptions {}
#[derive(Debug)]
pub struct VdirCollectionRename {
state: State,
#[allow(dead_code)]
opts: VdirCollectionRenameOptions,
}
impl VdirCollectionRename {
pub fn new(
path: impl Into<VdirPath>,
name: impl ToString,
opts: VdirCollectionRenameOptions,
) -> Self {
let from = path.into();
let to = from.with_file_name(&name.to_string());
Self {
opts,
state: State::Start {
pairs: vec![(from, to)],
},
}
}
}
impl VdirCoroutine for VdirCollectionRename {
type Yield = VdirYield;
type Return = Result<(), VdirCollectionRenameError>;
fn resume(&mut self, arg: Option<VdirReply>) -> VdirCoroutineState<Self::Yield, Self::Return> {
match (&mut self.state, arg) {
(State::Start { pairs }, None) => {
let pairs = mem::take(pairs);
self.state = State::AwaitRename;
VdirCoroutineState::Yielded(VdirYield::WantsRename(pairs))
}
(State::AwaitRename, Some(VdirReply::Rename)) => VdirCoroutineState::Complete(Ok(())),
(_, arg) => {
let err = VdirCollectionRenameError::UnexpectedArg(arg);
VdirCoroutineState::Complete(Err(err))
}
}
}
}
#[derive(Debug)]
enum State {
Start { pairs: Vec<(VdirPath, VdirPath)> },
AwaitRename,
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Start { .. } => f.write_str("start"),
Self::AwaitRename => f.write_str("await rename reply"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn renames_within_same_parent() {
let mut cor = VdirCollectionRename::new(
"root/contacts",
"people",
VdirCollectionRenameOptions::default(),
);
let pairs = match cor.resume(None) {
VdirCoroutineState::Yielded(VdirYield::WantsRename(pairs)) => pairs,
state => panic!("expected WantsRename, got {state:?}"),
};
assert_eq!(
pairs,
vec![(
VdirPath::from("root/contacts"),
VdirPath::from("root/people")
)]
);
match cor.resume(Some(VdirReply::Rename)) {
VdirCoroutineState::Complete(Ok(())) => {}
state => panic!("expected Complete(Ok), got {state:?}"),
}
}
#[test]
fn unexpected_reply_returns_error() {
let mut cor = VdirCollectionRename::new(
"root/contacts",
"people",
VdirCollectionRenameOptions::default(),
);
let _ = cor.resume(None);
let err = match cor.resume(Some(VdirReply::DirCreate)) {
VdirCoroutineState::Complete(Err(err)) => err,
state => panic!("expected Complete(Err), got {state:?}"),
};
assert!(matches!(err, VdirCollectionRenameError::UnexpectedArg(_)));
}
}