Skip to content

Commit 5cf0aa6

Browse files
committed
Add NativePath
And replace `AsRef<Path>` with the `AsPath` trait.
1 parent ef32456 commit 5cf0aa6

File tree

2 files changed

+112
-59
lines changed

2 files changed

+112
-59
lines changed

library/std/src/fs.rs

+60-58
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ mod tests;
1414
use crate::ffi::OsString;
1515
use crate::fmt;
1616
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
17-
use crate::path::{Path, PathBuf};
17+
use crate::path::{AsPath, Path, PathBuf};
1818
use crate::sealed::Sealed;
1919
use crate::sync::Arc;
20-
use crate::sys::fs as fs_imp;
20+
use crate::sys;
21+
use crate::sys::fs::fs_imp;
2122
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
2223
use crate::time::SystemTime;
2324

@@ -256,16 +257,16 @@ pub struct DirBuilder {
256257
/// }
257258
/// ```
258259
#[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
259-
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
260-
fn inner(path: &Path) -> io::Result<Vec<u8>> {
261-
let mut file = File::open(path)?;
260+
pub fn read<P: AsPath>(path: P) -> io::Result<Vec<u8>> {
261+
fn inner(mut file: File) -> io::Result<Vec<u8>> {
262262
let size = file.metadata().map(|m| m.len() as usize).ok();
263263
let mut bytes = Vec::new();
264264
bytes.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
265265
io::default_read_to_end(&mut file, &mut bytes, size)?;
266266
Ok(bytes)
267267
}
268-
inner(path.as_ref())
268+
let file = File::open(path)?;
269+
inner(file)
269270
}
270271

271272
/// Read the entire contents of a file into a string.
@@ -299,16 +300,16 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
299300
/// }
300301
/// ```
301302
#[stable(feature = "fs_read_write", since = "1.26.0")]
302-
pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
303-
fn inner(path: &Path) -> io::Result<String> {
304-
let mut file = File::open(path)?;
303+
pub fn read_to_string<P: AsPath>(path: P) -> io::Result<String> {
304+
fn inner(mut file: File) -> io::Result<String> {
305305
let size = file.metadata().map(|m| m.len() as usize).ok();
306306
let mut string = String::new();
307307
string.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
308308
io::default_read_to_string(&mut file, &mut string, size)?;
309309
Ok(string)
310310
}
311-
inner(path.as_ref())
311+
let file = File::open(path)?;
312+
inner(file)
312313
}
313314

314315
/// Write a slice as the entire contents of a file.
@@ -336,11 +337,12 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
336337
/// }
337338
/// ```
338339
#[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
339-
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
340-
fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
341-
File::create(path)?.write_all(contents)
340+
pub fn write<P: AsPath, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
341+
fn inner(mut file: File, contents: &[u8]) -> io::Result<()> {
342+
file.write_all(contents)
342343
}
343-
inner(path.as_ref(), contents.as_ref())
344+
let file = File::create(path)?;
345+
inner(file, contents.as_ref())
344346
}
345347

346348
impl File {
@@ -371,8 +373,8 @@ impl File {
371373
/// }
372374
/// ```
373375
#[stable(feature = "rust1", since = "1.0.0")]
374-
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
375-
OpenOptions::new().read(true).open(path.as_ref())
376+
pub fn open<P: AsPath>(path: P) -> io::Result<File> {
377+
OpenOptions::new().read(true).open(path)
376378
}
377379

378380
/// Opens a file in write-only mode.
@@ -400,8 +402,8 @@ impl File {
400402
/// }
401403
/// ```
402404
#[stable(feature = "rust1", since = "1.0.0")]
403-
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
404-
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
405+
pub fn create<P: AsPath>(path: P) -> io::Result<File> {
406+
OpenOptions::new().write(true).create(true).truncate(true).open(path)
405407
}
406408

407409
/// Creates a new file in read-write mode; error if the file exists.
@@ -429,8 +431,8 @@ impl File {
429431
/// }
430432
/// ```
431433
#[stable(feature = "file_create_new", since = "1.77.0")]
432-
pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
433-
OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
434+
pub fn create_new<P: AsPath>(path: P) -> io::Result<File> {
435+
OpenOptions::new().read(true).write(true).create_new(true).open(path)
434436
}
435437

436438
/// Returns a new OpenOptions object.
@@ -1127,12 +1129,12 @@ impl OpenOptions {
11271129
/// [`NotFound`]: io::ErrorKind::NotFound
11281130
/// [`PermissionDenied`]: io::ErrorKind::PermissionDenied
11291131
#[stable(feature = "rust1", since = "1.0.0")]
1130-
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
1131-
self._open(path.as_ref())
1132+
pub fn open<P: AsPath>(&self, path: P) -> io::Result<File> {
1133+
path.with_native_path(|path| self._open(path))
11321134
}
11331135

1134-
fn _open(&self, path: &Path) -> io::Result<File> {
1135-
fs_imp::File::open(path, &self.0).map(|inner| File { inner })
1136+
fn _open(&self, path: &sys::path::NativePath) -> io::Result<File> {
1137+
fs_imp::File::open_native(path, &self.0).map(|inner| File { inner })
11361138
}
11371139
}
11381140

@@ -1885,8 +1887,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
18851887
/// ```
18861888
#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")]
18871889
#[stable(feature = "rust1", since = "1.0.0")]
1888-
pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
1889-
fs_imp::unlink(path.as_ref())
1890+
pub fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
1891+
fs_imp::remove_file(path)
18901892
}
18911893

18921894
/// Given a path, query the file system to get information about a file,
@@ -1924,8 +1926,8 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
19241926
/// ```
19251927
#[doc(alias = "stat")]
19261928
#[stable(feature = "rust1", since = "1.0.0")]
1927-
pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
1928-
fs_imp::stat(path.as_ref()).map(Metadata)
1929+
pub fn metadata<P: AsPath>(path: P) -> io::Result<Metadata> {
1930+
fs_imp::metadata(path).map(Metadata)
19291931
}
19301932

19311933
/// Query the metadata about a file without following symlinks.
@@ -1959,8 +1961,8 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
19591961
/// ```
19601962
#[doc(alias = "lstat")]
19611963
#[stable(feature = "symlink_metadata", since = "1.1.0")]
1962-
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
1963-
fs_imp::lstat(path.as_ref()).map(Metadata)
1964+
pub fn symlink_metadata<P: AsPath>(path: P) -> io::Result<Metadata> {
1965+
fs_imp::symlink_metadata(path).map(Metadata)
19641966
}
19651967

19661968
/// Rename a file or directory to a new name, replacing the original file if
@@ -2003,8 +2005,8 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
20032005
/// ```
20042006
#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")]
20052007
#[stable(feature = "rust1", since = "1.0.0")]
2006-
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
2007-
fs_imp::rename(from.as_ref(), to.as_ref())
2008+
pub fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
2009+
fs_imp::rename(from, to)
20082010
}
20092011

20102012
/// Copies the contents of one file to another. This function will also
@@ -2064,8 +2066,8 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
20642066
#[doc(alias = "CopyFile", alias = "CopyFileEx")]
20652067
#[doc(alias = "fclonefileat", alias = "fcopyfile")]
20662068
#[stable(feature = "rust1", since = "1.0.0")]
2067-
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
2068-
fs_imp::copy(from.as_ref(), to.as_ref())
2069+
pub fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
2070+
fs_imp::copy(from, to)
20692071
}
20702072

20712073
/// Creates a new hard link on the filesystem.
@@ -2109,8 +2111,8 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
21092111
/// ```
21102112
#[doc(alias = "CreateHardLink", alias = "linkat")]
21112113
#[stable(feature = "rust1", since = "1.0.0")]
2112-
pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
2113-
fs_imp::link(original.as_ref(), link.as_ref())
2114+
pub fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
2115+
fs_imp::hard_link(original, link)
21142116
}
21152117

21162118
/// Creates a new symbolic link on the filesystem.
@@ -2141,8 +2143,8 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
21412143
note = "replaced with std::os::unix::fs::symlink and \
21422144
std::os::windows::fs::{symlink_file, symlink_dir}"
21432145
)]
2144-
pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
2145-
fs_imp::symlink(original.as_ref(), link.as_ref())
2146+
pub fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
2147+
fs_imp::soft_link(original, link)
21462148
}
21472149

