Skip to main content

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