Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Normalize paths in Windows (fix #113) #116

Merged
merged 6 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ test_script:
cargo fmt --all -- --check &&
cargo clippy --all-targets --all-features -- -D warnings &&
cargo clippy --all-targets -- -D warnings
cargo test
)

before_deploy:
Expand Down
1 change: 1 addition & 0 deletions ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ main() {
cross clippy --all-targets --all-features -- -D warnings

if [ ! -z $DISABLE_TESTS ]; then
cross test
return
fi

Expand Down
6 changes: 3 additions & 3 deletions src/steps/git.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::Error;
use crate::executor::{CommandExt, RunType};
use crate::terminal::print_separator;
use crate::utils::which;
use crate::utils::{which, HumanizedPath};
use log::{debug, error};
use std::collections::HashSet;
use std::io;
Expand Down Expand Up @@ -58,7 +58,7 @@ impl Git {
pub fn pull<P: AsRef<Path>>(&self, path: P, run_type: RunType) -> Option<(String, bool)> {
let path = path.as_ref();

print_separator(format!("Pulling {}", path.display()));
print_separator(format!("Pulling {}", HumanizedPath::from(path)));

let git = self.git.as_ref().unwrap();

Expand All @@ -73,7 +73,7 @@ impl Git {
}()
.is_ok();

Some((format!("git: {}", path.display()), success))
Some((format!("git: {}", HumanizedPath::from(path)), success))
}
}

Expand Down
89 changes: 87 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -68,3 +68,88 @@ 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 slashes
r-darwish marked this conversation as resolved.
Show resolved Hide resolved
/// instead of backslashes 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 is_prefix = if let Component::Prefix(_) = &component {
true
} else {
false
};

let printed = match &component {
Component::Normal(c) if *c == "?" => false,
Component::RootDir | Component::CurDir => false,
Component::ParentDir => {
write!(f, "..")?;
true
}
Component::Prefix(p) => {
write!(f, "{}", p.as_os_str().to_string_lossy())?;
true
}
Component::Normal(c) => {
write!(f, "{}", c.to_string_lossy())?;
true
}
};

if printed && (iterator.peek().is_some() || is_prefix) {
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"));
}
}