1#[cfg(feature = "async")]
5use std::future::Future;
6use std::marker;
7use std::pin::Pin;
8#[cfg(feature = "async")]
9use std::task::Context;
10#[cfg(feature = "async")]
11use std::task::Poll;
12
13#[cfg(feature = "async")]
14use pin_project::pin_project;
15
16use crate::error::VfsResult;
17use crate::prelude::*;
18#[cfg(feature = "async")]
19use crate::traits::asy::FromRefForWriterAsync;
20#[cfg(feature = "async")]
21use crate::traits::async_vfs::VfsAsync;
22#[cfg(feature = "async")]
23use crate::traits::async_vfs::WriteSupportingVfsAsync;
24#[cfg(feature = "resolve-path")]
25use crate::traits::resolve::DynamicHasField;
26#[cfg(feature = "resolve-path")]
27use crate::traits::resolve::HAS_FIELD_MAX_LEN;
28#[cfg(feature = "resolve-path")]
29use crate::traits::resolve::HasField;
30use crate::traits::sync::DirStructureItem;
31use crate::traits::sync::FromRefForWriter;
32use crate::traits::sync::NewtypeToInner;
33use crate::traits::vfs;
34#[cfg(feature = "resolve-path")]
35use crate::traits::vfs::OwnedPathType;
36#[cfg(feature = "async")]
37use crate::traits::vfs::PathType;
38#[cfg(feature = "async")]
39use crate::traits::vfs::VfsCore;
40
41#[cfg_attr(feature = "derive", doc = "```rust")]
49#[cfg_attr(not(feature = "derive"), doc = "```rust,compile_fail")]
50#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
77#[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
78pub struct CleanDir<T>(pub T);
79
80impl<'a, T, Vfs: vfs::Vfs<'a>> ReadFrom<'a, Vfs> for CleanDir<T>
81where
82 T: ReadFrom<'a, Vfs>,
83{
84 fn read_from(path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<Self, Vfs>
85 where
86 Self: Sized,
87 {
88 Ok(Self(T::read_from(path, vfs)?))
89 }
90}
91
92#[cfg(feature = "async")]
93#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
94#[pin_project]
95#[doc(hidden)]
96pub struct CleanDirReadFuture<'a, T, Vfs>
97where
98 T: ReadFromAsync<'a, Vfs> + Send + 'static,
99 Vfs: VfsAsync,
100{
101 #[pin]
102 inner: T::Future,
103}
104
105#[cfg(feature = "async")]
106#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
107impl<'a, T, Vfs: VfsAsync> Future for CleanDirReadFuture<'a, T, Vfs>
108where
109 T: ReadFromAsync<'a, Vfs> + Send + 'static,
110{
111 type Output = VfsResult<CleanDir<T>, Vfs>;
112
113 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
114 let this = self.project();
115 match this.inner.poll(cx) {
116 Poll::Ready(v) => Poll::Ready(v.map(CleanDir)),
117 Poll::Pending => Poll::Pending,
118 }
119 }
120}
121
122#[cfg(feature = "async")]
123#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
124impl<'a, T, Vfs: VfsAsync + 'a> ReadFromAsync<'a, Vfs> for CleanDir<T>
125where
126 T: ReadFromAsync<'a, Vfs> + Send + 'static,
127{
128 type Future = Pin<Box<dyn Future<Output = VfsResult<Self, Vfs>> + Send + 'a>>;
129
130 fn read_from_async(
131 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
132 vfs: Pin<&'a Vfs>,
133 ) -> Self::Future {
134 Box::pin(async move { T::read_from_async(path, vfs).await.map(Self) })
135 }
136}
137
138impl<'a, T, Vfs: vfs::WriteSupportingVfs<'a>> WriteTo<'a, Vfs> for CleanDir<T>
139where
140 T: WriteTo<'a, Vfs>,
141{
142 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<(), Vfs> {
143 Self::from_ref_for_writer(&self.0).write_to(path, vfs)
144 }
145}
146
147#[cfg(feature = "async")]
148#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
149impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs> for CleanDir<T>
150where
151 T: WriteToAsync<'a, Vfs> + Send + Sync + 'static,
152{
153 type Future = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'a>>;
154
155 fn write_to_async(
156 self,
157 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
158 vfs: Pin<&'a Vfs>,
159 ) -> Self::Future {
160 Box::pin(async move {
161 if vfs.exists(path.clone()).await? {
162 vfs.remove_dir_all(path.clone()).await?;
163 } else {
164 vfs.create_parent_dir(path.clone()).await?;
165 }
166 self.0.write_to_async(path, vfs).await
167 })
168 }
169}
170
171#[cfg(feature = "async")]
172#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
173#[pin_project(project_replace = CleanDirWriteRefFutureProjOwn)]
174#[doc(hidden)]
175pub enum CleanDirWriteRefFuture<'a, 'f, T, Vfs: WriteSupportingVfsAsync + 'static>
176where
177 T: WriteToAsyncRef<'a, Vfs> + Send + Sync + 'a,
178 <T as WriteToAsyncRef<'a, Vfs>>::Future<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
179 <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
180 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
181 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
182 'a: 'f,
183{
184 Poison,
185 ExistsCheck(
186 <Vfs as VfsAsync>::ExistsFuture<'f>,
187 <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
188 Pin<&'f Vfs>,
189 &'f T,
190 ),
191 RemoveDirAll(
192 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>,
193 <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
194 Pin<&'f Vfs>,
195 &'f T,
196 ),
197 Inner(<T as WriteToAsyncRef<'a, Vfs>>::Future<'f>),
198}
199
200#[cfg(feature = "async")]
201#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
202impl<'a, 'f, T, Vfs: WriteSupportingVfsAsync + 'static> Future
203 for CleanDirWriteRefFuture<'a, 'f, T, Vfs>
204where
205 T: WriteToAsyncRef<'a, Vfs> + Send + Sync + 'static,
206 <T as WriteToAsyncRef<'a, Vfs>>::Future<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
207 <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
208 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
209 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
210 'a: 'f,
211{
212 type Output = VfsResult<(), Vfs>;
213
214 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
215 let this = self.as_mut().project_replace(Self::Poison);
216 match this {
217 CleanDirWriteRefFutureProjOwn::Poison => {
218 panic!("polled after completion")
219 }
220 CleanDirWriteRefFutureProjOwn::ExistsCheck(mut fut, path, vfs, item) => {
221 match Pin::new(&mut fut).poll(cx) {
222 Poll::Ready(Ok(exists)) => {
223 if exists {
224 let fut = vfs.remove_dir_all(path.clone());
225 self.project_replace(Self::RemoveDirAll(fut, path.clone(), vfs, item));
226 cx.waker().wake_by_ref();
227 Poll::Pending
228 } else {
229 let fut = item.write_to_async_ref(path, vfs);
230 self.project_replace(Self::Inner(fut));
231 cx.waker().wake_by_ref();
232 Poll::Pending
233 }
234 }
235 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
236 Poll::Pending => {
237 self.project_replace(Self::ExistsCheck(fut, path, vfs, item));
238 Poll::Pending
239 }
240 }
241 }
242 CleanDirWriteRefFutureProjOwn::RemoveDirAll(mut fut, path, vfs, item) => {
243 match Pin::new(&mut fut).poll(cx) {
244 Poll::Ready(Ok(())) => {
245 let fut = item.write_to_async_ref(path.clone(), vfs);
246 self.project_replace(Self::Inner(fut));
247 cx.waker().wake_by_ref();
248 Poll::Pending
249 }
250 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
251 Poll::Pending => {
252 self.project_replace(Self::RemoveDirAll(fut, path, vfs, item));
253 Poll::Pending
254 }
255 }
256 }
257 CleanDirWriteRefFutureProjOwn::Inner(mut fut) => match Pin::new(&mut fut).poll(cx) {
258 Poll::Ready(v) => Poll::Ready(v),
259 Poll::Pending => {
260 self.project_replace(Self::Inner(fut));
261 Poll::Pending
262 }
263 },
264 }
265 }
266}
267
268#[cfg(feature = "async")]
269#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
270impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsyncRef<'a, Vfs> for CleanDir<T>
271where
272 T: WriteToAsyncRef<'a, Vfs> + Send + Sync + 'static,
273 for<'f> <T as WriteToAsyncRef<'a, Vfs>>::Future<'f>:
274 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
275 for<'f> <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
276 for<'f> <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
277 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
278{
279 type Future<'b>
280 = CleanDirWriteRefFuture<'a, 'b, T, Vfs>
281 where
282 Self: 'b,
283 'a: 'b,
284 Vfs: 'b;
285
286 fn write_to_async_ref<'b>(
287 &'b self,
288 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
289 vfs: Pin<&'b Vfs>,
290 ) -> Self::Future<'b>
291 where
292 'a: 'b,
293 {
294 let exists_future = vfs.exists(path.clone());
295 CleanDirWriteRefFuture::<'a, 'b, T, Vfs>::ExistsCheck(exists_future, path, vfs, &self.0)
296 }
297}
298
299impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs>> FromRefForWriter<'a, 'vfs, Vfs>
300 for CleanDir<T>
301where
302 T: WriteTo<'vfs, Vfs> + 'a,
303 Vfs: 'vfs,
304 'vfs: 'a,
305{
306 type Inner = T;
307 type Wr = CleanDirRefWr<'a, 'vfs, T, Vfs>;
308
309 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
310 CleanDirRefWr(value, marker::PhantomData)
311 }
312}
313
314#[cfg(feature = "async")]
315#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
316impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> FromRefForWriterAsync<'a, Vfs> for CleanDir<T>
317where
318 T: WriteToAsyncRef<'a, Vfs> + Send + Sync + 'static,
319 for<'f> <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
320 for<'f> <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
321 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
322{
323 type Inner = T;
324 type Wr = CleanDirRefWr<'a, 'a, T, Vfs>;
325
326 fn from_ref_for_writer_async(value: &'a Self::Inner) -> Self::Wr {
327 CleanDirRefWr(value, marker::PhantomData)
328 }
329}
330
331impl<T> NewtypeToInner for CleanDir<T>
332where
333 T: DirStructureItem,
334{
335 type Inner = T;
336
337 fn into_inner(self) -> Self::Inner {
338 self.0
339 }
340}
341
342#[cfg(feature = "resolve-path")]
343#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
344impl<const NAME: [char; HAS_FIELD_MAX_LEN], T> HasField<NAME> for CleanDir<T>
345where
346 T: HasField<NAME>,
347{
348 type Inner = <T as HasField<NAME>>::Inner;
349
350 fn resolve_path<P: OwnedPathType>(p: P) -> P {
351 T::resolve_path(p)
352 }
353}
354
355#[cfg(feature = "resolve-path")]
356#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
357impl<T> DynamicHasField for CleanDir<T>
358where
359 T: DynamicHasField,
360{
361 type Inner = <T as DynamicHasField>::Inner;
362
363 fn resolve_path<P: OwnedPathType>(p: P, name: &str) -> P {
364 T::resolve_path(p, name)
365 }
366}
367
368pub struct CleanDirRefWr<'a, 'vfs, T: ?Sized + 'a, Vfs: 'vfs>(
370 &'a T,
371 marker::PhantomData<&'vfs Vfs>,
372)
373where
374 'vfs: 'a;
375
376impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs>> WriteTo<'vfs, Vfs>
377 for CleanDirRefWr<'a, 'vfs, T, Vfs>
378where
379 T: ?Sized + WriteTo<'vfs, Vfs>,
380 'vfs: 'a,
381{
382 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
383 if vfs.exists(path)? {
384 vfs.remove_dir_all(path)?;
385 } else {
386 vfs.create_parent_dir(path)?;
387 }
388 self.0.write_to(path, vfs)
389 }
390}
391
392#[cfg(feature = "async")]
393#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
394impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs>
395 for CleanDirRefWr<'a, 'a, T, Vfs>
396where
397 T: WriteToAsyncRef<'a, Vfs> + Send + Sync + 'static,
398 for<'f> <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
399 for<'f> <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
400 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
401{
402 type Future = CleanDirRefWrWriteFuture<'a, 'a, T, Vfs>;
403
404 fn write_to_async(
405 self,
406 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
407 vfs: Pin<&'a Vfs>,
408 ) -> Self::Future {
409 let exists_future = vfs.exists(path.clone());
410 CleanDirRefWrWriteFuture::ExistsCheck(exists_future, path, vfs, self.0)
411 }
412}
413
414#[cfg(feature = "async")]
415#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
416#[pin_project(project_replace = CleanDirRefWrWriteFutureProjOwn)]
417#[doc(hidden)]
418pub enum CleanDirRefWrWriteFuture<'a, 'f, T, Vfs: WriteSupportingVfsAsync + 'static>
419where
420 T: WriteToAsyncRef<'a, Vfs> + ?Sized + 'a,
421 T::Future<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
422 <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
423 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
424 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
425 'a: 'f,
426{
427 Poison,
428 ExistsCheck(
429 <Vfs as VfsAsync>::ExistsFuture<'f>,
430 <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
431 Pin<&'a Vfs>,
432 &'f T,
433 ),
434 RemoveDirAll(
435 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>,
436 <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
437 Pin<&'f Vfs>,
438 &'f T,
439 ),
440 Write(
441 <T as WriteToAsyncRef<'a, Vfs>>::Future<'f>,
442 <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
443 Pin<&'f Vfs>,
444 ),
445}
446
447#[cfg(feature = "async")]
448#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
449impl<'a, 'f, T, Vfs: WriteSupportingVfsAsync + 'static> Future
450 for CleanDirRefWrWriteFuture<'a, 'f, T, Vfs>
451where
452 T: WriteToAsyncRef<'a, Vfs> + ?Sized + 'a,
453 T::Future<'f>: Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
454 <Vfs as VfsAsync>::ExistsFuture<'f>: Future<Output = VfsResult<bool, Vfs>> + Unpin + 'f,
455 <Vfs as WriteSupportingVfsAsync>::RemoveDirAllFuture<'f>:
456 Future<Output = VfsResult<(), Vfs>> + Unpin + 'f,
457 'a: 'f,
458{
459 type Output = VfsResult<(), Vfs>;
460
461 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
462 let this = self.as_mut().project_replace(Self::Poison);
463 match this {
464 CleanDirRefWrWriteFutureProjOwn::Poison => {
465 panic!("polled after completion")
466 }
467 CleanDirRefWrWriteFutureProjOwn::ExistsCheck(mut fut, path, vfs, v) => {
468 match Pin::new(&mut fut).poll(cx) {
469 Poll::Ready(Ok(exists)) => {
470 if exists {
471 let fut = vfs.remove_dir_all(path.clone());
472 self.project_replace(Self::RemoveDirAll(fut, path.clone(), vfs, v));
473 cx.waker().wake_by_ref();
474 Poll::Pending
475 } else {
476 let fut = T::write_to_async_ref(v, path.clone(), vfs);
477 self.project_replace(Self::Write(fut, path.clone(), vfs));
478 cx.waker().wake_by_ref();
479 Poll::Pending
480 }
481 }
482 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
483 Poll::Pending => {
484 self.project_replace(Self::ExistsCheck(fut, path, vfs, v));
485 Poll::Pending
486 }
487 }
488 }
489 CleanDirRefWrWriteFutureProjOwn::RemoveDirAll(mut fut, path, vfs, v) => {
490 match Pin::new(&mut fut).poll(cx) {
491 Poll::Ready(Ok(())) => {
492 let fut = T::write_to_async_ref(v, path.clone(), vfs);
493 self.project_replace(Self::Write(fut, path.clone(), vfs));
494 cx.waker().wake_by_ref();
495 Poll::Pending
496 }
497 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
498 Poll::Pending => {
499 self.project_replace(Self::RemoveDirAll(fut, path, vfs, v));
500 Poll::Pending
501 }
502 }
503 }
504 CleanDirRefWrWriteFutureProjOwn::Write(mut fut, _path, _vfs) => {
505 match Pin::new(&mut fut).poll(cx) {
506 Poll::Ready(v) => Poll::Ready(v),
507 Poll::Pending => {
508 self.project_replace(Self::Write(fut, _path, _vfs));
509 Poll::Pending
510 }
511 }
512 }
513 }
514 }
515}