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}