83 lines
2.6 KiB
Rust
83 lines
2.6 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
//! Path related helpers.
|
|
|
|
use std::ffi::OsStr;
|
|
use std::path::{Component, MAIN_SEPARATOR_STR, Path, PathBuf};
|
|
|
|
/// Normalizes a given path by removing redundant components.
|
|
/// The given path must be absolute (e.g. by joining it with the current working directory).
|
|
pub fn normalize(path: &Path) -> PathBuf {
|
|
debug_assert!(path.is_absolute());
|
|
|
|
let mut res = PathBuf::with_capacity(path.as_os_str().len());
|
|
let mut root_len = 0;
|
|
|
|
for component in path.components() {
|
|
match component {
|
|
Component::Prefix(p) => res.push(p.as_os_str()),
|
|
Component::RootDir => {
|
|
res.push(OsStr::new(MAIN_SEPARATOR_STR));
|
|
root_len = res.as_os_str().len();
|
|
}
|
|
Component::CurDir => {}
|
|
Component::ParentDir => {
|
|
// Get the length up to the parent directory
|
|
if let Some(len) = res
|
|
.parent()
|
|
.map(|p| p.as_os_str().as_encoded_bytes().len())
|
|
// Ensure we don't pop the root directory
|
|
&& len >= root_len
|
|
{
|
|
res.as_mut_os_string().truncate(len);
|
|
}
|
|
}
|
|
Component::Normal(p) => res.push(p),
|
|
}
|
|
}
|
|
|
|
res
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::ffi::OsString;
|
|
use std::path::Path;
|
|
|
|
use super::*;
|
|
|
|
fn norm(s: &str) -> OsString {
|
|
normalize(Path::new(s)).into_os_string()
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn test_unix() {
|
|
assert_eq!(norm("/a/b/c"), "/a/b/c");
|
|
assert_eq!(norm("/a/b/c/"), "/a/b/c");
|
|
assert_eq!(norm("/a/./b"), "/a/b");
|
|
assert_eq!(norm("/a/b/../c"), "/a/c");
|
|
assert_eq!(norm("/../../a"), "/a");
|
|
assert_eq!(norm("/../"), "/");
|
|
assert_eq!(norm("/a//b/c"), "/a/b/c");
|
|
assert_eq!(norm("/a/b/c/../../../../d"), "/d");
|
|
assert_eq!(norm("//"), "/");
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn test_windows() {
|
|
assert_eq!(norm(r"C:\a\b\c"), r"C:\a\b\c");
|
|
assert_eq!(norm(r"C:\a\b\c\"), r"C:\a\b\c");
|
|
assert_eq!(norm(r"C:\a\.\b"), r"C:\a\b");
|
|
assert_eq!(norm(r"C:\a\b\..\c"), r"C:\a\c");
|
|
assert_eq!(norm(r"C:\..\..\a"), r"C:\a");
|
|
assert_eq!(norm(r"C:\..\"), r"C:\");
|
|
assert_eq!(norm(r"C:\a\\b\c"), r"C:\a\b\c");
|
|
assert_eq!(norm(r"C:/a\b/c"), r"C:\a\b\c");
|
|
assert_eq!(norm(r"C:\a\b\c\..\..\..\..\d"), r"C:\d");
|
|
assert_eq!(norm(r"\\server\share\path"), r"\\server\share\path");
|
|
}
|
|
}
|