mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-23 13:13:17 +00:00
std::path::absolute
This commit is contained in:
parent
775e480722
commit
d59d32c4f1
@ -222,6 +222,7 @@
|
||||
// std is implemented with unstable features, many of which are internal
|
||||
// compiler details that will never be stable
|
||||
// NB: the following list is sorted to minimize merge conflicts.
|
||||
#![feature(absolute_path)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(allocator_api)]
|
||||
|
@ -85,7 +85,7 @@ use crate::str::FromStr;
|
||||
use crate::sync::Arc;
|
||||
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
|
||||
use crate::sys;
|
||||
use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -3164,3 +3164,79 @@ impl Error for StripPrefixError {
|
||||
"prefix not found"
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes the path absolute without accessing the filesystem.
|
||||
///
|
||||
/// If the path is relative, the current directory is used as the base directory.
|
||||
/// All intermediate components will be resolved according to platforms-specific
|
||||
/// rules but unlike [`canonicalize`][crate::fs::canonicalize] this does not
|
||||
/// resolve symlinks and may succeed even if the path does not exist.
|
||||
///
|
||||
/// If the `path` is empty or getting the
|
||||
/// [current directory][crate::env::current_dir] fails then an error will be
|
||||
/// returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Posix paths
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(absolute_path)]
|
||||
/// # #[cfg(unix)]
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// use std::path::{self, Path};
|
||||
///
|
||||
/// // Relative to absolute
|
||||
/// let absolute = path::absolute("foo/./bar")?;
|
||||
/// assert!(absolute.ends_with("foo/bar"));
|
||||
///
|
||||
/// // Absolute to absolute
|
||||
/// let absolute = path::absolute("/foo//test/.././bar.rs")?;
|
||||
/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs"));
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// # #[cfg(not(unix))]
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// The paths is resolved using [POSIX semantics][posix-semantics] except that
|
||||
/// it stops short of resolving symlinks. This means it will keep `..`
|
||||
/// components and trailing slashes.
|
||||
///
|
||||
/// ## Windows paths
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(absolute_path)]
|
||||
/// # #[cfg(windows)]
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// use std::path::{self, Path};
|
||||
///
|
||||
/// // Relative to absolute
|
||||
/// let absolute = path::absolute("foo/./bar")?;
|
||||
/// assert!(absolute.ends_with(r"foo\bar"));
|
||||
///
|
||||
/// // Absolute to absolute
|
||||
/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?;
|
||||
///
|
||||
/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs"));
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// # #[cfg(not(windows))]
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// For verbatim paths this will simply return the path as given. For other
|
||||
/// paths this is currently equivalent to calling [`GetFullPathNameW`][windows-path]
|
||||
/// This may change in the future.
|
||||
///
|
||||
/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
|
||||
/// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
|
||||
#[unstable(feature = "absolute_path", issue = "none")]
|
||||
pub fn absolute<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
||||
let path = path.as_ref();
|
||||
if path.as_os_str().is_empty() {
|
||||
Err(io::const_io_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",))
|
||||
} else {
|
||||
sys::path::absolute(path)
|
||||
}
|
||||
}
|
||||
|
@ -1665,6 +1665,64 @@ fn test_ord() {
|
||||
ord!(Equal, "foo/bar", "foo/bar//");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_unix_absolute() {
|
||||
use crate::path::absolute;
|
||||
|
||||
assert!(absolute("").is_err());
|
||||
|
||||
let relative = "a/b";
|
||||
let mut expected = crate::env::current_dir().unwrap();
|
||||
expected.push(relative);
|
||||
assert_eq!(absolute(relative).unwrap(), expected);
|
||||
|
||||
// Test how components are collected.
|
||||
assert_eq!(absolute("/a/b/c").unwrap(), Path::new("/a/b/c"));
|
||||
assert_eq!(absolute("/a//b/c").unwrap(), Path::new("/a/b/c"));
|
||||
assert_eq!(absolute("//a/b/c").unwrap(), Path::new("//a/b/c"));
|
||||
assert_eq!(absolute("///a/b/c").unwrap(), Path::new("/a/b/c"));
|
||||
assert_eq!(absolute("/a/b/c/").unwrap(), Path::new("/a/b/c/"));
|
||||
assert_eq!(absolute("/a/./b/../c/.././..").unwrap(), Path::new("/a/b/../c/../.."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_windows_absolute() {
|
||||
use crate::path::absolute;
|
||||
// An empty path is an error.
|
||||
assert!(absolute("").is_err());
|
||||
|
||||
let relative = r"a\b";
|
||||
let mut expected = crate::env::current_dir().unwrap();
|
||||
expected.push(relative);
|
||||
assert_eq!(absolute(relative).unwrap(), expected);
|
||||
|
||||
macro_rules! unchanged(
|
||||
($path:expr) => {
|
||||
assert_eq!(absolute($path).unwrap(), Path::new($path));
|
||||
}
|
||||
);
|
||||
|
||||
unchanged!(r"C:\path\to\file");
|
||||
unchanged!(r"C:\path\to\file\");
|
||||
unchanged!(r"\\server\share\to\file");
|
||||
unchanged!(r"\\server.\share.\to\file");
|
||||
unchanged!(r"\\.\PIPE\name");
|
||||
unchanged!(r"\\.\C:\path\to\COM1");
|
||||
unchanged!(r"\\?\C:\path\to\file");
|
||||
unchanged!(r"\\?\UNC\server\share\to\file");
|
||||
unchanged!(r"\\?\PIPE\name");
|
||||
// Verbatim paths are always unchanged, no matter what.
|
||||
unchanged!(r"\\?\path.\to/file..");
|
||||
|
||||
assert_eq!(absolute(r"C:\path..\to.\file.").unwrap(), Path::new(r"C:\path..\to\file"));
|
||||
assert_eq!(absolute(r"C:\path\to\COM1").unwrap(), Path::new(r"\\.\COM1"));
|
||||
assert_eq!(absolute(r"C:\path\to\COM1.txt").unwrap(), Path::new(r"\\.\COM1"));
|
||||
assert_eq!(absolute(r"C:\path\to\COM1 .txt").unwrap(), Path::new(r"\\.\COM1"));
|
||||
assert_eq!(absolute(r"C:\path\to\cOnOuT$").unwrap(), Path::new(r"\\.\cOnOuT$"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
|
||||
let prefix = "my/home";
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::ffi::OsStr;
|
||||
use crate::path::Prefix;
|
||||
use crate::path::{Path, PathBuf, Prefix};
|
||||
use crate::sys::unsupported;
|
||||
|
||||
#[inline]
|
||||
pub fn is_sep_byte(b: u8) -> bool {
|
||||
@ -17,3 +18,7 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "/";
|
||||
pub const MAIN_SEP: char = '/';
|
||||
|
||||
pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
|
||||
unsupported()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::ffi::OsStr;
|
||||
use crate::path::Prefix;
|
||||
use crate::path::{Path, PathBuf, Prefix};
|
||||
use crate::sys::unsupported;
|
||||
|
||||
#[inline]
|
||||
pub fn is_sep_byte(b: u8) -> bool {
|
||||
@ -17,3 +18,7 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "\\";
|
||||
pub const MAIN_SEP: char = '\\';
|
||||
|
||||
pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
|
||||
unsupported()
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::env;
|
||||
use crate::ffi::OsStr;
|
||||
use crate::path::Prefix;
|
||||
use crate::io;
|
||||
use crate::os::unix::ffi::OsStrExt;
|
||||
use crate::path::{Path, PathBuf, Prefix};
|
||||
|
||||
#[inline]
|
||||
pub fn is_sep_byte(b: u8) -> bool {
|
||||
@ -18,3 +21,43 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "/";
|
||||
pub const MAIN_SEP: char = '/';
|
||||
|
||||
/// Make a POSIX path absolute without changing its semantics.
|
||||
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
|
||||
// This is mostly a wrapper around collecting `Path::components`, with
|
||||
// exceptions made where this conflicts with the POSIX specification.
|
||||
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
|
||||
|
||||
let mut components = path.components();
|
||||
let path_os = path.as_os_str().as_bytes();
|
||||
|
||||
let mut normalized = if path.is_absolute() {
|
||||
// "If a pathname begins with two successive <slash> characters, the
|
||||
// first component following the leading <slash> characters may be
|
||||
// interpreted in an implementation-defined manner, although more than
|
||||
// two leading <slash> characters shall be treated as a single <slash>
|
||||
// character."
|
||||
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
|
||||
components.next();
|
||||
PathBuf::from("//")
|
||||
} else {
|
||||
PathBuf::new()
|
||||
}
|
||||
} else {
|
||||
env::current_dir()?
|
||||
};
|
||||
normalized.extend(components);
|
||||
|
||||
// "Interfaces using pathname resolution may specify additional constraints
|
||||
// when a pathname that does not name an existing directory contains at
|
||||
// least one non- <slash> character and contains one or more trailing
|
||||
// <slash> characters".
|
||||
// A trailing <slash> is also meaningful if "a symbolic link is
|
||||
// encountered during pathname resolution".
|
||||
if path_os.ends_with(b"/") {
|
||||
normalized.push("");
|
||||
}
|
||||
|
||||
Ok(normalized)
|
||||
}
|
||||
|
@ -260,3 +260,19 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
|
||||
)?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Make a Windows path absolute.
|
||||
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
|
||||
if path.as_os_str().bytes().starts_with(br"\\?\") {
|
||||
return Ok(path.into());
|
||||
}
|
||||
let path = to_u16s(path)?;
|
||||
let lpfilename = path.as_ptr();
|
||||
fill_utf16_buf(
|
||||
// SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
|
||||
// `lpfilename` is a pointer to a null terminated string that is not
|
||||
// invalidated until after `GetFullPathNameW` returns successfully.
|
||||
|buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
|
||||
super::os2path,
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user