use std::path::{Component, Path, PathBuf};
#[cfg_attr(not(windows), allow(dead_code))]
pub(crate) fn normalize_for_comparison(path: &Path) -> PathBuf {
let mut components = Vec::new();
for component in path.components() {
match component {
Component::CurDir => {} Component::ParentDir => {
if matches!(components.last(), Some(Component::Normal(_))) {
components.pop();
} else {
components.push(component);
}
}
_ => components.push(component),
}
}
if components.is_empty() {
return PathBuf::from(".");
}
let result: PathBuf = components.iter().collect();
result
}
#[cfg(windows)]
pub(crate) fn normalize_windows_path_str(path: &str) -> String {
let normalized = path.replace('/', "\\");
let trimmed = if normalized.len() > 3 && normalized.ends_with('\\') {
&normalized[..normalized.len() - 1]
} else {
&normalized
};
let path = Path::new(trimmed);
normalize_for_comparison(path)
.to_string_lossy()
.into_owned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_removes_dot_components() {
let result = normalize_for_comparison(Path::new("/usr/./local/bin"));
assert_eq!(result, PathBuf::from("/usr/local/bin"));
}
#[test]
fn normalize_resolves_parent_components() {
let result = normalize_for_comparison(Path::new("/home/user/../user/bin"));
assert_eq!(result, PathBuf::from("/home/user/bin"));
}
#[test]
fn normalize_resolves_multiple_parent_components() {
let result = normalize_for_comparison(Path::new("/a/b/c/../../d"));
assert_eq!(result, PathBuf::from("/a/d"));
}
#[test]
fn normalize_preserves_absolute_path() {
let result = normalize_for_comparison(Path::new("/usr/local/bin"));
assert_eq!(result, PathBuf::from("/usr/local/bin"));
}
#[test]
fn normalize_handles_root() {
let result = normalize_for_comparison(Path::new("/"));
assert_eq!(result, PathBuf::from("/"));
}
#[test]
fn normalize_handles_relative_with_parent() {
let result = normalize_for_comparison(Path::new("../foo"));
assert_eq!(result, PathBuf::from("../foo"));
}
#[test]
fn normalize_empty_becomes_dot() {
let result = normalize_for_comparison(Path::new(""));
assert_eq!(result, PathBuf::from("."));
}
#[test]
fn normalize_dot_only() {
let result = normalize_for_comparison(Path::new("."));
assert_eq!(result, PathBuf::from("."));
}
#[test]
fn normalize_trailing_slash() {
let result = normalize_for_comparison(Path::new("/usr/local/bin/"));
assert_eq!(result, PathBuf::from("/usr/local/bin"));
}
}