io_vdir/collection/
rename.rs1use core::{fmt, mem};
30
31use alloc::{string::ToString, vec::Vec};
32
33use thiserror::Error;
34
35use crate::{coroutine::*, path::VdirPath};
36
37#[derive(Clone, Debug, Error)]
39pub enum VdirCollectionRenameError {
40 #[error("Vdir collection rename failed: unexpected arg {0:?}")]
43 UnexpectedArg(Option<VdirReply>),
44}
45
46#[derive(Clone, Debug, Default, Eq, PartialEq)]
48pub struct VdirCollectionRenameOptions {}
49
50#[derive(Debug)]
53pub struct VdirCollectionRename {
54 state: State,
55 #[allow(dead_code)]
56 opts: VdirCollectionRenameOptions,
57}
58
59impl VdirCollectionRename {
60 pub fn new(
63 path: impl Into<VdirPath>,
64 name: impl ToString,
65 opts: VdirCollectionRenameOptions,
66 ) -> Self {
67 let from = path.into();
68 let to = from.with_file_name(&name.to_string());
69
70 Self {
71 opts,
72 state: State::Start {
73 pairs: vec![(from, to)],
74 },
75 }
76 }
77}
78
79impl VdirCoroutine for VdirCollectionRename {
80 type Yield = VdirYield;
81 type Return = Result<(), VdirCollectionRenameError>;
82
83 fn resume(&mut self, arg: Option<VdirReply>) -> VdirCoroutineState<Self::Yield, Self::Return> {
84 match (&mut self.state, arg) {
85 (State::Start { pairs }, None) => {
86 let pairs = mem::take(pairs);
87 self.state = State::AwaitRename;
88 VdirCoroutineState::Yielded(VdirYield::WantsRename(pairs))
89 }
90 (State::AwaitRename, Some(VdirReply::Rename)) => VdirCoroutineState::Complete(Ok(())),
91 (_, arg) => {
92 let err = VdirCollectionRenameError::UnexpectedArg(arg);
93 VdirCoroutineState::Complete(Err(err))
94 }
95 }
96 }
97}
98
99#[derive(Debug)]
100enum State {
101 Start { pairs: Vec<(VdirPath, VdirPath)> },
102 AwaitRename,
103}
104
105impl fmt::Display for State {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 Self::Start { .. } => f.write_str("start"),
109 Self::AwaitRename => f.write_str("await rename reply"),
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn renames_within_same_parent() {
120 let mut cor = VdirCollectionRename::new(
121 "root/contacts",
122 "people",
123 VdirCollectionRenameOptions::default(),
124 );
125
126 let pairs = match cor.resume(None) {
127 VdirCoroutineState::Yielded(VdirYield::WantsRename(pairs)) => pairs,
128 state => panic!("expected WantsRename, got {state:?}"),
129 };
130 assert_eq!(
131 pairs,
132 vec![(
133 VdirPath::from("root/contacts"),
134 VdirPath::from("root/people")
135 )]
136 );
137
138 match cor.resume(Some(VdirReply::Rename)) {
139 VdirCoroutineState::Complete(Ok(())) => {}
140 state => panic!("expected Complete(Ok), got {state:?}"),
141 }
142 }
143
144 #[test]
145 fn unexpected_reply_returns_error() {
146 let mut cor = VdirCollectionRename::new(
147 "root/contacts",
148 "people",
149 VdirCollectionRenameOptions::default(),
150 );
151 let _ = cor.resume(None);
152
153 let err = match cor.resume(Some(VdirReply::DirCreate)) {
154 VdirCoroutineState::Complete(Err(err)) => err,
155 state => panic!("expected Complete(Err), got {state:?}"),
156 };
157 assert!(matches!(err, VdirCollectionRenameError::UnexpectedArg(_)));
158 }
159}