From 2953e09593c3e705fe34d154425c48b6473a76b5 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) Kassimo Qian" Date: Sat, 4 Dec 2021 12:35:18 -0800 Subject: [PATCH] Add `fs::try_exists` --- tokio/src/fs/mod.rs | 3 +++ tokio/src/fs/try_exists.rs | 31 +++++++++++++++++++++++++++ tokio/tests/fs_try_exists.rs | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 tokio/src/fs/try_exists.rs create mode 100644 tokio/tests/fs_try_exists.rs diff --git a/tokio/src/fs/mod.rs b/tokio/src/fs/mod.rs index ca0264b3678..41e6a351d97 100644 --- a/tokio/src/fs/mod.rs +++ b/tokio/src/fs/mod.rs @@ -84,6 +84,9 @@ pub use self::write::write; mod copy; pub use self::copy::copy; +mod try_exists; +pub use self::try_exists::try_exists; + #[cfg(test)] mod mocks; diff --git a/tokio/src/fs/try_exists.rs b/tokio/src/fs/try_exists.rs new file mode 100644 index 00000000000..d7e5154f3e4 --- /dev/null +++ b/tokio/src/fs/try_exists.rs @@ -0,0 +1,31 @@ +use crate::fs::asyncify; +use std::path::Path; + +/// Returns `Ok(true)` if the path points at an existing entity. +/// +/// This function will traverse symbolic links to query information about the +/// destination file. In case of broken symbolic links this will return `Ok(false)`. +/// +/// This is the async equivalent of [`std::fs::try_exists`][std]. +/// +/// [std]: fn@std::fs::try_exists +/// +/// # Examples +/// +/// ```no_run +/// use tokio::fs; +/// +/// # async fn dox() -> std::io::Result<()> { +/// fs::try_exists("foo.txt").await?; +/// # Ok(()) +/// # } +/// ``` + +pub async fn try_exists(path: impl AsRef) -> Result { + let path = path.as_ref().to_owned(); + match asyncify(move || std::fs::metadata(path)).await { + Ok(_) => Ok(true), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error), + } +} diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs new file mode 100644 index 00000000000..b6813a1fe6b --- /dev/null +++ b/tokio/tests/fs_try_exists.rs @@ -0,0 +1,41 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] + +use std::os::unix::prelude::PermissionsExt; + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn try_exists() { + let dir = tempdir().unwrap(); + + let existing_path = dir.path().join("foo.txt"); + fs::write(&existing_path, b"Hello File!").await.unwrap(); + let nonexisting_path = dir.path().join("bar.txt"); + + assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); + assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); + + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); +}