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}