cap_fs_ext/
dir_ext.rs

1#[cfg(feature = "fs_utf8")]
2use camino::Utf8Path;
3#[cfg(all(windows, feature = "async_std", feature = "fs_utf8"))]
4use cap_primitives::fs::stat;
5#[cfg(not(windows))]
6use cap_primitives::fs::symlink;
7use cap_primitives::fs::{
8    access, open_dir_nofollow, set_symlink_permissions, set_times, set_times_nofollow,
9    FollowSymlinks, Permissions,
10};
11#[cfg(windows)]
12use cap_primitives::fs::{symlink_dir, symlink_file};
13use io_lifetimes::AsFilelike;
14use std::io;
15use std::path::Path;
16#[cfg(feature = "async_std")]
17use {async_std::task::spawn_blocking, async_trait::async_trait};
18
19pub use cap_primitives::fs::{AccessType, SystemTimeSpec};
20
21/// Extension trait for `Dir`.
22pub trait DirExt {
23    /// Set the last access time for a file on a filesystem.
24    ///
25    /// This corresponds to [`filetime::set_file_atime`].
26    ///
27    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
28    fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
29
30    /// Set the last modification time for a file on a filesystem.
31    ///
32    /// This corresponds to [`filetime::set_file_mtime`].
33    ///
34    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
35    fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
36
37    /// Set the last access and modification times for a file on a filesystem.
38    ///
39    /// This corresponds to [`filetime::set_file_times`].
40    ///
41    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
42    fn set_times<P: AsRef<Path>>(
43        &self,
44        path: P,
45        atime: Option<SystemTimeSpec>,
46        mtime: Option<SystemTimeSpec>,
47    ) -> io::Result<()>;
48
49    /// Set the last access and modification times for a file on a filesystem.
50    /// This function does not follow symlink.
51    ///
52    /// This corresponds to [`filetime::set_symlink_file_times`].
53    ///
54    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
55    fn set_symlink_times<P: AsRef<Path>>(
56        &self,
57        path: P,
58        atime: Option<SystemTimeSpec>,
59        mtime: Option<SystemTimeSpec>,
60    ) -> io::Result<()>;
61
62    /// Creates a new symbolic link on a filesystem.
63    ///
64    /// This corresponds to [`std::os::unix::fs::symlink`], except that it's
65    /// supported on non-Unix platforms as well, and it's not guaranteed to be
66    /// atomic.
67    ///
68    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
69    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
70
71    /// Creates a new file symbolic link on a filesystem.
72    ///
73    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
74    /// it's supported on non-Windows platforms as well, and it's not
75    /// guaranteed to fail if the target is not a file.
76    ///
77    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
78    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
79
80    /// Creates a new directory symbolic link on a filesystem.
81    ///
82    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
83    /// it's supported on non-Windows platforms as well, and it's not
84    /// guaranteed to fail if the target is not a directory.
85    ///
86    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
87    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
88
89    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
90    /// symlink.
91    fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self>
92    where
93        Self: Sized;
94
95    /// Removes a file or symlink from a filesystem.
96    ///
97    /// Removal of symlinks has different behavior under Windows - if a symlink
98    /// points to a directory, it cannot be removed with the `remove_file`
99    /// operation. This method will remove files and all symlinks.
100    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
101
102    /// Test for accessibility or existence of a filesystem object.
103    fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
104
105    /// Test for accessibility or existence of a filesystem object, without
106    /// following symbolic links.
107    fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
108
109    /// Changes the permissions found on a file or a directory, without following
110    /// symbolic links.
111    fn set_symlink_permissions<P: AsRef<Path>>(&self, path: P, perm: Permissions)
112        -> io::Result<()>;
113}
114
115/// Extension trait for `Dir`, async.
116///
117/// The path parameters include `Send` for the `async_trait` macro.
118#[cfg(feature = "async_std")]
119#[async_trait]
120pub trait AsyncDirExt {
121    /// Set the last access time for a file on a filesystem.
122    ///
123    /// This corresponds to [`filetime::set_file_atime`].
124    ///
125    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
126    async fn set_atime<P: AsRef<async_std::path::Path> + Send>(
127        &self,
128        path: P,
129        atime: SystemTimeSpec,
130    ) -> io::Result<()>;
131
132    /// Set the last modification time for a file on a filesystem.
133    ///
134    /// This corresponds to [`filetime::set_file_mtime`].
135    ///
136    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
137    async fn set_mtime<P: AsRef<async_std::path::Path> + Send>(
138        &self,
139        path: P,
140        mtime: SystemTimeSpec,
141    ) -> io::Result<()>;
142
143    /// Set the last access and modification times for a file on a filesystem.
144    ///
145    /// This corresponds to [`filetime::set_file_times`].
146    ///
147    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
148    async fn set_times<P: AsRef<async_std::path::Path> + Send>(
149        &self,
150        path: P,
151        atime: Option<SystemTimeSpec>,
152        mtime: Option<SystemTimeSpec>,
153    ) -> io::Result<()>;
154
155    /// Set the last access and modification times for a file on a filesystem.
156    /// This function does not follow symlink.
157    ///
158    /// This corresponds to [`filetime::set_symlink_file_times`].
159    ///
160    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
161    async fn set_symlink_times<P: AsRef<async_std::path::Path> + Send>(
162        &self,
163        path: P,
164        atime: Option<SystemTimeSpec>,
165        mtime: Option<SystemTimeSpec>,
166    ) -> io::Result<()>;
167
168    /// Creates a new symbolic link on a filesystem.
169    ///
170    /// This corresponds to [`std::os::unix::fs::symlink`], except that
171    /// it's supported on non-Unix platforms as well, and it's not guaranteed
172    /// to be atomic.
173    ///
174    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
175    async fn symlink<
176        P: AsRef<async_std::path::Path> + Send,
177        Q: AsRef<async_std::path::Path> + Send,
178    >(
179        &self,
180        src: P,
181        dst: Q,
182    ) -> io::Result<()>;
183
184    /// Creates a new file symbolic link on a filesystem.
185    ///
186    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
187    /// it's supported on non-Windows platforms as well, and it's not
188    /// guaranteed to fail if the target is not a file.
189    ///
190    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
191    async fn symlink_file<
192        P: AsRef<async_std::path::Path> + Send,
193        Q: AsRef<async_std::path::Path> + Send,
194    >(
195        &self,
196        src: P,
197        dst: Q,
198    ) -> io::Result<()>;
199
200    /// Creates a new directory symbolic link on a filesystem.
201    ///
202    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
203    /// it's supported on non-Windows platforms as well, and it's not
204    /// guaranteed to fail if the target is not a directory.
205    ///
206    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
207    async fn symlink_dir<
208        P: AsRef<async_std::path::Path> + Send,
209        Q: AsRef<async_std::path::Path> + Send,
210    >(
211        &self,
212        src: P,
213        dst: Q,
214    ) -> io::Result<()>;
215
216    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
217    /// symlink.
218    async fn open_dir_nofollow<P: AsRef<async_std::path::Path> + Send>(
219        &self,
220        path: P,
221    ) -> io::Result<Self>
222    where
223        Self: Sized;
224
225    /// Removes a file or symlink from a filesystem.
226    ///
227    /// Removal of symlinks has different behavior under Windows - if a symlink
228    /// points to a directory, it cannot be removed with the `remove_file`
229    /// operation. This method will remove files and all symlinks.
230    async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
231        &self,
232        path: P,
233    ) -> io::Result<()>;
234
235    /// Test for accessibility or existence of a filesystem object.
236    async fn access<P: AsRef<async_std::path::Path> + Send>(
237        &self,
238        path: P,
239        type_: AccessType,
240    ) -> io::Result<()>;
241
242    /// Test for accessibility or existence of a filesystem object, without
243    /// following symbolic links.
244    async fn access_symlink<P: AsRef<async_std::path::Path> + Send>(
245        &self,
246        path: P,
247        type_: AccessType,
248    ) -> io::Result<()>;
249
250    /// Changes the permissions found on a file or a directory, without following
251    /// symbolic links.
252    async fn set_symlink_permissions<P: AsRef<async_std::path::Path> + Send>(
253        &self,
254        path: P,
255        perm: Permissions,
256    ) -> io::Result<()>;
257}
258
259/// `fs_utf8` version of `DirExt`.
260#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
261pub trait DirExtUtf8 {
262    /// Set the last access time for a file on a filesystem.
263    ///
264    /// This corresponds to [`filetime::set_file_atime`].
265    ///
266    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
267    fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
268
269    /// Set the last modification time for a file on a filesystem.
270    ///
271    /// This corresponds to [`filetime::set_file_mtime`].
272    ///
273    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
274    fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
275
276    /// Set the last access and modification times for a file on a filesystem.
277    ///
278    /// This corresponds to [`filetime::set_file_times`].
279    ///
280    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
281    fn set_times<P: AsRef<Utf8Path>>(
282        &self,
283        path: P,
284        atime: Option<SystemTimeSpec>,
285        mtime: Option<SystemTimeSpec>,
286    ) -> io::Result<()>;
287
288    /// Set the last access and modification times for a file on a filesystem.
289    /// This function does not follow symlink.
290    ///
291    /// This corresponds to [`filetime::set_symlink_file_times`].
292    ///
293    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
294    fn set_symlink_times<P: AsRef<Utf8Path>>(
295        &self,
296        path: P,
297        atime: Option<SystemTimeSpec>,
298        mtime: Option<SystemTimeSpec>,
299    ) -> io::Result<()>;
300
301    /// Creates a new symbolic link on a filesystem.
302    ///
303    /// This corresponds to [`std::os::unix::fs::symlink`], except that it's
304    /// supported on non-Unix platforms as well, and it's not guaranteed to be
305    /// atomic.
306    ///
307    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
308    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()>;
309
310    /// Creates a new file symbolic link on a filesystem.
311    ///
312    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
313    /// it's supported on non-Windows platforms as well, and it's not
314    /// guaranteed to fail if the target is not a file.
315    ///
316    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
317    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
318        &self,
319        src: P,
320        dst: Q,
321    ) -> io::Result<()>;
322
323    /// Creates a new directory symbolic link on a filesystem.
324    ///
325    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
326    /// it's supported on non-Windows platforms as well, and it's not
327    /// guaranteed to fail if the target is not a directory.
328    ///
329    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
330    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q)
331        -> io::Result<()>;
332
333    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
334    /// symlink.
335    fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self>
336    where
337        Self: Sized;
338
339    /// Removes a file or symlink from a filesystem.
340    ///
341    /// This is similar to [`std::fs::remove_file`], except that it also works
342    /// on symlinks to directories on Windows, similar to how `unlink` works
343    /// on symlinks to directories on Posix-ish platforms.
344    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()>;
345
346    /// Test for accessibility or existence of a filesystem object.
347    fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
348
349    /// Test for accessibility or existence of a filesystem object, without
350    /// following symbolic links.
351    fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
352
353    /// Changes the permissions found on a file or a directory, without following
354    /// symbolic links.
355    fn set_symlink_permissions<P: AsRef<Utf8Path>>(
356        &self,
357        path: P,
358        perm: Permissions,
359    ) -> io::Result<()>;
360}
361
362/// `fs_utf8` version of `DirExt`.
363///
364/// The path parameters include `Send` for the `async_trait` macro.
365#[cfg(all(feature = "async_std", feature = "fs_utf8"))]
366#[async_trait]
367pub trait AsyncDirExtUtf8 {
368    /// Set the last access time for a file on a filesystem.
369    ///
370    /// This corresponds to [`filetime::set_file_atime`].
371    ///
372    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
373    async fn set_atime<P: AsRef<Utf8Path> + Send>(
374        &self,
375        path: P,
376        atime: SystemTimeSpec,
377    ) -> io::Result<()>;
378
379    /// Set the last modification time for a file on a filesystem.
380    ///
381    /// This corresponds to [`filetime::set_file_mtime`].
382    ///
383    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
384    async fn set_mtime<P: AsRef<Utf8Path> + Send>(
385        &self,
386        path: P,
387        mtime: SystemTimeSpec,
388    ) -> io::Result<()>;
389
390    /// Set the last access and modification times for a file on a filesystem.
391    ///
392    /// This corresponds to [`filetime::set_file_times`].
393    ///
394    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
395    async fn set_times<P: AsRef<Utf8Path> + Send>(
396        &self,
397        path: P,
398        atime: Option<SystemTimeSpec>,
399        mtime: Option<SystemTimeSpec>,
400    ) -> io::Result<()>;
401
402    /// Set the last access and modification times for a file on a filesystem.
403    /// This function does not follow symlink.
404    ///
405    /// This corresponds to [`filetime::set_symlink_file_times`].
406    ///
407    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
408    async fn set_symlink_times<P: AsRef<Utf8Path> + Send>(
409        &self,
410        path: P,
411        atime: Option<SystemTimeSpec>,
412        mtime: Option<SystemTimeSpec>,
413    ) -> io::Result<()>;
414
415    /// Creates a new symbolic link on a filesystem.
416    ///
417    /// This corresponds to [`std::os::unix::fs::symlink`], except that
418    /// it's supported on non-Unix platforms as well, and it's not guaranteed
419    /// to be atomic.
420    ///
421    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
422    async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
423        &self,
424        src: P,
425        dst: Q,
426    ) -> io::Result<()>;
427
428    /// Creates a new file symbolic link on a filesystem.
429    ///
430    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
431    /// it's supported on non-Windows platforms as well, and it's not
432    /// guaranteed to fail if the target is not a file.
433    ///
434    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
435    async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
436        &self,
437        src: P,
438        dst: Q,
439    ) -> io::Result<()>;
440
441    /// Creates a new directory symbolic link on a filesystem.
442    ///
443    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
444    /// it's supported on non-Windows platforms as well, and it's not
445    /// guaranteed to fail if the target is not a directory.
446    ///
447    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
448    async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
449        &self,
450        src: P,
451        dst: Q,
452    ) -> io::Result<()>;
453
454    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
455    /// symlink.
456    async fn open_dir_nofollow<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<Self>
457    where
458        Self: Sized;
459
460    /// Removes a file or symlink from a filesystem.
461    ///
462    /// Removal of symlinks has different behavior under Windows - if a symlink
463    /// points to a directory, it cannot be removed with the `remove_file`
464    /// operation. This method will remove files and all symlinks.
465    async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()>;
466
467    /// Test for accessibility or existence of a filesystem object.
468    async fn access<P: AsRef<Utf8Path> + Send>(&self, path: P, type_: AccessType)
469        -> io::Result<()>;
470
471    /// Test for accessibility or existence of a filesystem object, without
472    /// following symbolic links.
473    async fn access_symlink<P: AsRef<Utf8Path> + Send>(
474        &self,
475        path: P,
476        type_: AccessType,
477    ) -> io::Result<()>;
478
479    /// Changes the permissions found on a file or a directory, without following
480    /// symbolic links.
481    async fn set_symlink_permissions<P: AsRef<Utf8Path> + Send>(
482        &self,
483        path: P,
484        perm: Permissions,
485    ) -> io::Result<()>;
486}
487
488#[cfg(feature = "std")]
489impl DirExt for cap_std::fs::Dir {
490    #[inline]
491    fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
492        set_times(
493            &self.as_filelike_view::<std::fs::File>(),
494            path.as_ref(),
495            Some(atime),
496            None,
497        )
498    }
499
500    #[inline]
501    fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
502        set_times(
503            &self.as_filelike_view::<std::fs::File>(),
504            path.as_ref(),
505            None,
506            Some(mtime),
507        )
508    }
509
510    #[inline]
511    fn set_times<P: AsRef<Path>>(
512        &self,
513        path: P,
514        atime: Option<SystemTimeSpec>,
515        mtime: Option<SystemTimeSpec>,
516    ) -> io::Result<()> {
517        set_times(
518            &self.as_filelike_view::<std::fs::File>(),
519            path.as_ref(),
520            atime,
521            mtime,
522        )
523    }
524
525    #[inline]
526    fn set_symlink_times<P: AsRef<Path>>(
527        &self,
528        path: P,
529        atime: Option<SystemTimeSpec>,
530        mtime: Option<SystemTimeSpec>,
531    ) -> io::Result<()> {
532        set_times_nofollow(
533            &self.as_filelike_view::<std::fs::File>(),
534            path.as_ref(),
535            atime,
536            mtime,
537        )
538    }
539
540    #[cfg(not(windows))]
541    #[inline]
542    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
543        symlink(
544            src.as_ref(),
545            &self.as_filelike_view::<std::fs::File>(),
546            dst.as_ref(),
547        )
548    }
549
550    #[cfg(not(windows))]
551    #[inline]
552    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
553        self.symlink(src, dst)
554    }
555
556    #[cfg(not(windows))]
557    #[inline]
558    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
559        self.symlink(src, dst)
560    }
561
562    #[cfg(windows)]
563    #[inline]
564    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
565        if self.metadata(src.as_ref())?.is_dir() {
566            self.symlink_dir(src, dst)
567        } else {
568            self.symlink_file(src, dst)
569        }
570    }
571
572    #[cfg(windows)]
573    #[inline]
574    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
575        symlink_file(
576            src.as_ref(),
577            &self.as_filelike_view::<std::fs::File>(),
578            dst.as_ref(),
579        )
580    }
581
582    #[cfg(windows)]
583    #[inline]
584    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
585        symlink_dir(
586            src.as_ref(),
587            &self.as_filelike_view::<std::fs::File>(),
588            dst.as_ref(),
589        )
590    }
591
592    #[inline]
593    fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
594        match open_dir_nofollow(&self.as_filelike_view::<std::fs::File>(), path.as_ref()) {
595            Ok(file) => Ok(Self::from_std_file(file)),
596            Err(e) => Err(e),
597        }
598    }
599
600    #[cfg(not(windows))]
601    #[inline]
602    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
603        self.remove_file(path.as_ref())
604    }
605
606    #[cfg(windows)]
607    #[inline]
608    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
609        use crate::OpenOptionsFollowExt;
610        use cap_primitives::fs::_WindowsByHandle;
611        use cap_std::fs::{OpenOptions, OpenOptionsExt};
612        use windows_sys::Win32::Storage::FileSystem::{
613            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
614            FILE_FLAG_OPEN_REPARSE_POINT,
615        };
616        let path = path.as_ref();
617
618        let mut opts = OpenOptions::new();
619        opts.access_mode(DELETE);
620        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
621        opts.follow(FollowSymlinks::No);
622        let file = self.open_with(path, &opts)?;
623
624        let meta = file.metadata()?;
625        if meta.file_type().is_symlink()
626            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
627        {
628            self.remove_dir(path)?;
629        } else {
630            self.remove_file(path)?;
631        }
632
633        // Drop the file after calling `remove_file` or `remove_dir`, since
634        // Windows doesn't actually remove the file until after the last open
635        // handle is closed, and this protects us from race conditions where
636        // other processes replace the file out from underneath us.
637        drop(file);
638
639        Ok(())
640    }
641
642    /// Test for accessibility or existence of a filesystem object.
643    fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
644        access(
645            &self.as_filelike_view::<std::fs::File>(),
646            path.as_ref(),
647            type_,
648            FollowSymlinks::Yes,
649        )
650    }
651
652    /// Test for accessibility or existence of a filesystem object.
653    fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
654        access(
655            &self.as_filelike_view::<std::fs::File>(),
656            path.as_ref(),
657            type_,
658            FollowSymlinks::No,
659        )
660    }
661
662    /// Changes the permissions found on a file or a directory, without following
663    /// symbolic links.
664    fn set_symlink_permissions<P: AsRef<Path>>(
665        &self,
666        path: P,
667        perm: Permissions,
668    ) -> io::Result<()> {
669        set_symlink_permissions(
670            &self.as_filelike_view::<std::fs::File>(),
671            path.as_ref(),
672            perm,
673        )
674    }
675}
676
677#[cfg(feature = "async_std")]
678#[async_trait]
679impl AsyncDirExt for cap_async_std::fs::Dir {
680    #[inline]
681    async fn set_atime<P: AsRef<async_std::path::Path> + Send>(
682        &self,
683        path: P,
684        atime: SystemTimeSpec,
685    ) -> io::Result<()> {
686        let path = path.as_ref().to_path_buf();
687        let clone = self.clone();
688        spawn_blocking(move || {
689            set_times(
690                &clone.as_filelike_view::<std::fs::File>(),
691                path.as_ref(),
692                Some(atime),
693                None,
694            )
695        })
696        .await
697    }
698
699    #[inline]
700    async fn set_mtime<P: AsRef<async_std::path::Path> + Send>(
701        &self,
702        path: P,
703        mtime: SystemTimeSpec,
704    ) -> io::Result<()> {
705        let path = path.as_ref().to_path_buf();
706        let clone = self.clone();
707        spawn_blocking(move || {
708            set_times(
709                &clone.as_filelike_view::<std::fs::File>(),
710                path.as_ref(),
711                None,
712                Some(mtime),
713            )
714        })
715        .await
716    }
717
718    #[inline]
719    async fn set_times<P: AsRef<async_std::path::Path> + Send>(
720        &self,
721        path: P,
722        atime: Option<SystemTimeSpec>,
723        mtime: Option<SystemTimeSpec>,
724    ) -> io::Result<()> {
725        let path = path.as_ref().to_path_buf();
726        let clone = self.clone();
727        spawn_blocking(move || {
728            set_times(
729                &clone.as_filelike_view::<std::fs::File>(),
730                path.as_ref(),
731                atime,
732                mtime,
733            )
734        })
735        .await
736    }
737
738    #[inline]
739    async fn set_symlink_times<P: AsRef<async_std::path::Path> + Send>(
740        &self,
741        path: P,
742        atime: Option<SystemTimeSpec>,
743        mtime: Option<SystemTimeSpec>,
744    ) -> io::Result<()> {
745        let path = path.as_ref().to_path_buf();
746        let clone = self.clone();
747        spawn_blocking(move || {
748            set_times_nofollow(
749                &clone.as_filelike_view::<std::fs::File>(),
750                path.as_ref(),
751                atime,
752                mtime,
753            )
754        })
755        .await
756    }
757
758    #[cfg(not(windows))]
759    #[inline]
760    async fn symlink<
761        P: AsRef<async_std::path::Path> + Send,
762        Q: AsRef<async_std::path::Path> + Send,
763    >(
764        &self,
765        src: P,
766        dst: Q,
767    ) -> io::Result<()> {
768        let src = src.as_ref().to_path_buf();
769        let dst = dst.as_ref().to_path_buf();
770        let clone = self.clone();
771        spawn_blocking(move || {
772            symlink(
773                src.as_ref(),
774                &clone.as_filelike_view::<std::fs::File>(),
775                dst.as_ref(),
776            )
777        })
778        .await
779    }
780
781    #[cfg(not(windows))]
782    #[inline]
783    async fn symlink_file<
784        P: AsRef<async_std::path::Path> + Send,
785        Q: AsRef<async_std::path::Path> + Send,
786    >(
787        &self,
788        src: P,
789        dst: Q,
790    ) -> io::Result<()> {
791        let src = src.as_ref().to_path_buf();
792        let dst = dst.as_ref().to_path_buf();
793        let clone = self.clone();
794        spawn_blocking(move || {
795            symlink(
796                src.as_ref(),
797                &clone.as_filelike_view::<std::fs::File>(),
798                dst.as_ref(),
799            )
800        })
801        .await
802    }
803
804    #[cfg(not(windows))]
805    #[inline]
806    async fn symlink_dir<
807        P: AsRef<async_std::path::Path> + Send,
808        Q: AsRef<async_std::path::Path> + Send,
809    >(
810        &self,
811        src: P,
812        dst: Q,
813    ) -> io::Result<()> {
814        let src = src.as_ref().to_path_buf();
815        let dst = dst.as_ref().to_path_buf();
816        let clone = self.clone();
817        spawn_blocking(move || {
818            symlink(
819                src.as_ref(),
820                &clone.as_filelike_view::<std::fs::File>(),
821                dst.as_ref(),
822            )
823        })
824        .await
825    }
826
827    #[cfg(windows)]
828    #[inline]
829    async fn symlink<
830        P: AsRef<async_std::path::Path> + Send,
831        Q: AsRef<async_std::path::Path> + Send,
832    >(
833        &self,
834        src: P,
835        dst: Q,
836    ) -> io::Result<()> {
837        let src = src.as_ref().to_path_buf();
838        let dst = dst.as_ref().to_path_buf();
839        let clone = self.clone();
840        if self.metadata(&src).await?.is_dir() {
841            spawn_blocking(move || {
842                symlink_dir(
843                    src.as_ref(),
844                    &clone.as_filelike_view::<std::fs::File>(),
845                    dst.as_ref(),
846                )
847            })
848            .await
849        } else {
850            spawn_blocking(move || {
851                symlink_file(
852                    src.as_ref(),
853                    &clone.as_filelike_view::<std::fs::File>(),
854                    dst.as_ref(),
855                )
856            })
857            .await
858        }
859    }
860
861    #[cfg(windows)]
862    #[inline]
863    async fn symlink_file<
864        P: AsRef<async_std::path::Path> + Send,
865        Q: AsRef<async_std::path::Path> + Send,
866    >(
867        &self,
868        src: P,
869        dst: Q,
870    ) -> io::Result<()> {
871        let src = src.as_ref().to_path_buf();
872        let dst = dst.as_ref().to_path_buf();
873        let clone = self.clone();
874        spawn_blocking(move || {
875            symlink_file(
876                src.as_ref(),
877                &clone.as_filelike_view::<std::fs::File>(),
878                dst.as_ref(),
879            )
880        })
881        .await
882    }
883
884    #[cfg(windows)]
885    #[inline]
886    async fn symlink_dir<
887        P: AsRef<async_std::path::Path> + Send,
888        Q: AsRef<async_std::path::Path> + Send,
889    >(
890        &self,
891        src: P,
892        dst: Q,
893    ) -> io::Result<()> {
894        let src = src.as_ref().to_path_buf();
895        let dst = dst.as_ref().to_path_buf();
896        let clone = self.clone();
897        spawn_blocking(move || {
898            symlink_dir(
899                src.as_ref(),
900                &clone.as_filelike_view::<std::fs::File>(),
901                dst.as_ref(),
902            )
903        })
904        .await
905    }
906
907    #[inline]
908    async fn open_dir_nofollow<P: AsRef<async_std::path::Path> + Send>(
909        &self,
910        path: P,
911    ) -> io::Result<Self> {
912        let path = path.as_ref().to_path_buf();
913        let clone = self.clone();
914        spawn_blocking(move || {
915            match open_dir_nofollow(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()) {
916                Ok(file) => Ok(Self::from_std_file(file.into())),
917                Err(e) => Err(e),
918            }
919        })
920        .await
921    }
922
923    #[cfg(not(windows))]
924    #[inline]
925    async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
926        &self,
927        path: P,
928    ) -> io::Result<()> {
929        self.remove_file(path).await
930    }
931
932    #[cfg(windows)]
933    #[inline]
934    async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
935        &self,
936        path: P,
937    ) -> io::Result<()> {
938        use crate::OpenOptionsFollowExt;
939        use cap_primitives::fs::_WindowsByHandle;
940        use cap_std::fs::{OpenOptions, OpenOptionsExt};
941        use windows_sys::Win32::Storage::FileSystem::{
942            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
943            FILE_FLAG_OPEN_REPARSE_POINT,
944        };
945        let path = path.as_ref();
946
947        let mut opts = OpenOptions::new();
948        opts.access_mode(DELETE);
949        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
950        opts.follow(FollowSymlinks::No);
951        let file = self.open_with(path, &opts).await?;
952
953        let meta = file.metadata().await?;
954        if meta.file_type().is_symlink()
955            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
956        {
957            self.remove_dir(path).await?;
958        } else {
959            self.remove_file(path).await?;
960        }
961
962        // Drop the file after calling `remove_file` or `remove_dir`, since
963        // Windows doesn't actually remove the file until after the last open
964        // handle is closed, and this protects us from race conditions where
965        // other processes replace the file out from underneath us.
966        drop(file);
967
968        Ok(())
969    }
970
971    /// Test for accessibility or existence of a filesystem object.
972    async fn access<P: AsRef<async_std::path::Path> + Send>(
973        &self,
974        path: P,
975        type_: AccessType,
976    ) -> io::Result<()> {
977        let path = path.as_ref().to_path_buf();
978        let clone = self.clone();
979        spawn_blocking(move || {
980            access(
981                &clone.as_filelike_view::<std::fs::File>(),
982                path.as_ref(),
983                type_,
984                FollowSymlinks::Yes,
985            )
986        })
987        .await
988    }
989
990    /// Test for accessibility or existence of a filesystem object, without
991    /// following symbolic links.
992    async fn access_symlink<P: AsRef<async_std::path::Path> + Send>(
993        &self,
994        path: P,
995        type_: AccessType,
996    ) -> io::Result<()> {
997        let path = path.as_ref().to_path_buf();
998        let clone = self.clone();
999        spawn_blocking(move || {
1000            access(
1001                &clone.as_filelike_view::<std::fs::File>(),
1002                path.as_ref(),
1003                type_,
1004                FollowSymlinks::No,
1005            )
1006        })
1007        .await
1008    }
1009
1010    /// Changes the permissions found on a file or a directory, without following
1011    /// symbolic links.
1012    async fn set_symlink_permissions<P: AsRef<async_std::path::Path> + Send>(
1013        &self,
1014        path: P,
1015        perm: Permissions,
1016    ) -> io::Result<()> {
1017        let path = path.as_ref().to_path_buf();
1018        let clone = self.clone();
1019        spawn_blocking(move || {
1020            set_symlink_permissions(
1021                &clone.as_filelike_view::<std::fs::File>(),
1022                path.as_ref(),
1023                perm,
1024            )
1025        })
1026        .await
1027    }
1028}
1029
1030#[cfg(all(feature = "std", feature = "fs_utf8"))]
1031impl DirExtUtf8 for cap_std::fs_utf8::Dir {
1032    #[inline]
1033    fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
1034        let path = from_utf8(path.as_ref())?;
1035        set_times(
1036            &self.as_filelike_view::<std::fs::File>(),
1037            &path,
1038            Some(atime),
1039            None,
1040        )
1041    }
1042
1043    #[inline]
1044    fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
1045        let path = from_utf8(path.as_ref())?;
1046        set_times(
1047            &self.as_filelike_view::<std::fs::File>(),
1048            &path,
1049            None,
1050            Some(mtime),
1051        )
1052    }
1053
1054    #[inline]
1055    fn set_times<P: AsRef<Utf8Path>>(
1056        &self,
1057        path: P,
1058        atime: Option<SystemTimeSpec>,
1059        mtime: Option<SystemTimeSpec>,
1060    ) -> io::Result<()> {
1061        let path = from_utf8(path.as_ref())?;
1062        set_times(
1063            &self.as_filelike_view::<std::fs::File>(),
1064            &path,
1065            atime,
1066            mtime,
1067        )
1068    }
1069
1070    #[inline]
1071    fn set_symlink_times<P: AsRef<Utf8Path>>(
1072        &self,
1073        path: P,
1074        atime: Option<SystemTimeSpec>,
1075        mtime: Option<SystemTimeSpec>,
1076    ) -> io::Result<()> {
1077        let path = from_utf8(path.as_ref())?;
1078        set_times_nofollow(
1079            &self.as_filelike_view::<std::fs::File>(),
1080            &path,
1081            atime,
1082            mtime,
1083        )
1084    }
1085
1086    #[cfg(not(windows))]
1087    #[inline]
1088    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
1089        Self::symlink(self, src, dst)
1090    }
1091
1092    #[cfg(not(windows))]
1093    #[inline]
1094    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1095        &self,
1096        src: P,
1097        dst: Q,
1098    ) -> io::Result<()> {
1099        Self::symlink(self, src, dst)
1100    }
1101
1102    #[cfg(not(windows))]
1103    #[inline]
1104    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1105        &self,
1106        src: P,
1107        dst: Q,
1108    ) -> io::Result<()> {
1109        Self::symlink(self, src, dst)
1110    }
1111
1112    #[cfg(windows)]
1113    #[inline]
1114    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
1115        if self.metadata(src.as_ref())?.is_dir() {
1116            Self::symlink_dir(self, src, dst)
1117        } else {
1118            Self::symlink_file(self, src, dst)
1119        }
1120    }
1121
1122    #[cfg(windows)]
1123    #[inline]
1124    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1125        &self,
1126        src: P,
1127        dst: Q,
1128    ) -> io::Result<()> {
1129        Self::symlink_file(self, src, dst)
1130    }
1131
1132    #[cfg(windows)]
1133    #[inline]
1134    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1135        &self,
1136        src: P,
1137        dst: Q,
1138    ) -> io::Result<()> {
1139        Self::symlink_dir(self, src, dst)
1140    }
1141
1142    #[inline]
1143    fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self> {
1144        match open_dir_nofollow(
1145            &self.as_filelike_view::<std::fs::File>(),
1146            path.as_ref().as_ref(),
1147        ) {
1148            Ok(file) => Ok(Self::from_std_file(file)),
1149            Err(e) => Err(e),
1150        }
1151    }
1152
1153    #[cfg(not(windows))]
1154    #[inline]
1155    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
1156        self.remove_file(path.as_ref())
1157    }
1158
1159    #[cfg(windows)]
1160    #[inline]
1161    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
1162        use crate::OpenOptionsFollowExt;
1163        use cap_primitives::fs::_WindowsByHandle;
1164        use cap_std::fs::{OpenOptions, OpenOptionsExt};
1165        use windows_sys::Win32::Storage::FileSystem::{
1166            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
1167            FILE_FLAG_OPEN_REPARSE_POINT,
1168        };
1169        let path = path.as_ref();
1170
1171        let mut opts = OpenOptions::new();
1172        opts.access_mode(DELETE);
1173        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
1174        opts.follow(FollowSymlinks::No);
1175        let file = self.open_with(path, &opts)?;
1176
1177        let meta = file.metadata()?;
1178        if meta.file_type().is_symlink()
1179            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
1180        {
1181            self.remove_dir(path)?;
1182        } else {
1183            self.remove_file(path)?;
1184        }
1185
1186        // Drop the file after calling `remove_file` or `remove_dir`, since
1187        // Windows doesn't actually remove the file until after the last open
1188        // handle is closed, and this protects us from race conditions where
1189        // other processes replace the file out from underneath us.
1190        drop(file);
1191
1192        Ok(())
1193    }
1194
1195    /// Test for accessibility or existence of a filesystem object.
1196    fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
1197        access(
1198            &self.as_filelike_view::<std::fs::File>(),
1199            path.as_ref().as_ref(),
1200            type_,
1201            FollowSymlinks::Yes,
1202        )
1203    }
1204
1205    /// Test for accessibility or existence of a filesystem object.
1206    fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
1207        access(
1208            &self.as_filelike_view::<std::fs::File>(),
1209            path.as_ref().as_ref(),
1210            type_,
1211            FollowSymlinks::No,
1212        )
1213    }
1214
1215    /// Changes the permissions found on a file or a directory, without following
1216    /// symbolic links.
1217    fn set_symlink_permissions<P: AsRef<Utf8Path>>(
1218        &self,
1219        path: P,
1220        perm: Permissions,
1221    ) -> io::Result<()> {
1222        set_symlink_permissions(
1223            &self.as_filelike_view::<std::fs::File>(),
1224            path.as_ref().as_ref(),
1225            perm,
1226        )
1227    }
1228}
1229
1230#[cfg(all(feature = "async_std", feature = "fs_utf8"))]
1231#[async_trait]
1232impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir {
1233    #[inline]
1234    async fn set_atime<P: AsRef<Utf8Path> + Send>(
1235        &self,
1236        path: P,
1237        atime: SystemTimeSpec,
1238    ) -> io::Result<()> {
1239        let path = from_utf8(path.as_ref())?;
1240        let clone = self.clone();
1241        spawn_blocking(move || {
1242            set_times(
1243                &clone.as_filelike_view::<std::fs::File>(),
1244                &path,
1245                Some(atime),
1246                None,
1247            )
1248        })
1249        .await
1250    }
1251
1252    #[inline]
1253    async fn set_mtime<P: AsRef<Utf8Path> + Send>(
1254        &self,
1255        path: P,
1256        mtime: SystemTimeSpec,
1257    ) -> io::Result<()> {
1258        let path = from_utf8(path.as_ref())?;
1259        let clone = self.clone();
1260        spawn_blocking(move || {
1261            set_times(
1262                &clone.as_filelike_view::<std::fs::File>(),
1263                &path,
1264                None,
1265                Some(mtime),
1266            )
1267        })
1268        .await
1269    }
1270
1271    #[inline]
1272    async fn set_times<P: AsRef<Utf8Path> + Send>(
1273        &self,
1274        path: P,
1275        atime: Option<SystemTimeSpec>,
1276        mtime: Option<SystemTimeSpec>,
1277    ) -> io::Result<()> {
1278        let path = from_utf8(path.as_ref())?;
1279        let clone = self.clone();
1280        spawn_blocking(move || {
1281            set_times(
1282                &clone.as_filelike_view::<std::fs::File>(),
1283                &path,
1284                atime,
1285                mtime,
1286            )
1287        })
1288        .await
1289    }
1290
1291    #[inline]
1292    async fn set_symlink_times<P: AsRef<Utf8Path> + Send>(
1293        &self,
1294        path: P,
1295        atime: Option<SystemTimeSpec>,
1296        mtime: Option<SystemTimeSpec>,
1297    ) -> io::Result<()> {
1298        let path = from_utf8(path.as_ref())?;
1299        let clone = self.clone();
1300        spawn_blocking(move || {
1301            set_times_nofollow(
1302                &clone.as_filelike_view::<std::fs::File>(),
1303                &path,
1304                atime,
1305                mtime,
1306            )
1307        })
1308        .await
1309    }
1310
1311    #[cfg(not(windows))]
1312    #[inline]
1313    async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1314        &self,
1315        src: P,
1316        dst: Q,
1317    ) -> io::Result<()> {
1318        let src = from_utf8(src.as_ref())?;
1319        let dst = from_utf8(dst.as_ref())?;
1320        let clone = self.clone();
1321        spawn_blocking(move || symlink(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1322            .await
1323    }
1324
1325    #[cfg(not(windows))]
1326    #[inline]
1327    async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1328        &self,
1329        src: P,
1330        dst: Q,
1331    ) -> io::Result<()> {
1332        self.symlink(src, dst).await
1333    }
1334
1335    #[cfg(not(windows))]
1336    #[inline]
1337    async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1338        &self,
1339        src: P,
1340        dst: Q,
1341    ) -> io::Result<()> {
1342        self.symlink(src, dst).await
1343    }
1344
1345    #[cfg(windows)]
1346    #[inline]
1347    async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1348        &self,
1349        src: P,
1350        dst: Q,
1351    ) -> io::Result<()> {
1352        let src = from_utf8(src.as_ref())?;
1353        let src_ = src.clone();
1354        let dst = from_utf8(dst.as_ref())?;
1355        let clone = self.clone();
1356        // Call `stat` directly to avoid `async_trait` capturing `self`.
1357        let metadata = spawn_blocking(move || {
1358            stat(
1359                &clone.as_filelike_view::<std::fs::File>(),
1360                &src_,
1361                FollowSymlinks::Yes,
1362            )
1363        })
1364        .await?;
1365        let clone = self.clone();
1366        if metadata.is_dir() {
1367            spawn_blocking(move || {
1368                symlink_dir(&src, &clone.as_filelike_view::<std::fs::File>(), &dst)
1369            })
1370            .await
1371        } else {
1372            spawn_blocking(move || {
1373                symlink_file(&src, &clone.as_filelike_view::<std::fs::File>(), &dst)
1374            })
1375            .await
1376        }
1377    }
1378
1379    #[cfg(windows)]
1380    #[inline]
1381    async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1382        &self,
1383        src: P,
1384        dst: Q,
1385    ) -> io::Result<()> {
1386        let src = from_utf8(src.as_ref())?;
1387        let dst = from_utf8(dst.as_ref())?;
1388        let clone = self.clone();
1389        spawn_blocking(move || symlink_file(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1390            .await
1391    }
1392
1393    #[cfg(windows)]
1394    #[inline]
1395    async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1396        &self,
1397        src: P,
1398        dst: Q,
1399    ) -> io::Result<()> {
1400        let src = from_utf8(src.as_ref())?;
1401        let dst = from_utf8(dst.as_ref())?;
1402        let clone = self.clone();
1403        spawn_blocking(move || symlink_dir(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1404            .await
1405    }
1406
1407    #[inline]
1408    async fn open_dir_nofollow<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<Self> {
1409        let path = from_utf8(path.as_ref())?;
1410        let clone = self.clone();
1411        spawn_blocking(move || {
1412            match open_dir_nofollow(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()) {
1413                Ok(file) => Ok(Self::from_std_file(file.into())),
1414                Err(e) => Err(e),
1415            }
1416        })
1417        .await
1418    }
1419
1420    #[cfg(not(windows))]
1421    #[inline]
1422    async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()> {
1423        self.remove_file(path).await
1424    }
1425
1426    #[cfg(windows)]
1427    #[inline]
1428    async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()> {
1429        use crate::{FollowSymlinks, OpenOptionsFollowExt};
1430        use cap_primitives::fs::_WindowsByHandle;
1431        use cap_std::fs::{OpenOptions, OpenOptionsExt};
1432        use windows_sys::Win32::Storage::FileSystem::{
1433            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
1434            FILE_FLAG_OPEN_REPARSE_POINT,
1435        };
1436        let path = path.as_ref();
1437
1438        let mut opts = OpenOptions::new();
1439        opts.access_mode(DELETE);
1440        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
1441        opts.follow(FollowSymlinks::No);
1442        let file = self.open_with(path, &opts).await?;
1443
1444        let meta = file.metadata().await?;
1445        if meta.file_type().is_symlink()
1446            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
1447        {
1448            self.remove_dir(path).await?;
1449        } else {
1450            self.remove_file(path).await?;
1451        }
1452
1453        // Drop the file after calling `remove_file` or `remove_dir`, since
1454        // Windows doesn't actually remove the file until after the last open
1455        // handle is closed, and this protects us from race conditions where
1456        // other processes replace the file out from underneath us.
1457        drop(file);
1458
1459        Ok(())
1460    }
1461
1462    async fn access<P: AsRef<Utf8Path> + Send>(
1463        &self,
1464        path: P,
1465        type_: AccessType,
1466    ) -> io::Result<()> {
1467        let path = from_utf8(path.as_ref())?;
1468        let clone = self.clone();
1469        spawn_blocking(move || {
1470            access(
1471                &clone.as_filelike_view::<std::fs::File>(),
1472                path.as_ref(),
1473                type_,
1474                FollowSymlinks::Yes,
1475            )
1476        })
1477        .await
1478    }
1479
1480    async fn access_symlink<P: AsRef<Utf8Path> + Send>(
1481        &self,
1482        path: P,
1483        type_: AccessType,
1484    ) -> io::Result<()> {
1485        let path = from_utf8(path.as_ref())?;
1486        let clone = self.clone();
1487        spawn_blocking(move || {
1488            access(
1489                &clone.as_filelike_view::<std::fs::File>(),
1490                path.as_ref(),
1491                type_,
1492                FollowSymlinks::No,
1493            )
1494        })
1495        .await
1496    }
1497
1498    /// Changes the permissions found on a file or a directory, without following
1499    /// symbolic links.
1500    async fn set_symlink_permissions<P: AsRef<Utf8Path> + Send>(
1501        &self,
1502        path: P,
1503        perm: Permissions,
1504    ) -> io::Result<()> {
1505        let path = from_utf8(path.as_ref())?;
1506        let clone = self.clone();
1507        spawn_blocking(move || {
1508            set_symlink_permissions(
1509                &clone.as_filelike_view::<std::fs::File>(),
1510                path.as_ref(),
1511                perm,
1512            )
1513        })
1514        .await
1515    }
1516}
1517
1518#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
1519#[cfg(not(feature = "arf_strings"))]
1520fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<&'a std::path::Path> {
1521    Ok(path.as_std_path())
1522}
1523
1524#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
1525#[cfg(feature = "arf_strings")]
1526fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<std::path::PathBuf> {
1527    #[cfg(not(windows))]
1528    let path = {
1529        #[cfg(unix)]
1530        use std::{ffi::OsString, os::unix::ffi::OsStringExt};
1531        #[cfg(target_os = "wasi")]
1532        use std::{ffi::OsString, os::wasi::ffi::OsStringExt};
1533
1534        let string = arf_strings::str_to_host(path.as_str())?;
1535        OsString::from_vec(string.into_bytes())
1536    };
1537
1538    #[cfg(windows)]
1539    let path = arf_strings::str_to_host(path.as_str())?;
1540
1541    Ok(path.into())
1542}