cap_fs_ext/
dir_ext.rs

1#[cfg(feature = "fs_utf8")]
2use camino::Utf8Path;
3#[cfg(not(windows))]
4use cap_primitives::fs::symlink;
5use cap_primitives::fs::{
6    access, open_dir_nofollow, set_symlink_permissions, set_times, set_times_nofollow,
7    FollowSymlinks, Permissions,
8};
9#[cfg(windows)]
10use cap_primitives::fs::{symlink_dir, symlink_file};
11use io_lifetimes::AsFilelike;
12use std::io;
13use std::path::Path;
14
15pub use cap_primitives::fs::{AccessType, SystemTimeSpec};
16
17/// Extension trait for `Dir`.
18pub trait DirExt {
19    /// Set the last access time for a file on a filesystem.
20    ///
21    /// This corresponds to [`filetime::set_file_atime`].
22    ///
23    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
24    fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
25
26    /// Set the last modification time for a file on a filesystem.
27    ///
28    /// This corresponds to [`filetime::set_file_mtime`].
29    ///
30    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
31    fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
32
33    /// Set the last access and modification times for a file on a filesystem.
34    ///
35    /// This corresponds to [`filetime::set_file_times`].
36    ///
37    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
38    fn set_times<P: AsRef<Path>>(
39        &self,
40        path: P,
41        atime: Option<SystemTimeSpec>,
42        mtime: Option<SystemTimeSpec>,
43    ) -> io::Result<()>;
44
45    /// Set the last access and modification times for a file on a filesystem.
46    /// This function does not follow symlink.
47    ///
48    /// This corresponds to [`filetime::set_symlink_file_times`].
49    ///
50    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
51    fn set_symlink_times<P: AsRef<Path>>(
52        &self,
53        path: P,
54        atime: Option<SystemTimeSpec>,
55        mtime: Option<SystemTimeSpec>,
56    ) -> io::Result<()>;
57
58    /// Creates a new symbolic link on a filesystem.
59    ///
60    /// This corresponds to [`std::os::unix::fs::symlink`], except that it's
61    /// supported on non-Unix platforms as well, and it's not guaranteed to be
62    /// atomic.
63    ///
64    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
65    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
66
67    /// Creates a new file symbolic link on a filesystem.
68    ///
69    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
70    /// it's supported on non-Windows platforms as well, and it's not
71    /// guaranteed to fail if the target is not a file.
72    ///
73    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
74    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
75
76    /// Creates a new directory symbolic link on a filesystem.
77    ///
78    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
79    /// it's supported on non-Windows platforms as well, and it's not
80    /// guaranteed to fail if the target is not a directory.
81    ///
82    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
83    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
84
85    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
86    /// symlink.
87    fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self>
88    where
89        Self: Sized;
90
91    /// Removes a file or symlink from a filesystem.
92    ///
93    /// Removal of symlinks has different behavior under Windows - if a symlink
94    /// points to a directory, it cannot be removed with the `remove_file`
95    /// operation. This method will remove files and all symlinks.
96    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
97
98    /// Test for accessibility or existence of a filesystem object.
99    fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
100
101    /// Test for accessibility or existence of a filesystem object, without
102    /// following symbolic links.
103    fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
104
105    /// Changes the permissions found on a file or a directory, without following
106    /// symbolic links.
107    fn set_symlink_permissions<P: AsRef<Path>>(&self, path: P, perm: Permissions)
108        -> io::Result<()>;
109}
110
111/// `fs_utf8` version of `DirExt`.
112#[cfg(all(feature = "std", feature = "fs_utf8"))]
113pub trait DirExtUtf8 {
114    /// Set the last access time for a file on a filesystem.
115    ///
116    /// This corresponds to [`filetime::set_file_atime`].
117    ///
118    /// [`filetime::set_file_atime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_atime.html
119    fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
120
121    /// Set the last modification time for a file on a filesystem.
122    ///
123    /// This corresponds to [`filetime::set_file_mtime`].
124    ///
125    /// [`filetime::set_file_mtime`]: https://docs.rs/filetime/latest/filetime/fn.set_file_mtime.html
126    fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
127
128    /// Set the last access and modification times for a file on a filesystem.
129    ///
130    /// This corresponds to [`filetime::set_file_times`].
131    ///
132    /// [`filetime::set_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_file_times.html
133    fn set_times<P: AsRef<Utf8Path>>(
134        &self,
135        path: P,
136        atime: Option<SystemTimeSpec>,
137        mtime: Option<SystemTimeSpec>,
138    ) -> io::Result<()>;
139
140    /// Set the last access and modification times for a file on a filesystem.
141    /// This function does not follow symlink.
142    ///
143    /// This corresponds to [`filetime::set_symlink_file_times`].
144    ///
145    /// [`filetime::set_symlink_file_times`]: https://docs.rs/filetime/latest/filetime/fn.set_symlink_file_times.html
146    fn set_symlink_times<P: AsRef<Utf8Path>>(
147        &self,
148        path: P,
149        atime: Option<SystemTimeSpec>,
150        mtime: Option<SystemTimeSpec>,
151    ) -> io::Result<()>;
152
153    /// Creates a new symbolic link on a filesystem.
154    ///
155    /// This corresponds to [`std::os::unix::fs::symlink`], except that it's
156    /// supported on non-Unix platforms as well, and it's not guaranteed to be
157    /// atomic.
158    ///
159    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
160    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()>;
161
162    /// Creates a new file symbolic link on a filesystem.
163    ///
164    /// This corresponds to [`std::os::windows::fs::symlink_file`], except that
165    /// it's supported on non-Windows platforms as well, and it's not
166    /// guaranteed to fail if the target is not a file.
167    ///
168    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
169    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
170        &self,
171        src: P,
172        dst: Q,
173    ) -> io::Result<()>;
174
175    /// Creates a new directory symbolic link on a filesystem.
176    ///
177    /// This corresponds to [`std::os::windows::fs::symlink_dir`], except that
178    /// it's supported on non-Windows platforms as well, and it's not
179    /// guaranteed to fail if the target is not a directory.
180    ///
181    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
182    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q)
183        -> io::Result<()>;
184
185    /// Similar to `cap_std::fs::Dir::open_dir`, but fails if the path names a
186    /// symlink.
187    fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self>
188    where
189        Self: Sized;
190
191    /// Removes a file or symlink from a filesystem.
192    ///
193    /// This is similar to [`std::fs::remove_file`], except that it also works
194    /// on symlinks to directories on Windows, similar to how `unlink` works
195    /// on symlinks to directories on Posix-ish platforms.
196    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()>;
197
198    /// Test for accessibility or existence of a filesystem object.
199    fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
200
201    /// Test for accessibility or existence of a filesystem object, without
202    /// following symbolic links.
203    fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
204
205    /// Changes the permissions found on a file or a directory, without following
206    /// symbolic links.
207    fn set_symlink_permissions<P: AsRef<Utf8Path>>(
208        &self,
209        path: P,
210        perm: Permissions,
211    ) -> io::Result<()>;
212}
213
214#[cfg(feature = "std")]
215impl DirExt for cap_std::fs::Dir {
216    #[inline]
217    fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
218        set_times(
219            &self.as_filelike_view::<std::fs::File>(),
220            path.as_ref(),
221            Some(atime),
222            None,
223        )
224    }
225
226    #[inline]
227    fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
228        set_times(
229            &self.as_filelike_view::<std::fs::File>(),
230            path.as_ref(),
231            None,
232            Some(mtime),
233        )
234    }
235
236    #[inline]
237    fn set_times<P: AsRef<Path>>(
238        &self,
239        path: P,
240        atime: Option<SystemTimeSpec>,
241        mtime: Option<SystemTimeSpec>,
242    ) -> io::Result<()> {
243        set_times(
244            &self.as_filelike_view::<std::fs::File>(),
245            path.as_ref(),
246            atime,
247            mtime,
248        )
249    }
250
251    #[inline]
252    fn set_symlink_times<P: AsRef<Path>>(
253        &self,
254        path: P,
255        atime: Option<SystemTimeSpec>,
256        mtime: Option<SystemTimeSpec>,
257    ) -> io::Result<()> {
258        set_times_nofollow(
259            &self.as_filelike_view::<std::fs::File>(),
260            path.as_ref(),
261            atime,
262            mtime,
263        )
264    }
265
266    #[cfg(not(windows))]
267    #[inline]
268    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
269        symlink(
270            src.as_ref(),
271            &self.as_filelike_view::<std::fs::File>(),
272            dst.as_ref(),
273        )
274    }
275
276    #[cfg(not(windows))]
277    #[inline]
278    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
279        self.symlink(src, dst)
280    }
281
282    #[cfg(not(windows))]
283    #[inline]
284    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
285        self.symlink(src, dst)
286    }
287
288    #[cfg(windows)]
289    #[inline]
290    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
291        if self.metadata(src.as_ref())?.is_dir() {
292            self.symlink_dir(src, dst)
293        } else {
294            self.symlink_file(src, dst)
295        }
296    }
297
298    #[cfg(windows)]
299    #[inline]
300    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
301        symlink_file(
302            src.as_ref(),
303            &self.as_filelike_view::<std::fs::File>(),
304            dst.as_ref(),
305        )
306    }
307
308    #[cfg(windows)]
309    #[inline]
310    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
311        symlink_dir(
312            src.as_ref(),
313            &self.as_filelike_view::<std::fs::File>(),
314            dst.as_ref(),
315        )
316    }
317
318    #[inline]
319    fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
320        match open_dir_nofollow(&self.as_filelike_view::<std::fs::File>(), path.as_ref()) {
321            Ok(file) => Ok(Self::from_std_file(file)),
322            Err(e) => Err(e),
323        }
324    }
325
326    #[cfg(not(windows))]
327    #[inline]
328    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
329        self.remove_file(path.as_ref())
330    }
331
332    #[cfg(windows)]
333    #[inline]
334    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
335        use crate::OpenOptionsFollowExt;
336        use cap_primitives::fs::_WindowsByHandle;
337        use cap_std::fs::{OpenOptions, OpenOptionsExt};
338        use windows_sys::Win32::Storage::FileSystem::{
339            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
340            FILE_FLAG_OPEN_REPARSE_POINT,
341        };
342        let path = path.as_ref();
343
344        let mut opts = OpenOptions::new();
345        opts.access_mode(DELETE);
346        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
347        opts.follow(FollowSymlinks::No);
348        let file = self.open_with(path, &opts)?;
349
350        let meta = file.metadata()?;
351        if meta.file_type().is_symlink()
352            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
353        {
354            self.remove_dir(path)?;
355        } else {
356            self.remove_file(path)?;
357        }
358
359        // Drop the file after calling `remove_file` or `remove_dir`, since
360        // Windows doesn't actually remove the file until after the last open
361        // handle is closed, and this protects us from race conditions where
362        // other processes replace the file out from underneath us.
363        drop(file);
364
365        Ok(())
366    }
367
368    /// Test for accessibility or existence of a filesystem object.
369    fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
370        access(
371            &self.as_filelike_view::<std::fs::File>(),
372            path.as_ref(),
373            type_,
374            FollowSymlinks::Yes,
375        )
376    }
377
378    /// Test for accessibility or existence of a filesystem object.
379    fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
380        access(
381            &self.as_filelike_view::<std::fs::File>(),
382            path.as_ref(),
383            type_,
384            FollowSymlinks::No,
385        )
386    }
387
388    /// Changes the permissions found on a file or a directory, without following
389    /// symbolic links.
390    fn set_symlink_permissions<P: AsRef<Path>>(
391        &self,
392        path: P,
393        perm: Permissions,
394    ) -> io::Result<()> {
395        set_symlink_permissions(
396            &self.as_filelike_view::<std::fs::File>(),
397            path.as_ref(),
398            perm,
399        )
400    }
401}
402
403#[cfg(all(feature = "std", feature = "fs_utf8"))]
404impl DirExtUtf8 for cap_std::fs_utf8::Dir {
405    #[inline]
406    fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
407        let path = from_utf8(path.as_ref())?;
408        set_times(
409            &self.as_filelike_view::<std::fs::File>(),
410            &path,
411            Some(atime),
412            None,
413        )
414    }
415
416    #[inline]
417    fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
418        let path = from_utf8(path.as_ref())?;
419        set_times(
420            &self.as_filelike_view::<std::fs::File>(),
421            &path,
422            None,
423            Some(mtime),
424        )
425    }
426
427    #[inline]
428    fn set_times<P: AsRef<Utf8Path>>(
429        &self,
430        path: P,
431        atime: Option<SystemTimeSpec>,
432        mtime: Option<SystemTimeSpec>,
433    ) -> io::Result<()> {
434        let path = from_utf8(path.as_ref())?;
435        set_times(
436            &self.as_filelike_view::<std::fs::File>(),
437            &path,
438            atime,
439            mtime,
440        )
441    }
442
443    #[inline]
444    fn set_symlink_times<P: AsRef<Utf8Path>>(
445        &self,
446        path: P,
447        atime: Option<SystemTimeSpec>,
448        mtime: Option<SystemTimeSpec>,
449    ) -> io::Result<()> {
450        let path = from_utf8(path.as_ref())?;
451        set_times_nofollow(
452            &self.as_filelike_view::<std::fs::File>(),
453            &path,
454            atime,
455            mtime,
456        )
457    }
458
459    #[cfg(not(windows))]
460    #[inline]
461    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
462        Self::symlink(self, src, dst)
463    }
464
465    #[cfg(not(windows))]
466    #[inline]
467    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
468        &self,
469        src: P,
470        dst: Q,
471    ) -> io::Result<()> {
472        Self::symlink(self, src, dst)
473    }
474
475    #[cfg(not(windows))]
476    #[inline]
477    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
478        &self,
479        src: P,
480        dst: Q,
481    ) -> io::Result<()> {
482        Self::symlink(self, src, dst)
483    }
484
485    #[cfg(windows)]
486    #[inline]
487    fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
488        if self.metadata(src.as_ref())?.is_dir() {
489            Self::symlink_dir(self, src, dst)
490        } else {
491            Self::symlink_file(self, src, dst)
492        }
493    }
494
495    #[cfg(windows)]
496    #[inline]
497    fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
498        &self,
499        src: P,
500        dst: Q,
501    ) -> io::Result<()> {
502        Self::symlink_file(self, src, dst)
503    }
504
505    #[cfg(windows)]
506    #[inline]
507    fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
508        &self,
509        src: P,
510        dst: Q,
511    ) -> io::Result<()> {
512        Self::symlink_dir(self, src, dst)
513    }
514
515    #[inline]
516    fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self> {
517        match open_dir_nofollow(
518            &self.as_filelike_view::<std::fs::File>(),
519            path.as_ref().as_ref(),
520        ) {
521            Ok(file) => Ok(Self::from_std_file(file)),
522            Err(e) => Err(e),
523        }
524    }
525
526    #[cfg(not(windows))]
527    #[inline]
528    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
529        self.remove_file(path.as_ref())
530    }
531
532    #[cfg(windows)]
533    #[inline]
534    fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
535        use crate::OpenOptionsFollowExt;
536        use cap_primitives::fs::_WindowsByHandle;
537        use cap_std::fs::{OpenOptions, OpenOptionsExt};
538        use windows_sys::Win32::Storage::FileSystem::{
539            DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
540            FILE_FLAG_OPEN_REPARSE_POINT,
541        };
542        let path = path.as_ref();
543
544        let mut opts = OpenOptions::new();
545        opts.access_mode(DELETE);
546        opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
547        opts.follow(FollowSymlinks::No);
548        let file = self.open_with(path, &opts)?;
549
550        let meta = file.metadata()?;
551        if meta.file_type().is_symlink()
552            && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
553        {
554            self.remove_dir(path)?;
555        } else {
556            self.remove_file(path)?;
557        }
558
559        // Drop the file after calling `remove_file` or `remove_dir`, since
560        // Windows doesn't actually remove the file until after the last open
561        // handle is closed, and this protects us from race conditions where
562        // other processes replace the file out from underneath us.
563        drop(file);
564
565        Ok(())
566    }
567
568    /// Test for accessibility or existence of a filesystem object.
569    fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
570        access(
571            &self.as_filelike_view::<std::fs::File>(),
572            path.as_ref().as_ref(),
573            type_,
574            FollowSymlinks::Yes,
575        )
576    }
577
578    /// Test for accessibility or existence of a filesystem object.
579    fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
580        access(
581            &self.as_filelike_view::<std::fs::File>(),
582            path.as_ref().as_ref(),
583            type_,
584            FollowSymlinks::No,
585        )
586    }
587
588    /// Changes the permissions found on a file or a directory, without following
589    /// symbolic links.
590    fn set_symlink_permissions<P: AsRef<Utf8Path>>(
591        &self,
592        path: P,
593        perm: Permissions,
594    ) -> io::Result<()> {
595        set_symlink_permissions(
596            &self.as_filelike_view::<std::fs::File>(),
597            path.as_ref().as_ref(),
598            perm,
599        )
600    }
601}
602
603#[cfg(all(feature = "std", feature = "fs_utf8"))]
604#[cfg(not(feature = "arf_strings"))]
605fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<&'a std::path::Path> {
606    Ok(path.as_std_path())
607}
608
609#[cfg(all(feature = "std", feature = "fs_utf8"))]
610#[cfg(feature = "arf_strings")]
611fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<std::path::PathBuf> {
612    #[cfg(not(windows))]
613    let path = {
614        #[cfg(unix)]
615        use std::{ffi::OsString, os::unix::ffi::OsStringExt};
616        #[cfg(target_os = "wasi")]
617        use std::{ffi::OsString, os::wasi::ffi::OsStringExt};
618
619        let string = arf_strings::str_to_host(path.as_str())?;
620        OsString::from_vec(string.into_bytes())
621    };
622
623    #[cfg(windows)]
624    let path = arf_strings::str_to_host(path.as_str())?;
625
626    Ok(path.into())
627}