mirror of
https://github.com/nushell/nushell
synced 2025-01-28 04:45:18 +00:00
709b2479d9
# Description Fixes #12758. #12662 introduced a bug where calling `cd` with a path with a trailing slash would cause `PWD` to be set to a path including a trailing slash, which is not allowed. This adds a helper to `nu_path` to remove this, and uses it in the `cd` command to clean it up before setting `PWD`. # Tests + Formatting I added some tests to make sure we don't regress on this in the future. - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
108 lines
3.1 KiB
Rust
108 lines
3.1 KiB
Rust
use std::{
|
|
borrow::Cow,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
/// Strip any trailing slashes from a non-root path. This is required in some contexts, for example
|
|
/// for the `PWD` environment variable.
|
|
pub fn strip_trailing_slash(path: &Path) -> Cow<Path> {
|
|
if has_trailing_slash(path) {
|
|
// If there are, the safest thing to do is have Rust parse the path for us and build it
|
|
// again. This will correctly handle a root directory, but it won't add the trailing slash.
|
|
let mut out = PathBuf::with_capacity(path.as_os_str().len());
|
|
out.extend(path.components());
|
|
Cow::Owned(out)
|
|
} else {
|
|
// The path is safe and doesn't contain any trailing slashes.
|
|
Cow::Borrowed(path)
|
|
}
|
|
}
|
|
|
|
/// `true` if the path has a trailing slash, including if it's the root directory.
|
|
#[cfg(windows)]
|
|
pub fn has_trailing_slash(path: &Path) -> bool {
|
|
use std::os::windows::ffi::OsStrExt;
|
|
|
|
let last = path.as_os_str().encode_wide().last();
|
|
last == Some(b'\\' as u16) || last == Some(b'/' as u16)
|
|
}
|
|
|
|
/// `true` if the path has a trailing slash, including if it's the root directory.
|
|
#[cfg(unix)]
|
|
pub fn has_trailing_slash(path: &Path) -> bool {
|
|
use std::os::unix::ffi::OsStrExt;
|
|
|
|
let last = path.as_os_str().as_bytes().last();
|
|
last == Some(&b'/')
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[cfg_attr(not(unix), ignore = "only for Unix")]
|
|
#[test]
|
|
fn strip_root_unix() {
|
|
assert_eq!(Path::new("/"), strip_trailing_slash(Path::new("/")));
|
|
}
|
|
|
|
#[cfg_attr(not(unix), ignore = "only for Unix")]
|
|
#[test]
|
|
fn strip_non_trailing_unix() {
|
|
assert_eq!(
|
|
Path::new("/foo/bar"),
|
|
strip_trailing_slash(Path::new("/foo/bar"))
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(not(unix), ignore = "only for Unix")]
|
|
#[test]
|
|
fn strip_trailing_unix() {
|
|
assert_eq!(
|
|
Path::new("/foo/bar"),
|
|
strip_trailing_slash(Path::new("/foo/bar/"))
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(not(windows), ignore = "only for Windows")]
|
|
#[test]
|
|
fn strip_root_windows() {
|
|
assert_eq!(Path::new(r"C:\"), strip_trailing_slash(Path::new(r"C:\")));
|
|
}
|
|
|
|
#[cfg_attr(not(windows), ignore = "only for Windows")]
|
|
#[test]
|
|
fn strip_non_trailing_windows() {
|
|
assert_eq!(
|
|
Path::new(r"C:\foo\bar"),
|
|
strip_trailing_slash(Path::new(r"C:\foo\bar"))
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(not(windows), ignore = "only for Windows")]
|
|
#[test]
|
|
fn strip_non_trailing_windows_unc() {
|
|
assert_eq!(
|
|
Path::new(r"\\foo\bar"),
|
|
strip_trailing_slash(Path::new(r"\\foo\bar"))
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(not(windows), ignore = "only for Windows")]
|
|
#[test]
|
|
fn strip_trailing_windows() {
|
|
assert_eq!(
|
|
Path::new(r"C:\foo\bar"),
|
|
strip_trailing_slash(Path::new(r"C:\foo\bar\"))
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(not(windows), ignore = "only for Windows")]
|
|
#[test]
|
|
fn strip_trailing_windows_unc() {
|
|
assert_eq!(
|
|
Path::new(r"\\foo\bar"),
|
|
strip_trailing_slash(Path::new(r"\\foo\bar\"))
|
|
);
|
|
}
|
|
}
|