Skip to content

Commit 59c426c

Browse files
committed
Add NativePath
And replace `AsRef<Path>` with the `AsPath` trait.
1 parent 9823f17 commit 59c426c

File tree

2 files changed

+112
-59
lines changed

2 files changed

+112
-59
lines changed

library/std/src/fs.rs

Lines changed: 60 additions & 58 deletions
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

library/std/src/path.rs

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

8787
use crate::ffi::{os_str, 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)