From 0ca4ea0b1311568a4cd7d532d06eace36b17118e Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 7 Oct 2022 20:02:04 +0100 Subject: [PATCH 1/3] Add `PathLike` trait --- library/std/src/env.rs | 8 +- library/std/src/fs.rs | 98 +++++++++++----------- library/std/src/path.rs | 24 ++++++ library/std/src/process.rs | 6 +- src/test/ui/async-await/issue-72442.stderr | 5 +- 5 files changed, 84 insertions(+), 57 deletions(-) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 6eb7cbea6269d..3a30b254d80a2 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -17,7 +17,9 @@ use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; -use crate::path::{Path, PathBuf}; +#[cfg(doc)] +use crate::path::Path; +use crate::path::{PathBuf, PathLike}; use crate::sys; use crate::sys::os as os_imp; @@ -80,8 +82,8 @@ pub fn current_dir() -> io::Result { /// ``` #[doc(alias = "chdir")] #[stable(feature = "env", since = "1.0.0")] -pub fn set_current_dir>(path: P) -> io::Result<()> { - os_imp::chdir(path.as_ref()) +pub fn set_current_dir(path: P) -> io::Result<()> { + path.with_path(os_imp::chdir) } /// An iterator over a snapshot of the environment variables of this process. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index c6c78dc3939e7..34f91513ef2c1 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -14,7 +14,7 @@ mod tests; use crate::ffi::OsString; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; -use crate::path::{Path, PathBuf}; +use crate::path::{Path, PathBuf, PathLike}; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; @@ -246,14 +246,14 @@ pub struct DirBuilder { /// } /// ``` #[stable(feature = "fs_read_write_bytes", since = "1.26.0")] -pub fn read>(path: P) -> io::Result> { +pub fn read(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes)?; Ok(bytes) } - inner(path.as_ref()) + path.with_path(inner) } /// Read the entire contents of a file into a string. @@ -285,14 +285,14 @@ pub fn read>(path: P) -> io::Result> { /// } /// ``` #[stable(feature = "fs_read_write", since = "1.26.0")] -pub fn read_to_string>(path: P) -> io::Result { +pub fn read_to_string(path: P) -> io::Result { fn inner(path: &Path) -> io::Result { let mut file = File::open(path)?; let mut string = String::new(); file.read_to_string(&mut string)?; Ok(string) } - inner(path.as_ref()) + path.with_path(inner) } /// Write a slice as the entire contents of a file. @@ -320,11 +320,11 @@ pub fn read_to_string>(path: P) -> io::Result { /// } /// ``` #[stable(feature = "fs_read_write_bytes", since = "1.26.0")] -pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { +pub fn write>(path: P, contents: C) -> io::Result<()> { fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { File::create(path)?.write_all(contents) } - inner(path.as_ref(), contents.as_ref()) + path.with_path(|path| inner(path.as_ref(), contents.as_ref())) } impl File { @@ -348,8 +348,8 @@ impl File { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn open>(path: P) -> io::Result { - OpenOptions::new().read(true).open(path.as_ref()) + pub fn open(path: P) -> io::Result { + OpenOptions::new().read(true).open(path) } /// Opens a file in write-only mode. @@ -373,8 +373,8 @@ impl File { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn create>(path: P) -> io::Result { - OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) + pub fn create(path: P) -> io::Result { + OpenOptions::new().write(true).create(true).truncate(true).open(path) } /// Creates a new file in read-write mode; error if the file exists. @@ -402,8 +402,8 @@ impl File { /// } /// ``` #[unstable(feature = "file_create_new", issue = "none")] - pub fn create_new>(path: P) -> io::Result { - OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref()) + pub fn create_new(path: P) -> io::Result { + OpenOptions::new().read(true).write(true).create_new(true).open(path) } /// Returns a new OpenOptions object. @@ -1052,8 +1052,8 @@ impl OpenOptions { /// [`NotFound`]: io::ErrorKind::NotFound /// [`PermissionDenied`]: io::ErrorKind::PermissionDenied #[stable(feature = "rust1", since = "1.0.0")] - pub fn open>(&self, path: P) -> io::Result { - self._open(path.as_ref()) + pub fn open(&self, path: P) -> io::Result { + path.with_path(|path| self._open(path)) } fn _open(&self, path: &Path) -> io::Result { @@ -1723,8 +1723,8 @@ impl AsInner for DirEntry { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn remove_file>(path: P) -> io::Result<()> { - fs_imp::unlink(path.as_ref()) +pub fn remove_file(path: P) -> io::Result<()> { + path.with_path(fs_imp::unlink) } /// Given a path, query the file system to get information about a file, @@ -1761,8 +1761,8 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn metadata>(path: P) -> io::Result { - fs_imp::stat(path.as_ref()).map(Metadata) +pub fn metadata(path: P) -> io::Result { + path.with_path(fs_imp::stat).map(Metadata) } /// Query the metadata about a file without following symlinks. @@ -1795,8 +1795,8 @@ pub fn metadata>(path: P) -> io::Result { /// } /// ``` #[stable(feature = "symlink_metadata", since = "1.1.0")] -pub fn symlink_metadata>(path: P) -> io::Result { - fs_imp::lstat(path.as_ref()).map(Metadata) +pub fn symlink_metadata(path: P) -> io::Result { + path.with_path(fs_imp::lstat).map(Metadata) } /// Rename a file or directory to a new name, replacing the original file if @@ -1838,8 +1838,8 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { - fs_imp::rename(from.as_ref(), to.as_ref()) +pub fn rename(from: P, to: Q) -> io::Result<()> { + from.with_path(|from| to.with_path(|to| fs_imp::rename(from, to))) } /// Copies the contents of one file to another. This function will also @@ -1896,8 +1896,8 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { - fs_imp::copy(from.as_ref(), to.as_ref()) +pub fn copy(from: P, to: Q) -> io::Result { + from.with_path(|from| to.with_path(|to| fs_imp::copy(from, to))) } /// Creates a new hard link on the filesystem. @@ -1940,8 +1940,8 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn hard_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { - fs_imp::link(original.as_ref(), link.as_ref()) +pub fn hard_link(original: P, link: Q) -> io::Result<()> { + original.with_path(|original| link.with_path(|link| fs_imp::link(original, link))) } /// Creates a new symbolic link on the filesystem. @@ -1972,8 +1972,8 @@ pub fn hard_link, Q: AsRef>(original: P, link: Q) -> io::Re note = "replaced with std::os::unix::fs::symlink and \ std::os::windows::fs::{symlink_file, symlink_dir}" )] -pub fn soft_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { - fs_imp::symlink(original.as_ref(), link.as_ref()) +pub fn soft_link(original: P, link: Q) -> io::Result<()> { + original.with_path(|original| link.with_path(|link| fs_imp::symlink(original, link))) } /// Reads a symbolic link, returning the file that the link points to. @@ -2006,8 +2006,8 @@ pub fn soft_link, Q: AsRef>(original: P, link: Q) -> io::Re /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn read_link>(path: P) -> io::Result { - fs_imp::readlink(path.as_ref()) +pub fn read_link(path: P) -> io::Result { + path.with_path(fs_imp::readlink) } /// Returns the canonical, absolute form of a path with all intermediate @@ -2049,8 +2049,8 @@ pub fn read_link>(path: P) -> io::Result { #[doc(alias = "realpath")] #[doc(alias = "GetFinalPathNameByHandle")] #[stable(feature = "fs_canonicalize", since = "1.5.0")] -pub fn canonicalize>(path: P) -> io::Result { - fs_imp::canonicalize(path.as_ref()) +pub fn canonicalize(path: P) -> io::Result { + path.with_path(fs_imp::canonicalize) } /// Creates a new, empty directory at the provided path @@ -2090,8 +2090,8 @@ pub fn canonicalize>(path: P) -> io::Result { /// ``` #[doc(alias = "mkdir")] #[stable(feature = "rust1", since = "1.0.0")] -pub fn create_dir>(path: P) -> io::Result<()> { - DirBuilder::new().create(path.as_ref()) +pub fn create_dir(path: P) -> io::Result<()> { + DirBuilder::new().create(path) } /// Recursively create a directory and all of its parent components if they @@ -2134,8 +2134,8 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn create_dir_all>(path: P) -> io::Result<()> { - DirBuilder::new().recursive(true).create(path.as_ref()) +pub fn create_dir_all(path: P) -> io::Result<()> { + DirBuilder::new().recursive(true).create(path) } /// Removes an empty directory. @@ -2170,8 +2170,8 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// ``` #[doc(alias = "rmdir")] #[stable(feature = "rust1", since = "1.0.0")] -pub fn remove_dir>(path: P) -> io::Result<()> { - fs_imp::rmdir(path.as_ref()) +pub fn remove_dir(path: P) -> io::Result<()> { + path.with_path(fs_imp::rmdir) } /// Removes a directory at this path, after removing all its contents. Use @@ -2212,8 +2212,8 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn remove_dir_all>(path: P) -> io::Result<()> { - fs_imp::remove_dir_all(path.as_ref()) +pub fn remove_dir_all(path: P) -> io::Result<()> { + path.with_path(fs_imp::remove_dir_all) } /// Returns an iterator over the entries within a directory. @@ -2287,8 +2287,8 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn read_dir>(path: P) -> io::Result { - fs_imp::readdir(path.as_ref()).map(ReadDir) +pub fn read_dir(path: P) -> io::Result { + path.with_path(fs_imp::readdir).map(ReadDir) } /// Changes the permissions found on a file or a directory. @@ -2322,8 +2322,8 @@ pub fn read_dir>(path: P) -> io::Result { /// } /// ``` #[stable(feature = "set_permissions", since = "1.1.0")] -pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { - fs_imp::set_perm(path.as_ref(), perm.0) +pub fn set_permissions(path: P, perm: Permissions) -> io::Result<()> { + path.with_path(|path| fs_imp::set_perm(path, perm.0)) } impl DirBuilder { @@ -2382,8 +2382,8 @@ impl DirBuilder { /// assert!(fs::metadata(path).unwrap().is_dir()); /// ``` #[stable(feature = "dir_builder", since = "1.6.0")] - pub fn create>(&self, path: P) -> io::Result<()> { - self._create(path.as_ref()) + pub fn create(&self, path: P) -> io::Result<()> { + path.with_path(|path| self._create(path)) } fn _create(&self, path: &Path) -> io::Result<()> { @@ -2452,6 +2452,6 @@ impl AsInnerMut for DirBuilder { // instead. #[unstable(feature = "fs_try_exists", issue = "83186")] #[inline] -pub fn try_exists>(path: P) -> io::Result { - fs_imp::try_exists(path.as_ref()) +pub fn try_exists(path: P) -> io::Result { + path.with_path(fs_imp::try_exists) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 9d63281627d66..fb2ae7e035b27 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1109,6 +1109,30 @@ impl FusedIterator for Ancestors<'_> {} // Basic types and traits //////////////////////////////////////////////////////////////////////////////// +/// # Stable use +/// +/// Any function that requires a path may take a `PathLike` type. +/// +/// These types include [`OsStr`], [`Path`] and [`str`] as well as their owned +/// counterparts [`OsString`], [`PathBuf`] and [`String`]. +/// +/// # Unstable use +/// +/// The `PathLike` trait can be implemented for custom path types. This requires +/// a nightly compiler with the `path_like` feature enabled. Note that this +/// trait is unstable and highly likely to change between nightly versions. +#[unstable(feature = "path_like", issue = "none")] +pub trait PathLike { + /// Convert to a `Path` reference. + fn with_path T>(&self, f: F) -> T; +} +#[unstable(feature = "path_like", issue = "none")] +impl> PathLike for P { + fn with_path T>(&self, f: F) -> T { + f(self.as_ref()) + } +} + /// An owned, mutable path (akin to [`String`]). /// /// This type provides methods like [`push`] and [`set_extension`] that mutate diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 400d25beb26f3..224bd99e7c3e6 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -112,7 +112,7 @@ use crate::fmt; use crate::fs; use crate::io::{self, IoSlice, IoSliceMut}; use crate::num::NonZeroI32; -use crate::path::Path; +use crate::path::{Path, PathLike}; use crate::str; use crate::sys::pipe::{read2, AnonPipe}; use crate::sys::process as imp; @@ -769,8 +769,8 @@ impl Command { /// /// [`canonicalize`]: crate::fs::canonicalize #[stable(feature = "process", since = "1.0.0")] - pub fn current_dir>(&mut self, dir: P) -> &mut Command { - self.inner.cwd(dir.as_ref().as_ref()); + pub fn current_dir(&mut self, dir: P) -> &mut Command { + dir.with_path(|dir| self.inner.cwd(dir.as_ref())); self } diff --git a/src/test/ui/async-await/issue-72442.stderr b/src/test/ui/async-await/issue-72442.stderr index 919abf646037d..f1458d5026133 100644 --- a/src/test/ui/async-await/issue-72442.stderr +++ b/src/test/ui/async-await/issue-72442.stderr @@ -6,11 +6,12 @@ LL | let mut f = File::open(path.to_str())?; | | | required by a bound introduced by this call | + = note: required for `Option<&str>` to implement `PathLike` note: required by a bound in `File::open` --> $SRC_DIR/std/src/fs.rs:LL:COL | -LL | pub fn open>(path: P) -> io::Result { - | ^^^^^^^^^^^ required by this bound in `File::open` +LL | pub fn open(path: P) -> io::Result { + | ^^^^^^^^ required by this bound in `File::open` error: aborting due to previous error From d7cf592800caf91b58fff9fd701b884e54e16a17 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 19 Oct 2022 13:13:12 +0100 Subject: [PATCH 2/3] with_native_path --- library/std/src/path.rs | 5 +++++ library/std/src/sys/unix/path.rs | 8 +++++++- library/std/src/sys/windows/path.rs | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index fb2ae7e035b27..b51d92bbff65f 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1125,12 +1125,17 @@ impl FusedIterator for Ancestors<'_> {} pub trait PathLike { /// Convert to a `Path` reference. fn with_path T>(&self, f: F) -> T; + /// Convert to the native platform representation of a path. + fn with_native_path io::Result>(&self, f: F) -> io::Result; } #[unstable(feature = "path_like", issue = "none")] impl> PathLike for P { fn with_path T>(&self, f: F) -> T { f(self.as_ref()) } + fn with_native_path io::Result>(&self, f: F) -> io::Result { + crate::sys::path::with_native_path(self.as_ref(), f) + } } /// An owned, mutable path (akin to [`String`]). diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index a98a69e2db8e1..0c391ac39006c 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -1,7 +1,13 @@ use crate::env; -use crate::ffi::OsStr; +use crate::ffi::{CStr, OsStr}; use crate::io; use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::common::small_c_string::run_path_with_cstr; + +pub type NativePath = CStr; +pub fn with_native_path io::Result>(path: &Path, f: F) -> io::Result { + run_path_with_cstr(path, f) +} #[inline] pub fn is_sep_byte(b: u8) -> bool { diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index beeca1917a9af..3402391075c44 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -11,6 +11,12 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; +// Currently a no-op. Should convert to a wide string. +pub type NativePath = Path; +pub fn with_native_path T>(path: &Path, f: F) -> T { + f(path) +} + /// # Safety /// /// `bytes` must be a valid wtf8 encoded slice From 6af097ae81cbc0a2b300b64ad96c503edd3ce9f7 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 19 Oct 2022 14:05:02 +0100 Subject: [PATCH 3/3] fmt --- library/std/src/path.rs | 10 ++++++++-- library/std/src/sys/unix/path.rs | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index b51d92bbff65f..9e10af2718ab2 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1126,14 +1126,20 @@ pub trait PathLike { /// Convert to a `Path` reference. fn with_path T>(&self, f: F) -> T; /// Convert to the native platform representation of a path. - fn with_native_path io::Result>(&self, f: F) -> io::Result; + fn with_native_path io::Result>( + &self, + f: F, + ) -> io::Result; } #[unstable(feature = "path_like", issue = "none")] impl> PathLike for P { fn with_path T>(&self, f: F) -> T { f(self.as_ref()) } - fn with_native_path io::Result>(&self, f: F) -> io::Result { + fn with_native_path io::Result>( + &self, + f: F, + ) -> io::Result { crate::sys::path::with_native_path(self.as_ref(), f) } } diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index 0c391ac39006c..2e5c789cc1ba2 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -5,7 +5,10 @@ use crate::path::{Path, PathBuf, Prefix}; use crate::sys::common::small_c_string::run_path_with_cstr; pub type NativePath = CStr; -pub fn with_native_path io::Result>(path: &Path, f: F) -> io::Result { +pub fn with_native_path io::Result>( + path: &Path, + f: F, +) -> io::Result { run_path_with_cstr(path, f) }