Skip to content

Commit

Permalink
fs: add fs::try_exists (#4299)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkassimo committed Feb 21, 2023
1 parent 3ea5cc5 commit 12f81ff
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
3 changes: 3 additions & 0 deletions tokio/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,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;

Expand Down
34 changes: 34 additions & 0 deletions tokio/src/fs/try_exists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::fs::asyncify;

use std::io;
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::path::Path::try_exists`][std].
///
/// [std]: fn@std::path::Path::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<Path>) -> io::Result<bool> {
let path = path.as_ref().to_owned();
// std's Path::try_exists is not available for current Rust min supported version.
// Current implementation is based on its internal implementation instead.
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),
}
}
44 changes: 44 additions & 0 deletions tokio/tests/fs_try_exists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![warn(rust_2018_idioms)]
#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support file operations

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!(fs::try_exists(existing_path).await.unwrap());
assert!(!fs::try_exists(nonexisting_path).await.unwrap());
// FreeBSD root user always has permission to stat.
#[cfg(all(unix, not(target_os = "freebsd")))]
{
use std::os::unix::prelude::PermissionsExt;
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
);
}
}

0 comments on commit 12f81ff

Please sign in to comment.