21482150
/// Reads a symbolic link, returning the file that the link points to.
@@ -2175,8 +2177,8 @@ pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
21752177
/// }
21762178
/// ```
21772179
#[stable(feature = "rust1", since = "1.0.0")]
2178-
pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
2179-
fs_imp::readlink(path.as_ref())
2180+
pub fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
2181+
fs_imp::read_link(path)
21802182
}
21812183

21822184
/// Returns the canonical, absolute form of a path with all intermediate
@@ -2218,8 +2220,8 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
22182220
#[doc(alias = "realpath")]
22192221
#[doc(alias = "GetFinalPathNameByHandle")]
22202222
#[stable(feature = "fs_canonicalize", since = "1.5.0")]
2221-
pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
2222-
fs_imp::canonicalize(path.as_ref())
2223+
pub fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
2224+
fs_imp::canonicalize(path)
22232225
}
22242226

22252227
/// Creates a new, empty directory at the provided path
@@ -2260,8 +2262,8 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
22602262
#[doc(alias = "mkdir", alias = "CreateDirectory")]
22612263
#[stable(feature = "rust1", since = "1.0.0")]
22622264
#[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")]
2263-
pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
2264-
DirBuilder::new().create(path.as_ref())
2265+
pub fn create_dir<P: AsPath>(path: P) -> io::Result<()> {
2266+
DirBuilder::new().create(path)
22652267
}
22662268

