rasio/
fs.rs

1use bitmask_enum::bitmask;
2use futures::{future::poll_fn, AsyncRead, AsyncSeek, AsyncWrite, Stream};
3use std::{
4    io::{Result, SeekFrom},
5    ops::Deref,
6    path::{Path, PathBuf},
7    sync::{Arc, OnceLock},
8    task::{Context, Poll},
9};
10
11pub use std::fs::{FileType, Metadata, Permissions};
12
13/// A bitmask for open file.
14///
15/// See [`open_file`](FileSystem::open_file) for more information.
16#[bitmask(u8)]
17pub enum FileOpenMode {
18    /// Configures the option for append mode.
19    ///
20    /// When set to true, this option means the file will be writable after opening
21    /// and the file cursor will be moved to the end of file before every write operaiton.
22    Append,
23    /// Configures the option for write mode.
24    /// If the file already exists, write calls on it will overwrite the previous contents without truncating it.
25    Writable,
26    /// Configures the option for read mode.
27    /// When set to true, this option means the file will be readable after opening.
28    Readable,
29
30    /// Configures the option for creating a new file if it doesn’t exist.
31    /// When set to true, this option means a new file will be created if it doesn’t exist.
32    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
33    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
34    Create,
35
36    /// Configures the option for creating a new file or failing if it already exists.
37    /// When set to true, this option means a new file will be created, or the open
38    /// operation will fail if the file already exists.
39    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
40    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
41    CreateNew,
42
43    /// Configures the option for truncating the previous file.
44    /// When set to true, the file will be truncated to the length of 0 bytes.
45    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
46    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
47    Truncate,
48}
49
50#[cfg(any(windows, docrs))]
51pub mod windows {
52
53    use std::io::ErrorKind;
54
55    use super::*;
56
57    pub trait FSDNamedPipeListener: Sync + Send {
58        fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
59
60        fn poll_next(&self, cx: &mut Context<'_>) -> Poll<Result<NamedPipeStream>>;
61    }
62
63    pub struct NamedPipeListener(Box<dyn FSDNamedPipeListener>);
64
65    impl Deref for NamedPipeListener {
66        type Target = dyn FSDNamedPipeListener;
67        fn deref(&self) -> &Self::Target {
68            &*self.0
69        }
70    }
71
72    impl<F: FSDNamedPipeListener + 'static> From<F> for NamedPipeListener {
73        fn from(value: F) -> Self {
74            Self(Box::new(value))
75        }
76    }
77
78    impl NamedPipeListener {
79        /// Returns internal `FSDNamedPipeListener` object.
80        pub fn as_raw_ptr(&self) -> &dyn FSDNamedPipeListener {
81            &*self.0
82        }
83
84        pub async fn accept(&self) -> Result<NamedPipeStream> {
85            poll_fn(|cx| self.as_raw_ptr().poll_next(cx)).await
86        }
87    }
88
89    impl Stream for NamedPipeListener {
90        type Item = Result<NamedPipeStream>;
91
92        fn poll_next(
93            self: std::pin::Pin<&mut Self>,
94            cx: &mut Context<'_>,
95        ) -> Poll<Option<Self::Item>> {
96            match self.as_raw_ptr().poll_next(cx) {
97                Poll::Ready(Ok(stream)) => Poll::Ready(Some(Ok(stream))),
98                Poll::Ready(Err(err)) => {
99                    if err.kind() == ErrorKind::BrokenPipe {
100                        Poll::Ready(None)
101                    } else {
102                        Poll::Ready(Some(Err(err)))
103                    }
104                }
105                Poll::Pending => Poll::Pending,
106            }
107        }
108    }
109
110    pub trait FSDNamedPipeStream: Sync + Send {
111        fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
112
113        /// Write a buffer into this writer, returning how many bytes were written
114        fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
115
116        /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
117        fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
118
119        fn poll_close(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
120    }
121
122    pub struct NamedPipeStream(Arc<Box<dyn FSDNamedPipeStream>>);
123
124    impl Deref for NamedPipeStream {
125        type Target = dyn FSDNamedPipeStream;
126        fn deref(&self) -> &Self::Target {
127            &**self.0
128        }
129    }
130
131    impl<F: FSDNamedPipeStream + 'static> From<F> for NamedPipeStream {
132        fn from(value: F) -> Self {
133            Self(Arc::new(Box::new(value)))
134        }
135    }
136
137    impl NamedPipeStream {
138        /// Returns internal `FSDNamedPipeStream` object.
139        pub fn as_raw_ptr(&self) -> &dyn FSDNamedPipeStream {
140            &**self.0
141        }
142    }
143
144    impl AsyncRead for NamedPipeStream {
145        fn poll_read(
146            self: std::pin::Pin<&mut Self>,
147            cx: &mut Context<'_>,
148            buf: &mut [u8],
149        ) -> Poll<Result<usize>> {
150            self.as_raw_ptr().poll_read(cx, buf)
151        }
152    }
153
154    impl AsyncWrite for NamedPipeStream {
155        fn poll_write(
156            self: std::pin::Pin<&mut Self>,
157            cx: &mut Context<'_>,
158            buf: &[u8],
159        ) -> Poll<Result<usize>> {
160            self.as_raw_ptr().poll_write(cx, buf)
161        }
162
163        fn poll_flush(self: std::pin::Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
164            Poll::Ready(Ok(()))
165        }
166
167        fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
168            self.as_raw_ptr().poll_close(cx)
169        }
170    }
171}
172
173/// A driver is the main entry to access asynchronously filesystem functions
174pub trait FileSystemDriver: Sync + Send {
175    /// Open new file with provided `mode`.
176    fn open_file(&self, path: &Path, mode: FileOpenMode) -> Result<File>;
177
178    /// Returns the canonical form of a path.
179    /// The returned path is in absolute form with all intermediate components
180    /// normalized and symbolic links resolved.
181    /// This function is an async version of [`std::fs::canonicalize`].
182    fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
183
184    /// Copies the contents and permissions of a file to a new location.
185    /// On success, the total number of bytes copied is returned and equals
186    /// the length of the to file after this operation.
187    /// The old contents of to will be overwritten. If from and to both point
188    /// to the same file, then the file will likely get truncated as a result of this operation.
189    fn poll_copy(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<u64>>;
190
191    /// Creates a new directory.
192    /// Note that this function will only create the final directory in path.
193    /// If you want to create all of its missing parent directories too, use
194    /// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
195    ///
196    /// This function is an async version of [`std::fs::create_dir`].
197    fn poll_create_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
198
199    /// Creates a new directory and all of its parents if they are missing.
200    /// This function is an async version of [`std::fs::create_dir_all`].
201    fn poll_create_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
202
203    /// Creates a hard link on the filesystem.
204    /// The dst path will be a link pointing to the src path. Note that operating
205    /// systems often require these two paths to be located on the same filesystem.
206    ///
207    /// This function is an async version of [`std::fs::hard_link`].
208    fn poll_hard_link(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
209
210    /// Reads metadata for a path.
211    /// This function will traverse symbolic links to read metadata for the target
212    /// file or directory. If you want to read metadata without following symbolic
213    /// links, use symlink_metadata instead.
214    ///
215    /// This function is an async version of [`std::fs::metadata`].
216    fn poll_metadata(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<Metadata>>;
217
218    /// Reads a symbolic link and returns the path it points to.
219    ///
220    /// This function is an async version of [`std::fs::read_link`].
221    fn poll_read_link(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<PathBuf>>;
222
223    /// Removes an empty directory,
224    /// if the `path` is not an empty directory, use the function
225    /// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
226    ///
227    /// This function is an async version of std::fs::remove_dir.
228    fn poll_remove_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
229
230    /// Removes a directory and all of its contents.
231    ///
232    /// This function is an async version of [`std::fs::remove_dir_all`].
233    fn poll_remove_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
234
235    /// Removes a file.
236    /// This function is an async version of [`std::fs::remove_file`].
237    fn poll_remove_file(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
238
239    /// Renames a file or directory to a new location.
240    /// If a file or directory already exists at the target location, it will be overwritten by this operation.
241    /// This function is an async version of std::fs::rename.
242    fn poll_rename(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
243
244    /// Changes the permissions of a file or directory.
245    /// This function is an async version of [`std::fs::set_permissions`].
246    fn poll_set_permissions(
247        &self,
248        cx: &mut Context<'_>,
249        path: &Path,
250        perm: &Permissions,
251    ) -> Poll<Result<()>>;
252
253    /// Reads metadata for a path without following symbolic links.
254    /// If you want to follow symbolic links before reading metadata of the target file or directory,
255    /// use [`metadata`](FileSystem::metadata) instead.
256    ///
257    /// This function is an async version of [`std::fs::symlink_metadata`].
258    fn poll_symlink_metadata(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<Metadata>>;
259
260    /// Returns a iterator handle of entries in a directory.
261    ///
262    /// See [`dir_entry_next`](FileSystem::dir_entry_next) for more information about iteration.
263    fn read_dir(&self, path: &Path) -> Result<ReadDir>;
264
265    #[cfg(any(windows))]
266    /// Opens the named pipe identified by `addr`.
267    fn named_pipe_client_open(&self, addr: &std::ffi::OsStr) -> Result<windows::NamedPipeStream>;
268
269    #[cfg(any(windows))]
270    /// Creates the named pipe identified by `addr` for use as a server.
271    ///
272    /// This uses the [`CreateNamedPipe`] function.
273    fn named_pipe_server_create(
274        &self,
275        addr: &std::ffi::OsStr,
276    ) -> Result<windows::NamedPipeListener>;
277}
278
279pub trait FSDDirEntry: Sync + Send {
280    /// Returns the bare name of this entry without the leading path.
281    fn name(&self) -> String;
282
283    /// Returns the full path to this entry.
284    /// The full path is created by joining the original path passed to [`read_dir`](FileSystemDriver::read_dir) with the name of this entry.
285    fn path(&self) -> PathBuf;
286
287    /// Reads the metadata for this entry.
288    ///
289    /// This function will traverse symbolic links to read the metadata.
290    fn meta(&self) -> Result<Metadata>;
291
292    /// eads the file type for this entry.
293    /// This function will not traverse symbolic links if this entry points at one.
294    /// If you want to read metadata with following symbolic links, use [`meta`](FSDDirEntry::meta) instead.
295    fn file_type(&self) -> Result<FileType>;
296}
297
298pub struct DirEntry(Box<dyn FSDDirEntry>);
299
300impl Deref for DirEntry {
301    type Target = dyn FSDDirEntry;
302    fn deref(&self) -> &Self::Target {
303        &*self.0
304    }
305}
306
307impl<F: FSDDirEntry + 'static> From<F> for DirEntry {
308    fn from(value: F) -> Self {
309        Self(Box::new(value))
310    }
311}
312
313impl DirEntry {
314    /// Returns internal `FSDDirEntry` object.
315    pub fn as_raw_ptr(&self) -> &dyn FSDDirEntry {
316        &*self.0
317    }
318}
319
320pub trait FSDReadDir: Sync + Send {
321    /// Get asynchronously opened event.
322    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
323
324    fn poll_next(&self, cx: &mut Context<'_>) -> Poll<Option<Result<DirEntry>>>;
325}
326
327pub struct ReadDir(Box<dyn FSDReadDir>);
328
329impl Deref for ReadDir {
330    type Target = dyn FSDReadDir;
331    fn deref(&self) -> &Self::Target {
332        &*self.0
333    }
334}
335
336impl<F: FSDReadDir + 'static> From<F> for ReadDir {
337    fn from(value: F) -> Self {
338        Self(Box::new(value))
339    }
340}
341
342impl ReadDir {
343    /// Returns internal `FSDReadDir` object.
344    pub fn as_raw_ptr(&self) -> &dyn FSDReadDir {
345        &*self.0
346    }
347
348    pub async fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
349        Self::new_with(path, get_fs_driver()).await
350    }
351
352    pub async fn new_with<P: AsRef<Path>>(path: P, driver: &dyn FileSystemDriver) -> Result<Self> {
353        let readdir = driver.read_dir(path.as_ref())?;
354
355        poll_fn(|cx| readdir.as_raw_ptr().poll_ready(cx))
356            .await
357            .map(|_| readdir)
358    }
359}
360
361impl Stream for ReadDir {
362    type Item = Result<DirEntry>;
363    fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
364        self.as_raw_ptr().poll_next(cx)
365    }
366}
367
368/// Driver-specific `File` object.
369pub trait FSDFile: Sync + Send {
370    /// Poll if the file object is actually opened.
371    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
372
373    /// Write a buffer into this writer, returning how many bytes were written
374    fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
375
376    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
377    fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
378
379    /// Attempts to sync all OS-internal metadata to disk.
380    ///
381    /// This function will attempt to ensure that all in-memory data reaches the filesystem before returning.
382    ///
383    /// This can be used to handle errors that would otherwise only be caught when the File is closed.
384    /// Dropping a file will ignore errors in synchronizing this in-memory data.
385    fn poll_flush(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
386
387    /// Seek to an offset, in bytes, in a stream.
388    ///
389    /// A seek beyond the end of a stream is allowed, but behavior is defined by the implementation.
390    ///
391    /// If the seek operation completed successfully, this method returns the new position from the
392    /// start of the stream. That position can be used later with [`SeekFrom::Start`].
393    ///
394    /// # Errors
395    /// Seeking can fail, for example because it might involve flushing a buffer.
396    ///
397    /// Seeking to a negative offset is considered an error.
398    fn poll_seek(&self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<Result<u64>>;
399
400    ///  Reads the file's metadata.
401    fn poll_meta(&self, cx: &mut Context<'_>) -> Poll<Result<Metadata>>;
402
403    /// Changes the permissions on the file.
404    fn poll_set_permissions(&self, cx: &mut Context<'_>, perm: &Permissions) -> Poll<Result<()>>;
405
406    /// Truncates or extends the file.
407    ///
408    /// If `size` is less than the current file size, then the file will be truncated. If it is
409    /// greater than the current file size, then the file will be extended to `size` and have all
410    /// intermediate data filled with zeros.
411    ///
412    /// The file's cursor stays at the same position, even if the cursor ends up being past the end
413    /// of the file after this operation.
414    ///
415    fn poll_set_len(&self, cx: &mut Context<'_>, size: u64) -> Poll<Result<()>>;
416}
417
418/// An open file on the filesystem.
419///
420/// Depending on what options the file was opened with, this type can be used for reading and/or writing.
421/// Files are automatically closed when they get dropped and any errors detected on closing are ignored.
422/// Use the sync_all method before dropping a file if such errors need to be handled.
423///
424/// This type is an async version of std::fs::File.
425pub struct File(Arc<Box<dyn FSDFile>>);
426
427impl Deref for File {
428    type Target = dyn FSDFile;
429    fn deref(&self) -> &Self::Target {
430        &**self.0
431    }
432}
433
434impl<F: FSDFile + 'static> From<F> for File {
435    fn from(value: F) -> Self {
436        Self(Arc::new(Box::new(value)))
437    }
438}
439
440impl File {
441    /// Returns internal `FSDFile` object.
442    pub fn as_raw_ptr(&self) -> &dyn FSDFile {
443        &**self.0
444    }
445
446    /// Open a file with provided `FileOpenMode`.
447    pub async fn open<P: AsRef<Path>>(path: P, mode: FileOpenMode) -> Result<Self> {
448        Self::open_with(path, mode, get_fs_driver()).await
449    }
450
451    /// Use custom `FileSystemDriver` to open file.
452    pub async fn open_with<P: AsRef<Path>>(
453        path: P,
454        mode: FileOpenMode,
455        driver: &dyn FileSystemDriver,
456    ) -> Result<Self> {
457        let file = driver.open_file(path.as_ref(), mode)?;
458
459        // Wait for file opened event.
460        poll_fn(|cx| file.as_raw_ptr().poll_ready(cx)).await?;
461
462        Ok(file)
463    }
464
465    pub async fn meta(&self) -> Result<Metadata> {
466        poll_fn(|cx| self.as_raw_ptr().poll_meta(cx)).await
467    }
468
469    pub async fn set_permissions(&self, perm: &Permissions) -> Result<()> {
470        poll_fn(|cx| self.as_raw_ptr().poll_set_permissions(cx, perm)).await
471    }
472
473    pub async fn set_len(&self, len: u64) -> Result<()> {
474        poll_fn(|cx| self.as_raw_ptr().poll_set_len(cx, len)).await
475    }
476}
477
478impl AsyncRead for File {
479    fn poll_read(
480        self: std::pin::Pin<&mut Self>,
481        cx: &mut Context<'_>,
482        buf: &mut [u8],
483    ) -> Poll<Result<usize>> {
484        self.as_raw_ptr().poll_read(cx, buf)
485    }
486}
487
488impl AsyncWrite for File {
489    fn poll_write(
490        self: std::pin::Pin<&mut Self>,
491        cx: &mut Context<'_>,
492        buf: &[u8],
493    ) -> Poll<Result<usize>> {
494        self.as_raw_ptr().poll_write(cx, buf)
495    }
496
497    fn poll_flush(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
498        self.as_raw_ptr().poll_flush(cx)
499    }
500
501    fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
502        self.poll_flush(cx)
503    }
504}
505
506impl AsyncSeek for File {
507    fn poll_seek(
508        self: std::pin::Pin<&mut Self>,
509        cx: &mut Context<'_>,
510        pos: SeekFrom,
511    ) -> Poll<Result<u64>> {
512        self.as_raw_ptr().poll_seek(cx, pos)
513    }
514}
515
516/// Returns the canonical form of a path.
517/// The returned path is in absolute form with all intermediate components
518/// normalized and symbolic links resolved.
519/// This function is an async version of [`std::fs::canonicalize`].
520pub async fn canonicalize<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
521    get_fs_driver().canonicalize(path.as_ref())
522}
523
524/// Copies the contents and permissions of a file to a new location.
525/// On success, the total number of bytes copied is returned and equals
526/// the length of the to file after this operation.
527/// The old contents of to will be overwritten. If from and to both point
528/// to the same file, then the file will likely get truncated as a result of this operation.
529pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<u64> {
530    poll_fn(|cx| get_fs_driver().poll_copy(cx, from.as_ref(), to.as_ref())).await
531}
532
533/// Creates a new directory.
534/// Note that this function will only create the final directory in path.
535/// If you want to create all of its missing parent directories too, use
536/// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
537///
538/// This function is an async version of [`std::fs::create_dir`].
539pub async fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
540    poll_fn(|cx| get_fs_driver().poll_create_dir(cx, path.as_ref())).await
541}
542
543/// Creates a new directory and all of its parents if they are missing.
544/// This function is an async version of [`std::fs::create_dir_all`].
545pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
546    poll_fn(|cx| get_fs_driver().poll_create_dir_all(cx, path.as_ref())).await
547}
548
549/// Creates a hard link on the filesystem.
550/// The dst path will be a link pointing to the src path. Note that operating
551/// systems often require these two paths to be located on the same filesystem.
552///
553/// This function is an async version of [`std::fs::hard_link`].
554pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
555    poll_fn(|cx| get_fs_driver().poll_hard_link(cx, from.as_ref(), to.as_ref())).await
556}
557
558/// Reads metadata for a path.
559/// This function will traverse symbolic links to read metadata for the target
560/// file or directory. If you want to read metadata without following symbolic
561/// links, use symlink_metadata instead.
562///
563/// This function is an async version of [`std::fs::metadata`].
564pub async fn metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
565    poll_fn(|cx| get_fs_driver().poll_metadata(cx, path.as_ref())).await
566}
567
568/// Reads a symbolic link and returns the path it points to.
569///
570/// This function is an async version of [`std::fs::read_link`].
571pub async fn read_link<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
572    poll_fn(|cx| get_fs_driver().poll_read_link(cx, path.as_ref())).await
573}
574
575/// Removes an empty directory,
576/// if the `path` is not an empty directory, use the function
577/// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
578///
579/// This function is an async version of std::fs::remove_dir.
580pub async fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
581    poll_fn(|cx| get_fs_driver().poll_remove_dir(cx, path.as_ref())).await
582}
583
584/// Removes a directory and all of its contents.
585///
586/// This function is an async version of [`std::fs::remove_dir_all`].
587pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
588    poll_fn(|cx| get_fs_driver().poll_remove_dir_all(cx, path.as_ref())).await
589}
590
591/// Removes a file.
592/// This function is an async version of [`std::fs::remove_file`].
593pub async fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
594    poll_fn(|cx| get_fs_driver().poll_remove_file(cx, path.as_ref())).await
595}
596
597/// Renames a file or directory to a new location.
598/// If a file or directory already exists at the target location, it will be overwritten by this operation.
599/// This function is an async version of std::fs::rename.
600pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
601    poll_fn(|cx| get_fs_driver().poll_rename(cx, from.as_ref(), to.as_ref())).await
602}
603
604/// Changes the permissions of a file or directory.
605/// This function is an async version of [`std::fs::set_permissions`].
606pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: &Permissions) -> Result<()> {
607    poll_fn(|cx| get_fs_driver().poll_set_permissions(cx, path.as_ref(), perm)).await
608}
609
610/// Reads metadata for a path without following symbolic links.
611/// If you want to follow symbolic links before reading metadata of the target file or directory,
612/// use [`metadata`](FileSystem::metadata) instead.
613///
614/// This function is an async version of [`std::fs::symlink_metadata`].
615pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
616    poll_fn(|cx| get_fs_driver().poll_symlink_metadata(cx, path.as_ref())).await
617}
618
619/// Returns `true` if the path exists on disk and is pointing at a directory.
620///
621/// This function will traverse symbolic links to query information about the
622/// destination file. In case of broken symbolic links this will return `false`.
623///
624/// If you cannot access the directory containing the file, e.g., because of a
625/// permission error, this will return `false`.
626///
627/// # See Also
628///
629/// This is a convenience function that coerces errors to false. If you want to
630/// check errors, call [fs::metadata] and handle its Result. Then call
631/// [fs::Metadata::is_dir] if it was Ok.
632///
633/// [fs::metadata]: ../fs/fn.metadata.html
634/// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
635pub async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
636    metadata(path).await.map(|m| m.is_dir()).unwrap_or(false)
637}
638
639pub struct FileSystem<'a> {
640    driver: &'a dyn FileSystemDriver,
641}
642
643impl<'a> From<&'a dyn FileSystemDriver> for FileSystem<'a> {
644    fn from(value: &'a dyn FileSystemDriver) -> Self {
645        Self { driver: value }
646    }
647}
648
649impl<'a> FileSystem<'a> {
650    /// Returns `true` if the path exists on disk and is pointing at a directory.
651    ///
652    /// This function will traverse symbolic links to query information about the
653    /// destination file. In case of broken symbolic links this will return `false`.
654    ///
655    /// If you cannot access the directory containing the file, e.g., because of a
656    /// permission error, this will return `false`.
657    ///
658    ///
659    /// # See Also
660    ///
661    /// This is a convenience function that coerces errors to false. If you want to
662    /// check errors, call [fs::metadata] and handle its Result. Then call
663    /// [fs::Metadata::is_dir] if it was Ok.
664    ///
665    /// [fs::metadata]: ../fs/fn.metadata.html
666    /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
667    pub async fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
668        self.metadata(path)
669            .await
670            .map(|m| m.is_dir())
671            .unwrap_or(false)
672    }
673
674    /// Returns `true` if the path exists on disk and is pointing at a regular file.
675    ///
676    /// This function will traverse symbolic links to query information about the
677    /// destination file. In case of broken symbolic links this will return `false`.
678    ///
679    /// If you cannot access the directory containing the file, e.g., because of a
680    /// permission error, this will return `false`.
681    ///
682    /// # See Also
683    ///
684    /// This is a convenience function that coerces errors to false. If you want to
685    /// check errors, call [fs::metadata] and handle its Result. Then call
686    /// [fs::Metadata::is_file] if it was Ok.
687    ///
688    /// [fs::metadata]: ../fs/fn.metadata.html
689    /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file
690
691    pub async fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
692        self.metadata(path)
693            .await
694            .map(|m| m.is_file())
695            .unwrap_or(false)
696    }
697
698    pub async fn open_file<P: AsRef<Path>>(&self, path: P, mode: FileOpenMode) -> Result<File> {
699        File::open_with(path, mode, self.driver).await
700    }
701
702    /// Returns the canonical form of a path.
703    /// The returned path is in absolute form with all intermediate components
704    /// normalized and symbolic links resolved.
705    /// This function is an async version of [`std::fs::canonicalize`].
706    pub async fn canonicalize<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
707        self.driver.canonicalize(path.as_ref())
708    }
709
710    /// Copies the contents and permissions of a file to a new location.
711    /// On success, the total number of bytes copied is returned and equals
712    /// the length of the to file after this operation.
713    /// The old contents of to will be overwritten. If from and to both point
714    /// to the same file, then the file will likely get truncated as a result of this operation.
715    pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<u64> {
716        poll_fn(|cx| self.driver.poll_copy(cx, from.as_ref(), to.as_ref())).await
717    }
718
719    /// Creates a new directory.
720    /// Note that this function will only create the final directory in path.
721    /// If you want to create all of its missing parent directories too, use
722    /// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
723    ///
724    /// This function is an async version of [`std::fs::create_dir`].
725    pub async fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
726        poll_fn(|cx| self.driver.poll_create_dir(cx, path.as_ref())).await
727    }
728
729    /// Creates a new directory and all of its parents if they are missing.
730    /// This function is an async version of [`std::fs::create_dir_all`].
731    pub async fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
732        poll_fn(|cx| self.driver.poll_create_dir_all(cx, path.as_ref())).await
733    }
734
735    /// Creates a hard link on the filesystem.
736    /// The dst path will be a link pointing to the src path. Note that operating
737    /// systems often require these two paths to be located on the same filesystem.
738    ///
739    /// This function is an async version of [`std::fs::hard_link`].
740    pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
741        poll_fn(|cx| self.driver.poll_hard_link(cx, from.as_ref(), to.as_ref())).await
742    }
743
744    /// Reads metadata for a path.
745    /// This function will traverse symbolic links to read metadata for the target
746    /// file or directory. If you want to read metadata without following symbolic
747    /// links, use symlink_metadata instead.
748    ///
749    /// This function is an async version of [`std::fs::metadata`].
750    pub async fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
751        poll_fn(|cx| self.driver.poll_metadata(cx, path.as_ref())).await
752    }
753
754    /// Reads a symbolic link and returns the path it points to.
755    ///
756    /// This function is an async version of [`std::fs::read_link`].
757    pub async fn read_link<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
758        poll_fn(|cx| self.driver.poll_read_link(cx, path.as_ref())).await
759    }
760
761    /// Removes an empty directory,
762    /// if the `path` is not an empty directory, use the function
763    /// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
764    ///
765    /// This function is an async version of std::fs::remove_dir.
766    pub async fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
767        poll_fn(|cx| self.driver.poll_remove_dir(cx, path.as_ref())).await
768    }
769
770    /// Removes a directory and all of its contents.
771    ///
772    /// This function is an async version of [`std::fs::remove_dir_all`].
773    pub async fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
774        poll_fn(|cx| self.driver.poll_remove_dir_all(cx, path.as_ref())).await
775    }
776
777    /// Removes a file.
778    /// This function is an async version of [`std::fs::remove_file`].
779    pub async fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
780        poll_fn(|cx| self.driver.poll_remove_file(cx, path.as_ref())).await
781    }
782
783    /// Renames a file or directory to a new location.
784    /// If a file or directory already exists at the target location, it will be overwritten by this operation.
785    /// This function is an async version of std::fs::rename.
786    pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
787        poll_fn(|cx| self.driver.poll_rename(cx, from.as_ref(), to.as_ref())).await
788    }
789
790    /// Changes the permissions of a file or directory.
791    /// This function is an async version of [`std::fs::set_permissions`].
792    pub async fn set_permissions<P: AsRef<Path>>(&self, path: P, perm: &Permissions) -> Result<()> {
793        poll_fn(|cx| self.driver.poll_set_permissions(cx, path.as_ref(), perm)).await
794    }
795
796    /// Reads metadata for a path without following symbolic links.
797    /// If you want to follow symbolic links before reading metadata of the target file or directory,
798    /// use [`metadata`](FileSystem::metadata) instead.
799    ///
800    /// This function is an async version of [`std::fs::symlink_metadata`].
801    pub async fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
802        poll_fn(|cx| self.driver.poll_symlink_metadata(cx, path.as_ref())).await
803    }
804}
805
806static FIFLE_SYSTEM_DRIVER: OnceLock<Box<dyn FileSystemDriver>> = OnceLock::new();
807
808/// Get global register `FileSystemDriver` instance.
809pub fn get_fs_driver() -> &'static dyn FileSystemDriver {
810    FIFLE_SYSTEM_DRIVER
811        .get()
812        .expect("Call register_network_driver first.")
813        .as_ref()
814}
815
816/// Register provided [`FileSystemDriver`] as global network implementation.
817///
818/// # Panic
819///
820/// Multiple calls to this function are not permitted!!!
821pub fn register_fs_driver<E: FileSystemDriver + 'static>(driver: E) {
822    if FIFLE_SYSTEM_DRIVER.set(Box::new(driver)).is_err() {
823        panic!("Multiple calls to register_network_driver are not permitted!!!");
824    }
825}