Normalize paths in Windows (fix #113) (#116)

While `//?/C:/Users/Someone/config-files` is a valid path, it's not very human friendly.
Such paths are returned from `BaseDirs`. This patch formats them in the traditional way.
This commit is contained in:
Roey Darwish Dror
2019-01-30 16:00:10 +02:00
committed by GitHub
parent 38552646f4
commit 022115e237
4 changed files with 91 additions and 6 deletions

View File

@@ -1,8 +1,8 @@
use super::error::{Error, ErrorKind};
use log::{debug, error};
use std::ffi::OsStr;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::fmt::{self, Debug};
use std::path::{Component, Path, PathBuf};
use std::process::{ExitStatus, Output};
use which_crate;
@@ -68,3 +68,86 @@ pub fn which<T: AsRef<OsStr> + Debug>(binary_name: T) -> Option<PathBuf> {
}
}
}
/// `std::fmt::Display` implementation for `std::path::Path`.
///
/// This struct differs from `std::path::Display` in that in Windows it takes care of printing backslashes
/// instead of slashes and don't print the `\\?` prefix in long paths.
pub struct HumanizedPath<'a> {
path: &'a Path,
}
impl<'a> From<&'a Path> for HumanizedPath<'a> {
fn from(path: &'a Path) -> Self {
Self { path }
}
}
impl<'a> fmt::Display for HumanizedPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if cfg!(windows) {
let mut iterator = self.path.components().peekable();
while let Some(component) = iterator.next() {
let mut print_seperator = iterator.peek().is_some();
match &component {
Component::Normal(c) if *c == "?" => {
print_seperator = false;
}
Component::RootDir | Component::CurDir => {
print_seperator = false;
}
Component::ParentDir => {
write!(f, "..")?;
}
Component::Prefix(p) => {
write!(f, "{}", p.as_os_str().to_string_lossy())?;
print_seperator = true;
}
Component::Normal(c) => {
write!(f, "{}", c.to_string_lossy())?;
}
};
if print_seperator {
write!(f, "{}", std::path::MAIN_SEPARATOR)?;
}
}
} else {
write!(f, "{}", self.path.display())?;
}
Ok(())
}
}
#[cfg(test)]
#[cfg(windows)]
mod tests {
use super::*;
fn humanize<P: AsRef<Path>>(path: P) -> String {
format!("{}", HumanizedPath::from(path.as_ref()))
}
#[test]
fn test_just_drive() {
assert_eq!("C:\\", humanize("C:\\"));
}
#[test]
fn test_path() {
assert_eq!("C:\\hi", humanize("C:\\hi"));
}
#[test]
fn test_unc() {
assert_eq!("\\\\server\\share\\", humanize("\\\\server\\share"));
}
#[test]
fn test_long_path() {
assert_eq!("C:\\hi", humanize("//?/C:/hi"));
}
}