22672269
/// Recursively create a directory and all of its parent components if they
@@ -2304,8 +2306,8 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
23042306
/// }
23052307
/// ```
23062308
#[stable(feature = "rust1", since = "1.0.0")]
2307-
pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
2308-
DirBuilder::new().recursive(true).create(path.as_ref())
2309+
pub fn create_dir_all<P: AsPath>(path: P) -> io::Result<()> {
2310+
DirBuilder::new().recursive(true).create(path)
23092311
}
23102312

23112313
/// Removes an empty directory.
@@ -2340,8 +2342,8 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
23402342
/// ```
23412343
#[doc(alias = "rmdir", alias = "RemoveDirectory")]
23422344
#[stable(feature = "rust1", since = "1.0.0")]
2343-
pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
2344-
fs_imp::rmdir(path.as_ref())
2345+
pub fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
2346+
fs_imp::remove_dir(path)
23452347
}
23462348

23472349
/// Removes a directory at this path, after removing all its contents. Use
@@ -2387,8 +2389,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
23872389
/// }
23882390
/// ```
23892391
#[stable(feature = "rust1", since = "1.0.0")]
2390-
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
2391-
fs_imp::remove_dir_all(path.as_ref())
2392+
pub fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
2393+
fs_imp::remove_dir_all(path)
23922394
}
23932395

23942396
/// Returns an iterator over the entries within a directory.
@@ -2463,8 +2465,8 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
24632465
/// ```
24642466
#[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")]
24652467
#[stable(feature = "rust1", since = "1.0.0")]
2466-
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
2467-
fs_imp::readdir(path.as_ref()).map(ReadDir)
2468+
pub fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
2469+
fs_imp::read_dir(path).map(ReadDir)
24682470
}
24692471

24702472
/// Changes the permissions found on a file or a directory.
@@ -2499,8 +2501,8 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
24992501
/// ```
25002502
#[doc(alias = "chmod", alias = "SetFileAttributes")]
25012503
#[stable(feature = "set_permissions", since = "1.1.0")]
2502-
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
2503-
fs_imp::set_perm(path.as_ref(), perm.0)
2504+
pub fn set_permissions<P: AsPath>(path: P, perm: Permissions) -> io::Result<()> {
2505+
fs_imp::set_permissions(path, perm.0)
25042506
}
25052507

