diff options
author | Dmitri Shkurski <45545354+shkurski@users.noreply.github.com> | 2020-05-21 13:49:36 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-21 12:49:36 +0200 |
commit | 8fda5f1984e5c56548ea5bb0cf7d0f5c57c3a190 (patch) | |
tree | 334caa3afa736147218a55216c609b9305cf4adf /tokio | |
parent | 7cb5e3460c67bdfbe29fb6abf02e2d3cfc13625e (diff) |
fs: add DirBuilder (#2524)
The initial idea was to implement a thin wrapper around an internally
held `std::fs::DirBuilder` instance. This, however, didn't work due to
`std::fs::DirBuilder` not having a Copy/Clone traits implemented, which
are necessary for constructing an instance to move-capture it into a
closure.
Instead, we mirror `std::fs::DirBuilder` configuration by storing the
`recursive` and (unix-only) `mode` parameters locally, which are then
used to construct an `std::fs::DirBuilder` instance on-the-fly.
This commit also mirrors the (unix-only) DirBuilderExt trait from std.
Fixes: #2369
Diffstat (limited to 'tokio')
-rw-r--r-- | tokio/src/fs/dir_builder.rs | 117 | ||||
-rw-r--r-- | tokio/src/fs/mod.rs | 3 | ||||
-rw-r--r-- | tokio/src/fs/os/unix/dir_builder_ext.rs | 29 | ||||
-rw-r--r-- | tokio/src/fs/os/unix/mod.rs | 3 | ||||
-rw-r--r-- | tokio/tests/fs_dir.rs | 19 |
5 files changed, 170 insertions, 1 deletions
diff --git a/tokio/src/fs/dir_builder.rs b/tokio/src/fs/dir_builder.rs new file mode 100644 index 00000000..8752a371 --- /dev/null +++ b/tokio/src/fs/dir_builder.rs @@ -0,0 +1,117 @@ +use crate::fs::asyncify; + +use std::io; +use std::path::Path; + +/// A builder for creating directories in various manners. +/// +/// Additional Unix-specific options are available via importing the +/// [`DirBuilderExt`] trait. +/// +/// This is a specialized version of [`std::fs::DirBuilder`] for usage on +/// the Tokio runtime. +/// +/// [std::fs::DirBuilder]: std::fs::DirBuilder +/// [`DirBuilderExt`]: crate::fs::os::unix::DirBuilderExt +#[derive(Debug, Default)] +pub struct DirBuilder { + /// Indicates whether to create parent directories if they are missing. + recursive: bool, + + /// Set the Unix mode for newly created directories. + #[cfg(unix)] + pub(super) mode: Option<u32>, +} + +impl DirBuilder { + /// Creates a new set of options with default mode/security settings for all + /// platforms and also non-recursive. + /// + /// This is an async version of [`std::fs::DirBuilder::new`][std] + /// + /// [std]: std::fs::DirBuilder::new + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + pub fn new() -> Self { + Default::default() + } + + /// Indicates whether to create directories recursively (including all parent directories). + /// Parents that do not exist are created with the same security and permissions settings. + /// + /// This option defaults to `false`. + /// + /// This is an async version of [`std::fs::DirBuilder::recursive`][std] + /// + /// [std]: std::fs::DirBuilder::recursive + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Creates the specified directory with the configured options. + /// + /// It is considered an error if the directory already exists unless + /// recursive mode is enabled. + /// + /// This is an async version of [`std::fs::DirBuilder::create`][std] + /// + /// [std]: std::fs::DirBuilder::create + /// + /// # Errors + /// + /// An error will be returned under the following circumstances: + /// + /// * Path already points to an existing file. + /// * Path already points to an existing directory and the mode is + /// non-recursive. + /// * The calling process doesn't have permissions to create the directory + /// or its missing parents. + /// * Other I/O error occurred. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::DirBuilder; + /// use std::io; + /// + /// #[tokio::main] + /// async fn main() -> io::Result<()> { + /// DirBuilder::new() + /// .recursive(true) + /// .create("/tmp/foo/bar/baz") + /// .await?; + /// + /// Ok(()) + /// } + /// ``` + pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { + let path = path.as_ref().to_owned(); + let mut builder = std::fs::DirBuilder::new(); + builder.recursive(self.recursive); + + #[cfg(unix)] + { + if let Some(mode) = self.mode { + std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode); + } + } + + asyncify(move || builder.create(path)).await + } +} diff --git a/tokio/src/fs/mod.rs b/tokio/src/fs/mod.rs index 3eb03764..a2b062b1 100644 --- a/tokio/src/fs/mod.rs +++ b/tokio/src/fs/mod.rs @@ -33,6 +33,9 @@ pub use self::create_dir::create_dir; mod create_dir_all; pub use self::create_dir_all::create_dir_all; +mod dir_builder; +pub use self::dir_builder::DirBuilder; + mod file; pub use self::file::File; diff --git a/tokio/src/fs/os/unix/dir_builder_ext.rs b/tokio/src/fs/os/unix/dir_builder_ext.rs new file mode 100644 index 00000000..e9a25b95 --- /dev/null +++ b/tokio/src/fs/os/unix/dir_builder_ext.rs @@ -0,0 +1,29 @@ +use crate::fs::dir_builder::DirBuilder; + +/// Unix-specific extensions to [`DirBuilder`]. +/// +/// [`DirBuilder`]: crate::fs::DirBuilder +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. + /// + /// This option defaults to 0o777. + /// + /// # Examples + /// + /// + /// ```no_run + /// use tokio::fs::DirBuilder; + /// use tokio::fs::os::unix::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o775); + /// ``` + fn mode(&mut self, mode: u32) -> &mut Self; +} + +impl DirBuilderExt for DirBuilder { + fn mode(&mut self, mode: u32) -> &mut Self { + self.mode = Some(mode); + self + } +} diff --git a/tokio/src/fs/os/unix/mod.rs b/tokio/src/fs/os/unix/mod.rs index 3b0bec38..030eaf8a 100644 --- a/tokio/src/fs/os/unix/mod.rs +++ b/tokio/src/fs/os/unix/mod.rs @@ -2,3 +2,6 @@ mod symlink; pub use self::symlink::symlink; + +mod dir_builder_ext; +pub use self::dir_builder_ext::DirBuilderExt; diff --git a/tokio/tests/fs_dir.rs b/tokio/tests/fs_dir.rs index eaff59da..6355ef05 100644 --- a/tokio/tests/fs_dir.rs +++ b/tokio/tests/fs_dir.rs @@ -2,7 +2,7 @@ #![cfg(feature = "full")] use tokio::fs; -use tokio_test::assert_ok; +use tokio_test::{assert_err, assert_ok}; use std::sync::{Arc, Mutex}; use tempfile::tempdir; @@ -29,6 +29,23 @@ async fn create_all() { } #[tokio::test] +async fn build_dir() { + let base_dir = tempdir().unwrap(); + let new_dir = base_dir.path().join("foo").join("bar"); + let new_dir_2 = new_dir.clone(); + + assert_ok!(fs::DirBuilder::new().recursive(true).create(new_dir).await); + + assert!(new_dir_2.is_dir()); + assert_err!( + fs::DirBuilder::new() + .recursive(false) + .create(new_dir_2) + .await + ); +} + +#[tokio::test] async fn remove() { let base_dir = tempdir().unwrap(); let new_dir = base_dir.path().join("foo"); |