diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 5992766b5a42d..a4f6ce406cb05 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -1,3 +1,5 @@ +use alloc_crate::borrow::Cow; + use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; @@ -53,6 +55,37 @@ impl ReadDir { } } +/// Specialization trait used to construct a `ReadDir` +trait ReadDirFromPath

{ + fn from_path(dir: Vec, path: P) -> Self; +} + +impl> ReadDirFromPath

for ReadDir { + default fn from_path(dir: Vec, path: P) -> Self { + let inner = InnerReadDir { root: path.as_ref().to_path_buf(), dir }; + ReadDir::new(inner) + } +} + +/// This constructs a `ReadDir` for all types that can be converted +/// into `PathBuf` without allocating +macro_rules! impl_read_dir_from_path { + ($t:ty) => { + impl ReadDirFromPath<$t> for ReadDir { + fn from_path(dir: Vec, path: $t) -> Self { + let inner = InnerReadDir::new(path.into(), dir); + ReadDir::new(inner) + } + } + }; +} + +impl_read_dir_from_path!(PathBuf); +impl_read_dir_from_path!(Box); +impl_read_dir_from_path!(Cow<'_, Path>); +impl_read_dir_from_path!(OsString); +impl_read_dir_from_path!(String); + pub struct DirEntry { /// path to the entry root: PathBuf, @@ -512,12 +545,11 @@ impl FromRawFd for File { } } -pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, &|path| { +pub fn readdir>(path: P) -> io::Result { + let fd_raw = run_path_with_cstr(path.as_ref(), &|path| { cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) })?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; - let root = path.to_path_buf(); // read all director entries let mut vec: Vec = Vec::new(); @@ -551,7 +583,7 @@ pub fn readdir(path: &Path) -> io::Result { } } - Ok(ReadDir::new(InnerReadDir::new(root, vec))) + Ok(>::from_path(vec, path)) } pub fn unlink(path: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0c297c5766b82..be99643219ab4 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -63,7 +63,7 @@ pub use imp::{ ReadDir, }; -pub fn read_dir(path: &Path) -> io::Result { +pub fn read_dir>(path: P) -> io::Result { // FIXME: use with_native_path on all platforms imp::readdir(path) } diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 2ae01db24be5c..d6ec5b7fcfe8f 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -1,3 +1,5 @@ +use alloc_crate::borrow::Cow; + use crate::ffi::OsString; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; @@ -377,12 +379,49 @@ impl Drop for ReadDir { } } -pub fn readdir(path: &Path) -> io::Result { - let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; - Ok(ReadDir { - rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?, - path: path.to_owned(), - }) +/// Specialization trait used to construct a `ReadDir` +trait ReadDirFromPath

{ + fn from_path(path: P) -> io::Result; +} + +impl> ReadDirFromPath

for ReadDir { + default fn from_path(path: P) -> io::Result { + let path = path.as_ref().to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + Ok(ReadDir { + rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?, + path: path.to_owned(), + }) + } +} + +/// This constructs a `ReadDir` for all types that can be converted +/// into `String` without allocating +macro_rules! impl_read_dir_from_path { + ($t:ty) => { + impl ReadDirFromPath<$t> for ReadDir { + fn from_path(path: $t) -> io::Result { + let path_buf: PathBuf = path.into(); + let path = path_buf.into_os_string().into_string(); + match path { + Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidFilename)), + Ok(path) => Ok(ReadDir { + rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?, + path, + }), + } + } + } + }; +} + +impl_read_dir_from_path!(PathBuf); +impl_read_dir_from_path!(Box); +impl_read_dir_from_path!(Cow<'_, Path>); +impl_read_dir_from_path!(OsString); +impl_read_dir_from_path!(String); + +pub fn readdir>(path: P) -> io::Result { + >::from_path(path) } impl Iterator for ReadDir { diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index f15a152146ee5..3b783e466f826 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use alloc_crate::borrow::Cow; + use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::fs::TryLockError; @@ -146,19 +148,49 @@ impl FileType { } } -pub fn readdir(p: &Path) -> io::Result { +pub fn readdir>(p: P) -> io::Result { unsafe { let mut dir = MaybeUninit::uninit(); error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir( - cstr(p)?.as_ptr(), + cstr(p.as_ref())?.as_ptr(), dir.as_mut_ptr(), )) .map_err(|e| e.as_io_error())?; - let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() }); - Ok(ReadDir { inner }) + Ok(>::from_path(dir.assume_init(), p)) + } +} + +/// Specialization trait used to construct a `ReadDir` +trait ReadDirFromPath

{ + fn from_path(dirp: abi::S_DIR, path: P) -> Self; +} + +impl> ReadDirFromPath

for ReadDir { + default fn from_path(dirp: abi::S_DIR, path: P) -> Self { + let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() }; + ReadDir { inner: Arc::new(inner) } } } +/// This constructs a `ReadDir` for all types that can be converted +/// into `PathBuf` without allocating +macro_rules! impl_read_dir_from_path { + ($t:ty) => { + impl ReadDirFromPath<$t> for ReadDir { + fn from_path(dirp: abi::S_DIR, path: $t) -> Self { + let inner = InnerReadDir { dirp, root: path.into() }; + ReadDir { inner: Arc::new(inner) } + } + } + }; +} + +impl_read_dir_from_path!(PathBuf); +impl_read_dir_from_path!(Box); +impl_read_dir_from_path!(Cow<'_, Path>); +impl_read_dir_from_path!(OsString); +impl_read_dir_from_path!(String); + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 8135519317a02..c4f3bd5f4f19d 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -415,9 +415,8 @@ impl fmt::Debug for File { } } -pub fn readdir(p: &Path) -> io::Result { - let path = crate::path::absolute(p)?; - let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?; +pub fn readdir>(p: P) -> io::Result { + let f = uefi_fs::File::from_path(p.as_ref(), file::MODE_READ, 0)?; let file_info = f.file_info()?; let file_attr = FileAttr::from_uefi(file_info); diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 9b0ef5539d32b..5ccd2325c7dab 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -6,6 +6,7 @@ #[cfg(test)] mod tests; +use alloc_crate::borrow::Cow; #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::c_char; #[cfg(any( @@ -272,6 +273,37 @@ impl ReadDir { } } +/// Specialization trait used to construct a `ReadDir` +trait ReadDirFromPath