25062508
impl DirBuilder {
@@ -2559,8 +2561,8 @@ impl DirBuilder {
25592561
/// assert!(fs::metadata(path).unwrap().is_dir());
25602562
/// ```
25612563
#[stable(feature = "dir_builder", since = "1.6.0")]
2562-
pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
2563-
self._create(path.as_ref())
2564+
pub fn create<P: AsPath>(&self, path: P) -> io::Result<()> {
2565+
path.with_path(|path| self._create(path))
25642566
}
25652567

25662568
fn _create(&self, path: &Path) -> io::Result<()> {
@@ -2631,6 +2633,6 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
26312633
// instead.
26322634
#[unstable(feature = "fs_try_exists", issue = "83186")]
26332635
#[inline]
2634-
pub fn try_exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {
2635-
fs_imp::try_exists(path.as_ref())
2636+
pub fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
2637+
fs_imp::try_exists(path)
26362638
}

library/std/src/path.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ use crate::sync::Arc;
8686

8787
use crate::ffi::{OsStr, OsString};
8888
use crate::sys;
89-
use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR};
89+
use crate::sys::path::{
90+
is_sep_byte, is_verbatim_sep, parse_prefix, NativePath as NativePathImpl, MAIN_SEP_STR,
91+
};
9092

9193
////////////////////////////////////////////////////////////////////////////////
9294
// GENERAL NOTES
@@ -1108,6 +1110,55 @@ impl FusedIterator for Ancestors<'_> {}
11081110
// Basic types and traits
11091111
////////////////////////////////////////////////////////////////////////////////
11101112

1113+
/// Represents a path in a form native to the OS.
1114+
#[derive(Debug)]
1115+
#[repr(transparent)]
1116+
#[unstable(feature = "fs_native_path", issue = "108979")]
1117+
pub struct NativePath(pub(crate) NativePathImpl);
1118+
1119+
#[unstable(feature = "sealed", issue = "none")]
1120+
impl crate::sealed::Sealed for NativePath {}
1121+
#[unstable(feature = "sealed", issue = "none")]
1122+
impl crate::sealed::Sealed for &NativePath {}
1123+
1124+
/// # Stable use
1125+
///
1126+
/// Functions that need a filesystem path will often accept any type that implements [`AsRef<Path>`].
1127+
///
1128+
/// These types include [`Path`], [`OsStr`] and [`str`] as well as their owned
1129+
/// counterparts [`PathBuf`], [`OsString`] and [`String`].
1130+
///
1131+
/// You can also implement you own [`AsRef<Path>`] for your own types and they'll
1132+
/// automatically work for filesystem functions.
1133+
///
1134+
/// ## Example
1135+
///
1136+
/// ```no_run
1137+
/// use std::ffi::OsStr;
1138+
/// use std::path::Path;
1139+
/// use std::fs;
1140+
///
1141+
/// // These are all equivalent.
1142+
/// let metadata = fs::metadata("path/to/file")?;
1143+
/// let metadata = fs::metadata(Path::new("path/to/file"))?;
1144+
/// let metadata = fs::metadata(OsStr::new("path/to/file"))?;
1145+
/// # Ok::<(), std::io::Error>(())
1146+
/// ```
1147+
///
1148+
/// # Unstable use
1149+
///
1150+
/// The `AsPath` trait can also be used with [`NativePath`] to pass platform
1151+
/// native paths more directly to system APIs.
1152+
#[unstable(feature = "fs_native_path", issue = "108979")]
1153+
pub trait AsPath {
1154+
#[doc(hidden)]
1155+
#[unstable(feature = "fs_native_path_internals", issue = "none")]
1156+
fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T>;
1157+
#[doc(hidden)]
1158+
#[unstable(feature = "fs_native_path_internals", issue = "none")]
1159+
fn with_native_path<T, F: Fn(&NativePathImpl) -> io::Result<T>>(self, f: F) -> io::Result<T>;
1160+
}
1161+
11111162
/// An owned, mutable path (akin to [`String`]).
11121163
///
11131164
/// This type provides methods like [`push`] and [`set_extension`] that mutate

0 commit comments

Comments
 (0)