async_fs/
lib.rs

1//! Async filesystem primitives.
2//!
3//! This crate is an async version of [`std::fs`].
4//!
5//! # Implementation
6//!
7//! This crate uses [`blocking`] to offload blocking I/O onto a thread pool.
8//!
9//! [`blocking`]: https://docs.rs/blocking
10//!
11//! # Examples
12//!
13//! Create a new file and write some bytes to it:
14//!
15//! ```no_run
16//! use async_fs::File;
17//! use futures_lite::io::AsyncWriteExt;
18//!
19//! # futures_lite::future::block_on(async {
20//! let mut file = File::create("a.txt").await?;
21//! file.write_all(b"Hello, world!").await?;
22//! file.flush().await?;
23//! # std::io::Result::Ok(()) });
24//! ```
25
26#![forbid(unsafe_code)]
27#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
28#![doc(
29    html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
30)]
31#![doc(
32    html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
33)]
34
35use std::ffi::OsString;
36use std::fmt;
37use std::future::Future;
38use std::io::{self, SeekFrom};
39use std::path::{Path, PathBuf};
40use std::pin::Pin;
41use std::sync::Arc;
42use std::task::{Context, Poll};
43
44#[cfg(unix)]
45use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _};
46
47#[cfg(windows)]
48use std::os::windows::fs::OpenOptionsExt as _;
49
50use async_lock::Mutex;
51use blocking::{unblock, Unblock};
52use futures_lite::future::FutureExt;
53use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt};
54use futures_lite::ready;
55use futures_lite::stream::Stream;
56
57#[doc(no_inline)]
58pub use std::fs::{FileType, Metadata, Permissions};
59
60/// Returns the canonical form of a path.
61///
62/// The returned path is in absolute form with all intermediate components normalized and symbolic
63/// links resolved.
64///
65/// # Errors
66///
67/// An error will be returned in the following situations:
68///
69/// * `path` does not point to an existing file or directory.
70/// * A non-final component in `path` is not a directory.
71/// * Some other I/O error occurred.
72///
73/// # Examples
74///
75/// ```no_run
76/// # futures_lite::future::block_on(async {
77/// let path = async_fs::canonicalize(".").await?;
78/// # std::io::Result::Ok(()) });
79/// ```
80pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
81    let path = path.as_ref().to_owned();
82    unblock(move || std::fs::canonicalize(path)).await
83}
84
85/// Copies a file to a new location.
86///
87/// On success, the total number of bytes copied is returned and equals the length of the `dst`
88/// file after this operation.
89///
90/// The old contents of `dst` will be overwritten. If `src` and `dst` both point to the same
91/// file, then the file will likely get truncated as a result of this operation.
92///
93/// If you're working with open [`File`]s and want to copy contents through those types, use
94/// [`futures_lite::io::copy()`] instead.
95///
96/// # Errors
97///
98/// An error will be returned in the following situations:
99///
100/// * `src` does not point to an existing file.
101/// * The current process lacks permissions to read `src` or write `dst`.
102/// * Some other I/O error occurred.
103///
104/// # Examples
105///
106/// ```no_run
107/// # futures_lite::future::block_on(async {
108/// let num_bytes = async_fs::copy("a.txt", "b.txt").await?;
109/// # std::io::Result::Ok(()) });
110/// ```
111pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<u64> {
112    let src = src.as_ref().to_owned();
113    let dst = dst.as_ref().to_owned();
114    unblock(move || std::fs::copy(&src, &dst)).await
115}
116
117/// Creates a new, empty directory at the provided path
118///
119/// # Platform-specific behavior
120///
121/// This function currently corresponds to the `mkdir` function on Unix
122/// and the `CreateDirectory` function on Windows.
123/// Note that, this [may change in the future][changes].
124///
125/// [changes]: io#platform-specific-behavior
126///
127/// **NOTE**: If a parent of the given path doesn't exist, this function will
128/// return an error. To create a directory and all its missing parents at the
129/// same time, use the [`create_dir_all`] function.
130///
131/// # Errors
132///
133/// This function will return an error in the following situations, but is not
134/// limited to just these cases:
135///
136/// * User lacks permissions to create directory at `path`.
137/// * A parent of the given path doesn't exist. (To create a directory and all
138///   its missing parents at the same time, use the [`create_dir_all`]
139///   function.)
140/// * `path` already exists.
141///
142/// # Examples
143///
144/// ```no_run
145/// use std::fs;
146///
147/// fn main() -> std::io::Result<()> {
148///     fs::create_dir("/some/dir")?;
149///     Ok(())
150/// }
151/// ```
152pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
153    let path = path.as_ref().to_owned();
154    unblock(move || std::fs::create_dir(path)).await
155}
156
157/// Recursively create a directory and all of its parent components if they
158/// are missing.
159///
160/// # Platform-specific behavior
161///
162/// This function currently corresponds to the `mkdir` function on Unix
163/// and the `CreateDirectory` function on Windows.
164/// Note that, this [may change in the future][changes].
165///
166/// [changes]: io#platform-specific-behavior
167///
168/// # Errors
169///
170/// This function will return an error in the following situations, but is not
171/// limited to just these cases:
172///
173/// * If any directory in the path specified by `path`
174/// does not already exist and it could not be created otherwise. The specific
175/// error conditions for when a directory is being created (after it is
176/// determined to not exist) are outlined by [`fs::create_dir`].
177///
178/// Notable exception is made for situations where any of the directories
179/// specified in the `path` could not be created as it was being created concurrently.
180/// Such cases are considered to be successful. That is, calling `create_dir_all`
181/// concurrently from multiple threads or processes is guaranteed not to fail
182/// due to a race condition with itself.
183///
184/// [`fs::create_dir`]: create_dir
185///
186/// # Examples
187///
188/// ```no_run
189/// use std::fs;
190///
191/// fn main() -> std::io::Result<()> {
192///     fs::create_dir_all("/some/dir")?;
193///     Ok(())
194/// }
195/// ```
196pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
197    let path = path.as_ref().to_owned();
198    unblock(move || std::fs::create_dir_all(path)).await
199}
200
201/// Creates a hard link on the filesystem.
202///
203/// The `dst` path will be a link pointing to the `src` path. Note that operating systems often
204/// require these two paths to be located on the same filesystem.
205///
206/// # Errors
207///
208/// An error will be returned in the following situations:
209///
210/// * `src` does not point to an existing file.
211/// * Some other I/O error occurred.
212///
213/// # Examples
214///
215/// ```no_run
216/// # futures_lite::future::block_on(async {
217/// async_fs::hard_link("a.txt", "b.txt").await?;
218/// # std::io::Result::Ok(()) });
219/// ```
220pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
221    let src = src.as_ref().to_owned();
222    let dst = dst.as_ref().to_owned();
223    unblock(move || std::fs::hard_link(&src, &dst)).await
224}
225
226/// Reads metadata for a path.
227///
228/// This function will traverse symbolic links to read metadata for the target file or directory.
229/// If you want to read metadata without following symbolic links, use [`symlink_metadata()`]
230/// instead.
231///
232/// # Errors
233///
234/// An error will be returned in the following situations:
235///
236/// * `path` does not point to an existing file or directory.
237/// * The current process lacks permissions to read metadata for the path.
238/// * Some other I/O error occurred.
239///
240/// # Examples
241///
242/// ```no_run
243/// # futures_lite::future::block_on(async {
244/// let perm = async_fs::metadata("a.txt").await?.permissions();
245/// # std::io::Result::Ok(()) });
246/// ```
247pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
248    let path = path.as_ref().to_owned();
249    unblock(move || std::fs::metadata(path)).await
250}
251
252/// Reads the entire contents of a file as raw bytes.
253///
254/// This is a convenience function for reading entire files. It pre-allocates a buffer based on the
255/// file size when available, so it is typically faster than manually opening a file and reading
256/// from it.
257///
258/// If you want to read the contents as a string, use [`read_to_string()`] instead.
259///
260/// # Errors
261///
262/// An error will be returned in the following situations:
263///
264/// * `path` does not point to an existing file.
265/// * The current process lacks permissions to read the file.
266/// * Some other I/O error occurred.
267///
268/// # Examples
269///
270/// ```no_run
271/// # futures_lite::future::block_on(async {
272/// let contents = async_fs::read("a.txt").await?;
273/// # std::io::Result::Ok(()) });
274/// ```
275pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
276    let path = path.as_ref().to_owned();
277    unblock(move || std::fs::read(path)).await
278}
279
280/// Returns a stream of entries in a directory.
281///
282/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can
283/// occur while reading from the stream.
284///
285/// # Errors
286///
287/// An error will be returned in the following situations:
288///
289/// * `path` does not point to an existing directory.
290/// * The current process lacks permissions to read the contents of the directory.
291/// * Some other I/O error occurred.
292///
293/// # Examples
294///
295/// ```no_run
296/// # futures_lite::future::block_on(async {
297/// use futures_lite::stream::StreamExt;
298///
299/// let mut entries = async_fs::read_dir(".").await?;
300///
301/// while let Some(entry) = entries.try_next().await? {
302///     println!("{}", entry.file_name().to_string_lossy());
303/// }
304/// # std::io::Result::Ok(()) });
305/// ```
306pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
307    let path = path.as_ref().to_owned();
308    unblock(move || std::fs::read_dir(path).map(|inner| ReadDir(State::Idle(Some(inner))))).await
309}
310
311/// A stream of entries in a directory.
312///
313/// This stream is returned by [`read_dir()`] and yields items of type
314/// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's
315/// path or metadata.
316pub struct ReadDir(State);
317
318/// The state of an asynchronous `ReadDir`.
319///
320/// The `ReadDir` can be either idle or busy performing an asynchronous operation.
321enum State {
322    Idle(Option<std::fs::ReadDir>),
323    Busy(blocking::Task<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
324}
325
326impl fmt::Debug for ReadDir {
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        f.debug_struct("ReadDir").finish()
329    }
330}
331
332impl Stream for ReadDir {
333    type Item = io::Result<DirEntry>;
334
335    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
336        loop {
337            match &mut self.0 {
338                State::Idle(opt) => {
339                    let mut inner = opt.take().unwrap();
340
341                    // Start the operation asynchronously.
342                    self.0 = State::Busy(unblock(move || {
343                        let next = inner.next();
344                        (inner, next)
345                    }));
346                }
347                // Poll the asynchronous operation the file is currently blocked on.
348                State::Busy(task) => {
349                    let (inner, opt) = ready!(task.poll(cx));
350                    self.0 = State::Idle(Some(inner));
351                    return Poll::Ready(opt.map(|res| res.map(|inner| DirEntry(Arc::new(inner)))));
352                }
353            }
354        }
355    }
356}
357
358/// An entry in a directory.
359///
360/// A stream of entries in a directory is returned by [`read_dir()`].
361///
362/// For Unix-specific options, import the [`DirEntryExt`][`std::os::unix::fs::DirEntryExt`] trait.
363pub struct DirEntry(Arc<std::fs::DirEntry>);
364
365impl DirEntry {
366    /// Returns the full path to this entry.
367    ///
368    /// The full path is created by joining the original path passed to [`read_dir()`] with the
369    /// name of this entry.
370    ///
371    /// # Examples
372    ///
373    /// ```no_run
374    /// use futures_lite::stream::StreamExt;
375    ///
376    /// # futures_lite::future::block_on(async {
377    /// let mut dir = async_fs::read_dir(".").await?;
378    ///
379    /// while let Some(entry) = dir.try_next().await? {
380    ///     println!("{:?}", entry.path());
381    /// }
382    /// # std::io::Result::Ok(()) });
383    /// ```
384    pub fn path(&self) -> PathBuf {
385        self.0.path()
386    }
387
388    /// Reads the metadata for this entry.
389    ///
390    /// This function will traverse symbolic links to read the metadata.
391    ///
392    /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`]
393    /// instead.
394    ///
395    /// # Errors
396    ///
397    /// An error will be returned in the following situations:
398    ///
399    /// * This entry does not point to an existing file or directory anymore.
400    /// * The current process lacks permissions to read the metadata.
401    /// * Some other I/O error occurred.
402    ///
403    /// # Examples
404    ///
405    /// ```no_run
406    /// use futures_lite::stream::StreamExt;
407    ///
408    /// # futures_lite::future::block_on(async {
409    /// let mut dir = async_fs::read_dir(".").await?;
410    ///
411    /// while let Some(entry) = dir.try_next().await? {
412    ///     println!("{:?}", entry.metadata().await?);
413    /// }
414    /// # std::io::Result::Ok(()) });
415    /// ```
416    pub async fn metadata(&self) -> io::Result<Metadata> {
417        let inner = self.0.clone();
418        unblock(move || inner.metadata()).await
419    }
420
421    /// Reads the file type for this entry.
422    ///
423    /// This function will not traverse symbolic links if this entry points at one.
424    ///
425    /// If you want to read metadata with following symbolic links, use [`metadata()`] instead.
426    ///
427    /// # Errors
428    ///
429    /// An error will be returned in the following situations:
430    ///
431    /// * This entry does not point to an existing file or directory anymore.
432    /// * The current process lacks permissions to read this entry's metadata.
433    /// * Some other I/O error occurred.
434    ///
435    /// # Examples
436    ///
437    /// ```no_run
438    /// use futures_lite::stream::StreamExt;
439    ///
440    /// # futures_lite::future::block_on(async {
441    /// let mut dir = async_fs::read_dir(".").await?;
442    ///
443    /// while let Some(entry) = dir.try_next().await? {
444    ///     println!("{:?}", entry.file_type().await?);
445    /// }
446    /// # std::io::Result::Ok(()) });
447    /// ```
448    pub async fn file_type(&self) -> io::Result<FileType> {
449        let inner = self.0.clone();
450        unblock(move || inner.file_type()).await
451    }
452
453    /// Returns the bare name of this entry without the leading path.
454    ///
455    /// # Examples
456    ///
457    /// ```no_run
458    /// use futures_lite::stream::StreamExt;
459    ///
460    /// # futures_lite::future::block_on(async {
461    /// let mut dir = async_fs::read_dir(".").await?;
462    ///
463    /// while let Some(entry) = dir.try_next().await? {
464    ///     println!("{}", entry.file_name().to_string_lossy());
465    /// }
466    /// # std::io::Result::Ok(()) });
467    /// ```
468    pub fn file_name(&self) -> OsString {
469        self.0.file_name()
470    }
471}
472
473impl fmt::Debug for DirEntry {
474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475        f.debug_tuple("DirEntry").field(&self.path()).finish()
476    }
477}
478
479impl Clone for DirEntry {
480    fn clone(&self) -> Self {
481        DirEntry(self.0.clone())
482    }
483}
484
485#[cfg(unix)]
486impl unix::DirEntryExt for DirEntry {
487    fn ino(&self) -> u64 {
488        self.0.ino()
489    }
490}
491
492/// Reads a symbolic link and returns the path it points to.
493///
494/// # Errors
495///
496/// An error will be returned in the following situations:
497///
498/// * `path` does not point to an existing link.
499/// * Some other I/O error occurred.
500///
501/// # Examples
502///
503/// ```no_run
504/// # futures_lite::future::block_on(async {
505/// let path = async_fs::read_link("a.txt").await?;
506/// # std::io::Result::Ok(()) });
507/// ```
508pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
509    let path = path.as_ref().to_owned();
510    unblock(move || std::fs::read_link(path)).await
511}
512
513/// Reads the entire contents of a file as a string.
514///
515/// This is a convenience function for reading entire files. It pre-allocates a string based on the
516/// file size when available, so it is typically faster than manually opening a file and reading
517/// from it.
518///
519/// If you want to read the contents as raw bytes, use [`read()`] instead.
520///
521/// # Errors
522///
523/// An error will be returned in the following situations:
524///
525/// * `path` does not point to an existing file.
526/// * The current process lacks permissions to read the file.
527/// * The contents of the file cannot be read as a UTF-8 string.
528/// * Some other I/O error occurred.
529///
530/// # Examples
531///
532/// ```no_run
533/// # futures_lite::future::block_on(async {
534/// let contents = async_fs::read_to_string("a.txt").await?;
535/// # std::io::Result::Ok(()) });
536/// ```
537pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
538    let path = path.as_ref().to_owned();
539    unblock(move || std::fs::read_to_string(path)).await
540}
541
542/// Removes an empty directory.
543///
544/// Note that this function can only delete an empty directory. If you want to delete a directory
545/// and all of its contents, use [`remove_dir_all()`] instead.
546///
547/// # Errors
548///
549/// An error will be returned in the following situations:
550///
551/// * `path` is not an existing and empty directory.
552/// * The current process lacks permissions to remove the directory.
553/// * Some other I/O error occurred.
554///
555/// # Examples
556///
557/// ```no_run
558/// # futures_lite::future::block_on(async {
559/// async_fs::remove_dir("./some/directory").await?;
560/// # std::io::Result::Ok(()) });
561/// ```
562pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
563    let path = path.as_ref().to_owned();
564    unblock(move || std::fs::remove_dir(path)).await
565}
566
567/// Removes a directory and all of its contents.
568///
569/// # Errors
570///
571/// An error will be returned in the following situations:
572///
573/// * `path` is not an existing directory.
574/// * The current process lacks permissions to remove the directory.
575/// * Some other I/O error occurred.
576///
577/// # Examples
578///
579/// ```no_run
580/// # futures_lite::future::block_on(async {
581/// async_fs::remove_dir_all("./some/directory").await?;
582/// # std::io::Result::Ok(()) });
583/// ```
584pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
585    let path = path.as_ref().to_owned();
586    unblock(move || std::fs::remove_dir_all(path)).await
587}
588
589/// Removes a file.
590///
591/// # Errors
592///
593/// An error will be returned in the following situations:
594///
595/// * `path` does not point to an existing file.
596/// * The current process lacks permissions to remove the file.
597/// * Some other I/O error occurred.
598///
599/// # Examples
600///
601/// ```no_run
602/// # futures_lite::future::block_on(async {
603/// async_fs::remove_file("a.txt").await?;
604/// # std::io::Result::Ok(()) });
605/// ```
606pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
607    let path = path.as_ref().to_owned();
608    unblock(move || std::fs::remove_file(path)).await
609}
610
611/// Renames a file or directory to a new location.
612///
613/// If a file or directory already exists at the target location, it will be overwritten by this
614/// operation.
615///
616/// # Errors
617///
618/// An error will be returned in the following situations:
619///
620/// * `src` does not point to an existing file or directory.
621/// * `src` and `dst` are on different filesystems.
622/// * The current process lacks permissions to do the rename operation.
623/// * Some other I/O error occurred.
624///
625/// # Examples
626///
627/// ```no_run
628/// # futures_lite::future::block_on(async {
629/// async_fs::rename("a.txt", "b.txt").await?;
630/// # std::io::Result::Ok(()) });
631/// ```
632pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
633    let src = src.as_ref().to_owned();
634    let dst = dst.as_ref().to_owned();
635    unblock(move || std::fs::rename(&src, &dst)).await
636}
637
638/// Changes the permissions of a file or directory.
639///
640/// # Errors
641///
642/// An error will be returned in the following situations:
643///
644/// * `path` does not point to an existing file or directory.
645/// * The current process lacks permissions to change attributes on the file or directory.
646/// * Some other I/O error occurred.
647///
648/// # Examples
649///
650/// ```no_run
651/// # futures_lite::future::block_on(async {
652/// let mut perm = async_fs::metadata("a.txt").await?.permissions();
653/// perm.set_readonly(true);
654/// async_fs::set_permissions("a.txt", perm).await?;
655/// # std::io::Result::Ok(()) });
656/// ```
657pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
658    let path = path.as_ref().to_owned();
659    unblock(move || std::fs::set_permissions(path, perm)).await
660}
661
662/// Reads metadata for a path without following symbolic links.
663///
664/// If you want to follow symbolic links before reading metadata of the target file or directory,
665/// use [`metadata()`] instead.
666///
667/// # Errors
668///
669/// An error will be returned in the following situations:
670///
671/// * `path` does not point to an existing file or directory.
672/// * The current process lacks permissions to read metadata for the path.
673/// * Some other I/O error occurred.
674///
675/// # Examples
676///
677/// ```no_run
678/// # futures_lite::future::block_on(async {
679/// let perm = async_fs::symlink_metadata("a.txt").await?.permissions();
680/// # std::io::Result::Ok(()) });
681/// ```
682pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
683    let path = path.as_ref().to_owned();
684    unblock(move || std::fs::symlink_metadata(path)).await
685}
686
687/// Writes a slice of bytes as the new contents of a file.
688///
689/// This function will create a file if it does not exist, and will entirely replace its contents
690/// if it does.
691///
692/// # Errors
693///
694/// An error will be returned in the following situations:
695///
696/// * The file's parent directory does not exist.
697/// * The current process lacks permissions to write to the file.
698/// * Some other I/O error occurred.
699///
700/// # Examples
701///
702/// ```no_run
703/// # futures_lite::future::block_on(async {
704/// async_fs::write("a.txt", b"Hello world!").await?;
705/// # std::io::Result::Ok(()) });
706/// ```
707pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
708    let path = path.as_ref().to_owned();
709    let contents = contents.as_ref().to_owned();
710    unblock(move || std::fs::write(&path, contents)).await
711}
712
713/// A builder for creating directories with configurable options.
714///
715/// For Unix-specific options, import the [`DirBuilderExt`][`std::os::unix::fs::DirBuilderExt`]
716/// trait.
717#[derive(Debug, Default)]
718pub struct DirBuilder {
719    /// Set to `true` if non-existent parent directories should be created.
720    recursive: bool,
721
722    /// Unix mode for newly created directories.
723    #[cfg(unix)]
724    mode: Option<u32>,
725}
726
727impl DirBuilder {
728    /// Creates a blank set of options.
729    ///
730    /// The [`recursive()`][`DirBuilder::recursive()`] option is initially set to `false`.
731    ///
732    /// # Examples
733    ///
734    /// ```
735    /// use async_fs::DirBuilder;
736    ///
737    /// let builder = DirBuilder::new();
738    /// ```
739    pub fn new() -> DirBuilder {
740        #[cfg(not(unix))]
741        let builder = DirBuilder { recursive: false };
742
743        #[cfg(unix)]
744        let builder = DirBuilder {
745            recursive: false,
746            mode: None,
747        };
748
749        builder
750    }
751
752    /// Sets the option for recursive mode.
753    ///
754    /// When set to `true`, this option means all parent directories should be created recursively
755    /// if they don't exist. Parents are created with the same permissions as the final directory.
756    ///
757    /// This option is initially set to `false`.
758    ///
759    /// # Examples
760    ///
761    /// ```
762    /// use async_fs::DirBuilder;
763    ///
764    /// let mut builder = DirBuilder::new();
765    /// builder.recursive(true);
766    /// ```
767    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
768        self.recursive = recursive;
769        self
770    }
771
772    /// Creates a directory with the configured options.
773    ///
774    /// It is considered an error if the directory already exists unless recursive mode is enabled.
775    ///
776    /// # Errors
777    ///
778    /// An error will be returned in the following situations:
779    ///
780    /// * `path` already points to an existing file or directory.
781    /// * The current process lacks permissions to create the directory or its missing parents.
782    /// * Some other I/O error occurred.
783    ///
784    /// # Examples
785    ///
786    /// ```no_run
787    /// use async_fs::DirBuilder;
788    ///
789    /// # futures_lite::future::block_on(async {
790    /// DirBuilder::new()
791    ///     .recursive(true)
792    ///     .create("./some/directory")
793    ///     .await?;
794    /// # std::io::Result::Ok(()) });
795    /// ```
796    pub fn create<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<()>> {
797        let mut builder = std::fs::DirBuilder::new();
798        builder.recursive(self.recursive);
799
800        #[cfg(unix)]
801        {
802            if let Some(mode) = self.mode {
803                std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
804            }
805        }
806
807        let path = path.as_ref().to_owned();
808        unblock(move || builder.create(path))
809    }
810}
811
812#[cfg(unix)]
813impl unix::DirBuilderExt for DirBuilder {
814    fn mode(&mut self, mode: u32) -> &mut Self {
815        self.mode = Some(mode);
816        self
817    }
818}
819
820/// An open file on the filesystem.
821///
822/// Depending on what options the file was opened with, this type can be used for reading and/or
823/// writing.
824///
825/// Files are automatically closed when they get dropped and any errors detected on closing are
826/// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such
827/// errors need to be handled.
828///
829/// **NOTE:** If writing to a file, make sure to call
830/// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`],
831/// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data
832/// might get lost!
833///
834/// # Examples
835///
836/// Create a new file and write some bytes to it:
837///
838/// ```no_run
839/// use async_fs::File;
840/// use futures_lite::io::AsyncWriteExt;
841///
842/// # futures_lite::future::block_on(async {
843/// let mut file = File::create("a.txt").await?;
844///
845/// file.write_all(b"Hello, world!").await?;
846/// file.flush().await?;
847/// # std::io::Result::Ok(()) });
848/// ```
849///
850/// Read the contents of a file into a vector of bytes:
851///
852/// ```no_run
853/// use async_fs::File;
854/// use futures_lite::io::AsyncReadExt;
855///
856/// # futures_lite::future::block_on(async {
857/// let mut file = File::open("a.txt").await?;
858///
859/// let mut contents = Vec::new();
860/// file.read_to_end(&mut contents).await?;
861/// # std::io::Result::Ok(()) });
862/// ```
863pub struct File {
864    /// Always accessible reference to the file.
865    file: Arc<std::fs::File>,
866
867    /// Performs blocking I/O operations on a thread pool.
868    unblock: Mutex<Unblock<ArcFile>>,
869
870    /// Logical file cursor, tracked when reading from the file.
871    ///
872    /// This will be set to an error if the file is not seekable.
873    read_pos: Option<io::Result<u64>>,
874
875    /// Set to `true` if the file needs flushing.
876    is_dirty: bool,
877}
878
879impl File {
880    /// Creates an async file from a blocking file.
881    fn new(inner: std::fs::File, is_dirty: bool) -> File {
882        let file = Arc::new(inner);
883        let unblock = Mutex::new(Unblock::new(ArcFile(file.clone())));
884        let read_pos = None;
885        File {
886            file,
887            unblock,
888            read_pos,
889            is_dirty,
890        }
891    }
892
893    /// Opens a file in read-only mode.
894    ///
895    /// See the [`OpenOptions::open()`] function for more options.
896    ///
897    /// # Errors
898    ///
899    /// An error will be returned in the following situations:
900    ///
901    /// * `path` does not point to an existing file.
902    /// * The current process lacks permissions to read the file.
903    /// * Some other I/O error occurred.
904    ///
905    /// For more details, see the list of errors documented by [`OpenOptions::open()`].
906    ///
907    /// # Examples
908    ///
909    /// ```no_run
910    /// use async_fs::File;
911    ///
912    /// # futures_lite::future::block_on(async {
913    /// let file = File::open("a.txt").await?;
914    /// # std::io::Result::Ok(()) });
915    /// ```
916    pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
917        let path = path.as_ref().to_owned();
918        let file = unblock(move || std::fs::File::open(path)).await?;
919        Ok(File::new(file, false))
920    }
921
922    /// Opens a file in write-only mode.
923    ///
924    /// This method will create a file if it does not exist, and will truncate it if it does.
925    ///
926    /// See the [`OpenOptions::open`] function for more options.
927    ///
928    /// # Errors
929    ///
930    /// An error will be returned in the following situations:
931    ///
932    /// * The file's parent directory does not exist.
933    /// * The current process lacks permissions to write to the file.
934    /// * Some other I/O error occurred.
935    ///
936    /// For more details, see the list of errors documented by [`OpenOptions::open()`].
937    ///
938    /// # Examples
939    ///
940    /// ```no_run
941    /// use async_fs::File;
942    ///
943    /// # futures_lite::future::block_on(async {
944    /// let file = File::create("a.txt").await?;
945    /// # std::io::Result::Ok(()) });
946    /// ```
947    pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
948        let path = path.as_ref().to_owned();
949        let file = unblock(move || std::fs::File::create(path)).await?;
950        Ok(File::new(file, false))
951    }
952
953    /// Synchronizes OS-internal buffered contents and metadata to disk.
954    ///
955    /// This function will ensure that all in-memory data reaches the filesystem.
956    ///
957    /// This can be used to handle errors that would otherwise only be caught by closing the file.
958    /// When a file is dropped, errors in synchronizing this in-memory data are ignored.
959    ///
960    /// # Examples
961    ///
962    /// ```no_run
963    /// use async_fs::File;
964    /// use futures_lite::io::AsyncWriteExt;
965    ///
966    /// # futures_lite::future::block_on(async {
967    /// let mut file = File::create("a.txt").await?;
968    ///
969    /// file.write_all(b"Hello, world!").await?;
970    /// file.sync_all().await?;
971    /// # std::io::Result::Ok(()) });
972    /// ```
973    pub async fn sync_all(&self) -> io::Result<()> {
974        let mut inner = self.unblock.lock().await;
975        inner.flush().await?;
976        let file = self.file.clone();
977        unblock(move || file.sync_all()).await
978    }
979
980    /// Synchronizes OS-internal buffered contents to disk.
981    ///
982    /// This is similar to [`sync_all()`][`File::sync_data()`], except that file metadata may not
983    /// be synchronized.
984    ///
985    /// This is intended for use cases that must synchronize the contents of the file, but don't
986    /// need the file metadata synchronized to disk.
987    ///
988    /// Note that some platforms may simply implement this in terms of
989    /// [`sync_all()`][`File::sync_data()`].
990    ///
991    /// # Examples
992    ///
993    /// ```no_run
994    /// use async_fs::File;
995    /// use futures_lite::io::AsyncWriteExt;
996    ///
997    /// # futures_lite::future::block_on(async {
998    /// let mut file = File::create("a.txt").await?;
999    ///
1000    /// file.write_all(b"Hello, world!").await?;
1001    /// file.sync_data().await?;
1002    /// # std::io::Result::Ok(()) });
1003    /// ```
1004    pub async fn sync_data(&self) -> io::Result<()> {
1005        let mut inner = self.unblock.lock().await;
1006        inner.flush().await?;
1007        let file = self.file.clone();
1008        unblock(move || file.sync_data()).await
1009    }
1010
1011    /// Truncates or extends the file.
1012    ///
1013    /// If `size` is less than the current file size, then the file will be truncated. If it is
1014    /// greater than the current file size, then the file will be extended to `size` and have all
1015    /// intermediate data filled with zeros.
1016    ///
1017    /// The file's cursor stays at the same position, even if the cursor ends up being past the end
1018    /// of the file after this operation.
1019    ///
1020    /// # Examples
1021    ///
1022    /// ```no_run
1023    /// use async_fs::File;
1024    ///
1025    /// # futures_lite::future::block_on(async {
1026    /// let mut file = File::create("a.txt").await?;
1027    /// file.set_len(10).await?;
1028    /// # std::io::Result::Ok(()) });
1029    /// ```
1030    pub async fn set_len(&self, size: u64) -> io::Result<()> {
1031        let mut inner = self.unblock.lock().await;
1032        inner.flush().await?;
1033        let file = self.file.clone();
1034        unblock(move || file.set_len(size)).await
1035    }
1036
1037    /// Reads the file's metadata.
1038    ///
1039    /// # Examples
1040    ///
1041    /// ```no_run
1042    /// use async_fs::File;
1043    ///
1044    /// # futures_lite::future::block_on(async {
1045    /// let file = File::open("a.txt").await?;
1046    /// let metadata = file.metadata().await?;
1047    /// # std::io::Result::Ok(()) });
1048    /// ```
1049    pub async fn metadata(&self) -> io::Result<Metadata> {
1050        let file = self.file.clone();
1051        unblock(move || file.metadata()).await
1052    }
1053
1054    /// Changes the permissions on the file.
1055    ///
1056    /// # Errors
1057    ///
1058    /// An error will be returned in the following situations:
1059    ///
1060    /// * The current process lacks permissions to change attributes on the file.
1061    /// * Some other I/O error occurred.
1062    ///
1063    /// # Examples
1064    ///
1065    /// ```no_run
1066    /// use async_fs::File;
1067    ///
1068    /// # futures_lite::future::block_on(async {
1069    /// let file = File::create("a.txt").await?;
1070    ///
1071    /// let mut perms = file.metadata().await?.permissions();
1072    /// perms.set_readonly(true);
1073    /// file.set_permissions(perms).await?;
1074    /// # std::io::Result::Ok(()) });
1075    /// ```
1076    pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
1077        let file = self.file.clone();
1078        unblock(move || file.set_permissions(perm)).await
1079    }
1080
1081    /// Repositions the cursor after reading.
1082    ///
1083    /// When reading from a file, actual file reads run asynchronously in the background, which
1084    /// means the real file cursor is usually ahead of the logical cursor, and the data between
1085    /// them is buffered in memory. This kind of buffering is an important optimization.
1086    ///
1087    /// After reading ends, if we decide to perform a write or a seek operation, the real file
1088    /// cursor must first be repositioned back to the correct logical position.
1089    fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1090        if let Some(Ok(read_pos)) = self.read_pos {
1091            ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?;
1092        }
1093        self.read_pos = None;
1094        Poll::Ready(Ok(()))
1095    }
1096}
1097
1098impl fmt::Debug for File {
1099    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100        self.file.fmt(f)
1101    }
1102}
1103
1104impl From<std::fs::File> for File {
1105    fn from(inner: std::fs::File) -> File {
1106        File::new(inner, true)
1107    }
1108}
1109
1110#[cfg(unix)]
1111impl std::os::unix::io::AsRawFd for File {
1112    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
1113        self.file.as_raw_fd()
1114    }
1115}
1116
1117#[cfg(windows)]
1118impl std::os::windows::io::AsRawHandle for File {
1119    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
1120        self.file.as_raw_handle()
1121    }
1122}
1123
1124#[cfg(unix)]
1125impl From<std::os::unix::io::OwnedFd> for File {
1126    fn from(fd: std::os::unix::io::OwnedFd) -> Self {
1127        File::from(std::fs::File::from(fd))
1128    }
1129}
1130
1131#[cfg(windows)]
1132impl From<std::os::windows::io::OwnedHandle> for File {
1133    fn from(fd: std::os::windows::io::OwnedHandle) -> Self {
1134        File::from(std::fs::File::from(fd))
1135    }
1136}
1137
1138#[cfg(unix)]
1139impl std::os::unix::io::AsFd for File {
1140    fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
1141        self.file.as_fd()
1142    }
1143}
1144
1145#[cfg(windows)]
1146impl std::os::windows::io::AsHandle for File {
1147    fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> {
1148        self.file.as_handle()
1149    }
1150}
1151
1152impl AsyncRead for File {
1153    fn poll_read(
1154        mut self: Pin<&mut Self>,
1155        cx: &mut Context<'_>,
1156        buf: &mut [u8],
1157    ) -> Poll<io::Result<usize>> {
1158        // Before reading begins, remember the current cursor position.
1159        if self.read_pos.is_none() {
1160            // Initialize the logical cursor to the current position in the file.
1161            self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0))));
1162        }
1163
1164        let n = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?;
1165
1166        // Update the logical cursor if the file is seekable.
1167        if let Some(Ok(pos)) = self.read_pos.as_mut() {
1168            *pos += n as u64;
1169        }
1170
1171        Poll::Ready(Ok(n))
1172    }
1173}
1174
1175impl AsyncWrite for File {
1176    fn poll_write(
1177        mut self: Pin<&mut Self>,
1178        cx: &mut Context<'_>,
1179        buf: &[u8],
1180    ) -> Poll<io::Result<usize>> {
1181        ready!(self.poll_reposition(cx))?;
1182        self.is_dirty = true;
1183        Pin::new(self.unblock.get_mut()).poll_write(cx, buf)
1184    }
1185
1186    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1187        if self.is_dirty {
1188            ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?;
1189            self.is_dirty = false;
1190        }
1191        Poll::Ready(Ok(()))
1192    }
1193
1194    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1195        Pin::new(self.unblock.get_mut()).poll_close(cx)
1196    }
1197}
1198
1199impl AsyncSeek for File {
1200    fn poll_seek(
1201        mut self: Pin<&mut Self>,
1202        cx: &mut Context<'_>,
1203        pos: SeekFrom,
1204    ) -> Poll<io::Result<u64>> {
1205        ready!(self.poll_reposition(cx))?;
1206        Pin::new(self.unblock.get_mut()).poll_seek(cx, pos)
1207    }
1208}
1209
1210/// A wrapper around `Arc<std::fs::File>` that implements `Read`, `Write`, and `Seek`.
1211struct ArcFile(Arc<std::fs::File>);
1212
1213impl io::Read for ArcFile {
1214    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1215        (&*self.0).read(buf)
1216    }
1217}
1218
1219impl io::Write for ArcFile {
1220    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1221        (&*self.0).write(buf)
1222    }
1223
1224    fn flush(&mut self) -> io::Result<()> {
1225        (&*self.0).flush()
1226    }
1227}
1228
1229impl io::Seek for ArcFile {
1230    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
1231        (&*self.0).seek(pos)
1232    }
1233}
1234
1235/// A builder for opening files with configurable options.
1236///
1237/// Files can be opened in [`read`][`OpenOptions::read()`] and/or
1238/// [`write`][`OpenOptions::write()`] mode.
1239///
1240/// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that
1241/// moves the file cursor to the end of file before every write operation.
1242///
1243/// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening,
1244/// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a
1245/// new file with [`create_new`][`OpenOptions::create_new()`].
1246///
1247/// # Examples
1248///
1249/// Open a file for reading:
1250///
1251/// ```no_run
1252/// use async_fs::OpenOptions;
1253///
1254/// # futures_lite::future::block_on(async {
1255/// let file = OpenOptions::new()
1256///     .read(true)
1257///     .open("a.txt")
1258///     .await?;
1259/// # std::io::Result::Ok(()) });
1260/// ```
1261///
1262/// Open a file for both reading and writing, and create it if it doesn't exist yet:
1263///
1264/// ```no_run
1265/// use async_fs::OpenOptions;
1266///
1267/// # futures_lite::future::block_on(async {
1268/// let file = OpenOptions::new()
1269///     .read(true)
1270///     .write(true)
1271///     .create(true)
1272///     .open("a.txt")
1273///     .await?;
1274/// # std::io::Result::Ok(()) });
1275/// ```
1276#[derive(Clone, Debug)]
1277pub struct OpenOptions(std::fs::OpenOptions);
1278
1279impl OpenOptions {
1280    /// Creates a blank set of options.
1281    ///
1282    /// All options are initially set to `false`.
1283    ///
1284    /// # Examples
1285    ///
1286    /// ```no_run
1287    /// use async_fs::OpenOptions;
1288    ///
1289    /// # futures_lite::future::block_on(async {
1290    /// let file = OpenOptions::new()
1291    ///     .read(true)
1292    ///     .open("a.txt")
1293    ///     .await?;
1294    /// # std::io::Result::Ok(()) });
1295    /// ```
1296    pub fn new() -> OpenOptions {
1297        OpenOptions(std::fs::OpenOptions::new())
1298    }
1299
1300    /// Configures the option for read mode.
1301    ///
1302    /// When set to `true`, this option means the file will be readable after opening.
1303    ///
1304    /// # Examples
1305    ///
1306    /// ```no_run
1307    /// use async_fs::OpenOptions;
1308    ///
1309    /// # futures_lite::future::block_on(async {
1310    /// let file = OpenOptions::new()
1311    ///     .read(true)
1312    ///     .open("a.txt")
1313    ///     .await?;
1314    /// # std::io::Result::Ok(()) });
1315    /// ```
1316    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
1317        self.0.read(read);
1318        self
1319    }
1320
1321    /// Configures the option for write mode.
1322    ///
1323    /// When set to `true`, this option means the file will be writable after opening.
1324    ///
1325    /// If the file already exists, write calls on it will overwrite the previous contents without
1326    /// truncating it.
1327    ///
1328    /// # Examples
1329    ///
1330    /// ```no_run
1331    /// use async_fs::OpenOptions;
1332    ///
1333    /// # futures_lite::future::block_on(async {
1334    /// let file = OpenOptions::new()
1335    ///     .write(true)
1336    ///     .open("a.txt")
1337    ///     .await?;
1338    /// # std::io::Result::Ok(()) });
1339    /// ```
1340    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
1341        self.0.write(write);
1342        self
1343    }
1344
1345    /// Configures the option for append mode.
1346    ///
1347    /// When set to `true`, this option means the file will be writable after opening and the file
1348    /// cursor will be moved to the end of file before every write operaiton.
1349    ///
1350    /// # Examples
1351    ///
1352    /// ```no_run
1353    /// use async_fs::OpenOptions;
1354    ///
1355    /// # futures_lite::future::block_on(async {
1356    /// let file = OpenOptions::new()
1357    ///     .append(true)
1358    ///     .open("a.txt")
1359    ///     .await?;
1360    /// # std::io::Result::Ok(()) });
1361    /// ```
1362    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
1363        self.0.append(append);
1364        self
1365    }
1366
1367    /// Configures the option for truncating the previous file.
1368    ///
1369    /// When set to `true`, the file will be truncated to the length of 0 bytes.
1370    ///
1371    /// The file must be opened in [`write`][`OpenOptions::write()`] or
1372    /// [`append`][`OpenOptions::append()`] mode for truncation to work.
1373    ///
1374    /// # Examples
1375    ///
1376    /// ```no_run
1377    /// use async_fs::OpenOptions;
1378    ///
1379    /// # futures_lite::future::block_on(async {
1380    /// let file = OpenOptions::new()
1381    ///     .write(true)
1382    ///     .truncate(true)
1383    ///     .open("a.txt")
1384    ///     .await?;
1385    /// # std::io::Result::Ok(()) });
1386    /// ```
1387    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
1388        self.0.truncate(truncate);
1389        self
1390    }
1391
1392    /// Configures the option for creating a new file if it doesn't exist.
1393    ///
1394    /// When set to `true`, this option means a new file will be created if it doesn't exist.
1395    ///
1396    /// The file must be opened in [`write`][`OpenOptions::write()`] or
1397    /// [`append`][`OpenOptions::append()`] mode for file creation to work.
1398    ///
1399    /// # Examples
1400    ///
1401    /// ```no_run
1402    /// use async_fs::OpenOptions;
1403    ///
1404    /// # futures_lite::future::block_on(async {
1405    /// let file = OpenOptions::new()
1406    ///     .write(true)
1407    ///     .create(true)
1408    ///     .open("a.txt")
1409    ///     .await?;
1410    /// # std::io::Result::Ok(()) });
1411    /// ```
1412    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
1413        self.0.create(create);
1414        self
1415    }
1416
1417    /// Configures the option for creating a new file or failing if it already exists.
1418    ///
1419    /// When set to `true`, this option means a new file will be created, or the open operation
1420    /// will fail if the file already exists.
1421    ///
1422    /// The file must be opened in [`write`][`OpenOptions::write()`] or
1423    /// [`append`][`OpenOptions::append()`] mode for file creation to work.
1424    ///
1425    /// # Examples
1426    ///
1427    /// ```no_run
1428    /// use async_fs::OpenOptions;
1429    ///
1430    /// # futures_lite::future::block_on(async {
1431    /// let file = OpenOptions::new()
1432    ///     .write(true)
1433    ///     .create_new(true)
1434    ///     .open("a.txt")
1435    ///     .await?;
1436    /// # std::io::Result::Ok(()) });
1437    /// ```
1438    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
1439        self.0.create_new(create_new);
1440        self
1441    }
1442
1443    /// Opens a file with the configured options.
1444    ///
1445    /// # Errors
1446    ///
1447    /// An error will be returned in the following situations:
1448    ///
1449    /// * The file does not exist and neither [`create`] nor [`create_new`] were set.
1450    /// * The file's parent directory does not exist.
1451    /// * The current process lacks permissions to open the file in the configured mode.
1452    /// * The file already exists and [`create_new`] was set.
1453    /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't,
1454    ///   or none of [`read`], [`write`], and [`append`] modes was set.
1455    /// * An OS-level occurred, like too many files are open or the file name is too long.
1456    /// * Some other I/O error occurred.
1457    ///
1458    /// [`read`]: `OpenOptions::read()`
1459    /// [`write`]: `OpenOptions::write()`
1460    /// [`append`]: `OpenOptions::append()`
1461    /// [`truncate`]: `OpenOptions::truncate()`
1462    /// [`create`]: `OpenOptions::create()`
1463    /// [`create_new`]: `OpenOptions::create_new()`
1464    ///
1465    /// # Examples
1466    ///
1467    /// ```no_run
1468    /// use async_fs::OpenOptions;
1469    ///
1470    /// # futures_lite::future::block_on(async {
1471    /// let file = OpenOptions::new()
1472    ///     .read(true)
1473    ///     .open("a.txt")
1474    ///     .await?;
1475    /// # std::io::Result::Ok(()) });
1476    /// ```
1477    pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
1478        let path = path.as_ref().to_owned();
1479        let options = self.0.clone();
1480        async move {
1481            let file = unblock(move || options.open(path)).await?;
1482            Ok(File::new(file, false))
1483        }
1484    }
1485}
1486
1487impl Default for OpenOptions {
1488    fn default() -> Self {
1489        Self::new()
1490    }
1491}
1492
1493#[cfg(unix)]
1494impl unix::OpenOptionsExt for OpenOptions {
1495    fn mode(&mut self, mode: u32) -> &mut Self {
1496        self.0.mode(mode);
1497        self
1498    }
1499
1500    fn custom_flags(&mut self, flags: i32) -> &mut Self {
1501        self.0.custom_flags(flags);
1502        self
1503    }
1504}
1505
1506#[cfg(windows)]
1507impl windows::OpenOptionsExt for OpenOptions {
1508    fn access_mode(&mut self, access: u32) -> &mut Self {
1509        self.0.access_mode(access);
1510        self
1511    }
1512
1513    fn share_mode(&mut self, val: u32) -> &mut Self {
1514        self.0.share_mode(val);
1515        self
1516    }
1517
1518    fn custom_flags(&mut self, flags: u32) -> &mut Self {
1519        self.0.custom_flags(flags);
1520        self
1521    }
1522
1523    fn attributes(&mut self, val: u32) -> &mut Self {
1524        self.0.attributes(val);
1525        self
1526    }
1527
1528    fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
1529        self.0.security_qos_flags(flags);
1530        self
1531    }
1532}
1533
1534mod __private {
1535    #[doc(hidden)]
1536    pub trait Sealed {}
1537
1538    impl Sealed for super::OpenOptions {}
1539    impl Sealed for super::File {}
1540    impl Sealed for super::DirBuilder {}
1541    impl Sealed for super::DirEntry {}
1542}
1543
1544/// Unix-specific extensions.
1545#[cfg(unix)]
1546pub mod unix {
1547    use super::__private::Sealed;
1548    use super::*;
1549
1550    #[doc(no_inline)]
1551    pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
1552
1553    /// Creates a new symbolic link on the filesystem.
1554    ///
1555    /// The `dst` path will be a symbolic link pointing to the `src` path.
1556    ///
1557    /// # Examples
1558    ///
1559    /// ```no_run
1560    /// # futures_lite::future::block_on(async {
1561    /// async_fs::unix::symlink("a.txt", "b.txt").await?;
1562    /// # std::io::Result::Ok(()) });
1563    /// ```
1564    pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
1565        let src = src.as_ref().to_owned();
1566        let dst = dst.as_ref().to_owned();
1567        unblock(move || std::os::unix::fs::symlink(&src, &dst)).await
1568    }
1569
1570    /// Unix-specific extensions to [`DirBuilder`].
1571    pub trait DirBuilderExt: Sealed {
1572        /// Sets the mode to create new directories with.
1573        ///
1574        /// This option defaults to `0o777`.
1575        ///
1576        /// # Examples
1577        ///
1578        /// ```no_run
1579        /// use async_fs::{DirBuilder, unix::DirBuilderExt};
1580        ///
1581        /// let mut builder = DirBuilder::new();
1582        /// builder.mode(0o755);
1583        /// ```
1584        fn mode(&mut self, mode: u32) -> &mut Self;
1585    }
1586
1587    /// Unix-specific extension methods for [`DirEntry`].
1588    pub trait DirEntryExt: Sealed {
1589        /// Returns the underlying `d_ino` field in the contained `dirent` structure.
1590        ///
1591        /// # Examples
1592        ///
1593        /// ```no_run
1594        /// use async_fs::unix::DirEntryExt;
1595        /// use futures_lite::stream::StreamExt;
1596        ///
1597        /// # futures_lite::future::block_on(async {
1598        /// let mut entries = async_fs::read_dir(".").await?;
1599        ///
1600        /// while let Some(entry) = entries.try_next().await? {
1601        ///     println!("{:?}: {}", entry.file_name(), entry.ino());
1602        /// }
1603        /// # std::io::Result::Ok(()) });
1604        /// ```
1605        fn ino(&self) -> u64;
1606    }
1607
1608    /// Unix-specific extensions to [`OpenOptions`].
1609    pub trait OpenOptionsExt: Sealed {
1610        /// Sets the mode bits that a new file will be created with.
1611        ///
1612        /// If a new file is created as part of an [`OpenOptions::open()`] call then this
1613        /// specified `mode` will be used as the permission bits for the new file.
1614        ///
1615        /// If no `mode` is set, the default of `0o666` will be used.
1616        /// The operating system masks out bits with the system's `umask`, to produce
1617        /// the final permissions.
1618        ///
1619        /// # Examples
1620        ///
1621        /// ```no_run
1622        /// use async_fs::{OpenOptions, unix::OpenOptionsExt};
1623        ///
1624        /// # futures_lite::future::block_on(async {
1625        /// let mut options = OpenOptions::new();
1626        /// // Read/write permissions for owner and read permissions for others.
1627        /// options.mode(0o644);
1628        /// let file = options.open("foo.txt").await?;
1629        /// # std::io::Result::Ok(()) });
1630        /// ```
1631        fn mode(&mut self, mode: u32) -> &mut Self;
1632
1633        /// Passes custom flags to the `flags` argument of `open`.
1634        ///
1635        /// The bits that define the access mode are masked out with `O_ACCMODE`, to
1636        /// ensure they do not interfere with the access mode set by Rust's options.
1637        ///
1638        /// Custom flags can only set flags, not remove flags set by Rust's options.
1639        /// This options overwrites any previously set custom flags.
1640        ///
1641        /// # Examples
1642        ///
1643        /// ```no_run
1644        /// use async_fs::{OpenOptions, unix::OpenOptionsExt};
1645        ///
1646        /// # futures_lite::future::block_on(async {
1647        /// let mut options = OpenOptions::new();
1648        /// options.write(true);
1649        /// options.custom_flags(libc::O_NOFOLLOW);
1650        /// let file = options.open("foo.txt").await?;
1651        /// # std::io::Result::Ok(()) });
1652        /// ```
1653        fn custom_flags(&mut self, flags: i32) -> &mut Self;
1654    }
1655}
1656
1657/// Windows-specific extensions.
1658#[cfg(windows)]
1659pub mod windows {
1660    use super::__private::Sealed;
1661    use super::*;
1662
1663    #[doc(no_inline)]
1664    pub use std::os::windows::fs::MetadataExt;
1665
1666    /// Creates a new directory symbolic link on the filesystem.
1667    ///
1668    /// The `dst` path will be a directory symbolic link pointing to the `src` path.
1669    ///
1670    /// # Examples
1671    ///
1672    /// ```no_run
1673    /// # futures_lite::future::block_on(async {
1674    /// async_fs::windows::symlink_dir("a", "b").await?;
1675    /// # std::io::Result::Ok(()) });
1676    /// ```
1677    pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
1678        let src = src.as_ref().to_owned();
1679        let dst = dst.as_ref().to_owned();
1680        unblock(move || std::os::windows::fs::symlink_dir(&src, &dst)).await
1681    }
1682
1683    /// Creates a new file symbolic link on the filesystem.
1684    ///
1685    /// The `dst` path will be a file symbolic link pointing to the `src` path.
1686    ///
1687    /// # Examples
1688    ///
1689    /// ```no_run
1690    /// # futures_lite::future::block_on(async {
1691    /// async_fs::windows::symlink_file("a.txt", "b.txt").await?;
1692    /// # std::io::Result::Ok(()) });
1693    /// ```
1694    pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
1695        let src = src.as_ref().to_owned();
1696        let dst = dst.as_ref().to_owned();
1697        unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await
1698    }
1699
1700    /// Windows-specific extensions to [`OpenOptions`].
1701    pub trait OpenOptionsExt: Sealed {
1702        /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
1703        /// with the specified value.
1704        ///
1705        /// This will override the `read`, `write`, and `append` flags on the
1706        /// [`OpenOptions`] structure. This method provides fine-grained control over
1707        /// the permissions to read, write and append data, attributes (like hidden
1708        /// and system), and extended attributes.
1709        ///
1710        /// # Examples
1711        ///
1712        /// ```no_run
1713        /// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1714        ///
1715        /// # futures_lite::future::block_on(async {
1716        /// // Open without read and write permission, for example if you only need
1717        /// // to call `stat` on the file
1718        /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
1719        /// # std::io::Result::Ok(()) });
1720        /// ```
1721        ///
1722        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1723        fn access_mode(&mut self, access: u32) -> &mut Self;
1724
1725        /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
1726        /// the specified value.
1727        ///
1728        /// By default `share_mode` is set to
1729        /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
1730        /// other processes to read, write, and delete/rename the same file
1731        /// while it is open. Removing any of the flags will prevent other
1732        /// processes from performing the corresponding operation until the file
1733        /// handle is closed.
1734        ///
1735        /// # Examples
1736        ///
1737        /// ```no_run
1738        /// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1739        ///
1740        /// # futures_lite::future::block_on(async {
1741        /// // Do not allow others to read or modify this file while we have it open
1742        /// // for writing.
1743        /// let file = OpenOptions::new()
1744        ///     .write(true)
1745        ///     .share_mode(0)
1746        ///     .open("foo.txt")
1747        ///     .await?;
1748        /// # std::io::Result::Ok(()) });
1749        /// ```
1750        ///
1751        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1752        fn share_mode(&mut self, val: u32) -> &mut Self;
1753
1754        /// Sets extra flags for the `dwFileFlags` argument to the call to
1755        /// [`CreateFile2`] to the specified value (or combines it with
1756        /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
1757        /// for [`CreateFile`]).
1758        ///
1759        /// Custom flags can only set flags, not remove flags set by Rust's options.
1760        /// This option overwrites any previously set custom flags.
1761        ///
1762        /// # Examples
1763        ///
1764        /// ```no_run
1765        /// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1766        ///
1767        /// # futures_lite::future::block_on(async {
1768        /// let file = OpenOptions::new()
1769        ///     .create(true)
1770        ///     .write(true)
1771        ///     .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE)
1772        ///     .open("foo.txt")
1773        ///     .await?;
1774        /// # std::io::Result::Ok(()) });
1775        /// ```
1776        ///
1777        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1778        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1779        fn custom_flags(&mut self, flags: u32) -> &mut Self;
1780
1781        /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
1782        /// the specified value (or combines it with `custom_flags` and
1783        /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
1784        /// [`CreateFile`]).
1785        ///
1786        /// If a _new_ file is created because it does not yet exist and
1787        /// `.create(true)` or `.create_new(true)` are specified, the new file is
1788        /// given the attributes declared with `.attributes()`.
1789        ///
1790        /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
1791        /// existing attributes are preserved and combined with the ones declared
1792        /// with `.attributes()`.
1793        ///
1794        /// In all other cases the attributes get ignored.
1795        ///
1796        /// # Examples
1797        ///
1798        /// ```no_run
1799        /// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1800        ///
1801        /// # futures_lite::future::block_on(async {
1802        /// let file = OpenOptions::new()
1803        ///     .write(true)
1804        ///     .create(true)
1805        ///     .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN)
1806        ///     .open("foo.txt")
1807        ///     .await?;
1808        /// # std::io::Result::Ok(()) });
1809        /// ```
1810        ///
1811        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1812        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1813        fn attributes(&mut self, val: u32) -> &mut Self;
1814
1815        /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
1816        /// the specified value (or combines it with `custom_flags` and `attributes`
1817        /// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
1818        ///
1819        /// By default `security_qos_flags` is not set. It should be specified when
1820        /// opening a named pipe, to control to which degree a server process can
1821        /// act on behalf of a client process (security impersonation level).
1822        ///
1823        /// When `security_qos_flags` is not set, a malicious program can gain the
1824        /// elevated privileges of a privileged Rust process when it allows opening
1825        /// user-specified paths, by tricking it into opening a named pipe. So
1826        /// arguably `security_qos_flags` should also be set when opening arbitrary
1827        /// paths. However the bits can then conflict with other flags, specifically
1828        /// `FILE_FLAG_OPEN_NO_RECALL`.
1829        ///
1830        /// For information about possible values, see [Impersonation Levels] on the
1831        /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
1832        /// automatically when using this method.
1833        ///
1834        /// # Examples
1835        ///
1836        /// ```no_run
1837        /// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1838        ///
1839        /// # futures_lite::future::block_on(async {
1840        /// let file = OpenOptions::new()
1841        ///     .write(true)
1842        ///     .create(true)
1843        ///     .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION)
1844        ///     .open(r"\\.\pipe\MyPipe")
1845        ///     .await?;
1846        /// # std::io::Result::Ok(()) });
1847        /// ```
1848        ///
1849        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1850        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1851        /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
1852        fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
1853    }
1854}