{ + fn from_path(dirp: DirStream, path: P) -> Self; +} + +impl> ReadDirFromPath

for ReadDir { + default fn from_path(dirp: DirStream, path: P) -> Self { + let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() }; + ReadDir::new(inner) + } +} + +/// This constructs a `ReadDir` for all types that can be converted +/// into `PathBuf` without allocating +macro_rules! impl_read_dir_from_path { + ($t:ty) => { + impl ReadDirFromPath<$t> for ReadDir { + fn from_path(dirp: DirStream, path: $t) -> Self { + let inner = InnerReadDir { dirp, root: path.into() }; + ReadDir::new(inner) + } + } + }; +} + +impl_read_dir_from_path!(PathBuf); +impl_read_dir_from_path!(Box); +impl_read_dir_from_path!(Cow<'_, Path>); +impl_read_dir_from_path!(OsString); +impl_read_dir_from_path!(String); + struct DirStream(*mut libc::DIR); // dir::Dir requires openat support @@ -2077,14 +2109,12 @@ impl fmt::Debug for Mode { } } -pub fn readdir(path: &Path) -> io::Result { - let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; +pub fn readdir>(path: P) -> io::Result { + let ptr = run_path_with_cstr(path.as_ref(), &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; if ptr.is_null() { Err(Error::last_os_error()) } else { - let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: DirStream(ptr), root }; - Ok(ReadDir::new(inner)) + Ok(>::from_path(DirStream(ptr), path)) } } diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 069b4fb8a29ce..b25446b007e9f 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -297,7 +297,7 @@ impl fmt::Debug for File { } } -pub fn readdir(_p: &Path) -> io::Result { +pub fn readdir>(_p: P) -> io::Result { unsupported() } diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 5f75bcd92421b..73d9f88de6ba1 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -477,7 +477,7 @@ impl Drop for File { } } -pub fn readdir(_p: &Path) -> io::Result { +pub fn readdir>(_p: P) -> io::Result { // While there *is* a userspace function for reading file directories, // the necessary implementation cannot currently be done cleanly, as // VEXos does not expose directory length to user programs. diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 74854cdeb498d..112e4c84e74c5 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -53,6 +53,47 @@ pub struct ReadDir { first: Option, } +/// Specialization trait used to construct a `ReadDir` +trait ReadDirFromPath

{ + fn from_path( + handle: Option, + path: P, + first: Option, + ) -> Self; +} + +impl> ReadDirFromPath

for ReadDir { + default fn from_path( + handle: Option, + path: P, + first: Option, + ) -> Self { + ReadDir { handle, root: Arc::new(path.as_ref().to_path_buf()), first } + } +} + +/// This constructs a `ReadDir` for all types that can be converted +/// into `PathBuf` without allocating +macro_rules! impl_read_dir_from_path { + ($t:ty) => { + impl ReadDirFromPath<$t> for ReadDir { + fn from_path( + handle: Option, + path: $t, + first: Option, + ) -> Self { + ReadDir { handle, root: Arc::new(path.into()), first } + } + } + }; +} + +impl_read_dir_from_path!(PathBuf); +impl_read_dir_from_path!(Box); +impl_read_dir_from_path!(Cow<'_, Path>); +impl_read_dir_from_path!(OsString); +impl_read_dir_from_path!(String); + struct FindNextFileHandle(c::HANDLE); unsafe impl Send for FindNextFileHandle {} @@ -1222,17 +1263,16 @@ impl DirBuilder { } } -pub fn readdir(p: &Path) -> io::Result { +pub fn readdir>(p: P) -> io::Result { // We push a `*` to the end of the path which cause the empty path to be // treated as the current directory. So, for consistency with other platforms, // we explicitly error on the empty path. - if p.as_os_str().is_empty() { + if p.as_ref().as_os_str().is_empty() { // Return an error code consistent with other ways of opening files. // E.g. fs::metadata or File::open. return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); } - let root = p.to_path_buf(); - let star = p.join("*"); + let star = p.as_ref().join("*"); let path = maybe_verbatim(&star)?; unsafe { @@ -1254,11 +1294,11 @@ pub fn readdir(p: &Path) -> io::Result { ); if find_handle != c::INVALID_HANDLE_VALUE { - Ok(ReadDir { - handle: Some(FindNextFileHandle(find_handle)), - root: Arc::new(root), - first: Some(wfd), - }) + Ok(>::from_path( + Some(FindNextFileHandle(find_handle)), + p, + Some(wfd), + )) } else { // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function // if no matching files can be found, but not necessarily that the path to find the @@ -1273,7 +1313,7 @@ pub fn readdir(p: &Path) -> io::Result { // See issue #120040: https://github.com/rust-lang/rust/issues/120040. let last_error = api::get_last_error(); if last_error == WinError::FILE_NOT_FOUND { - return Ok(ReadDir { handle: None, root: Arc::new(root), first: None }); + return Ok(>::from_path(None, p, None)); } // Just return the error constructed from the raw OS error if the above is not the case.