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