diff --git a/src/steps/git.rs b/src/steps/git.rs index 56f6df0d..d06c4684 100644 --- a/src/steps/git.rs +++ b/src/steps/git.rs @@ -1,7 +1,7 @@ -use crate::error::TopgradeError; +use crate::error::{SkipStep, TopgradeError}; use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; -use crate::utils::{which, HumanizedPath}; +use crate::utils::which; use anyhow::Result; use console::style; use futures::future::{join_all, Future}; @@ -14,6 +14,9 @@ use std::process::Command; use tokio::runtime::Runtime; use tokio_process::CommandExt as TokioCommandExt; +#[cfg(windows)] +static PATH_PREFIX: &str = "\\\\?\\"; + #[derive(Debug)] pub struct Git { git: Option, @@ -56,6 +59,18 @@ impl Git { debug!("Checking if {} is a git repository", path.display()); + #[cfg(windows)] + let path = { + let mut path_string = path.into_os_string().to_string_lossy().into_owned(); + if path_string.starts_with(PATH_PREFIX) { + path_string.replace_range(0..PATH_PREFIX.len(), ""); + } + + debug!("Transformed path to {}", path_string); + + path_string + }; + if let Some(git) = &self.git { let output = Command::new(&git) .args(&["rev-parse", "--show-toplevel"]) @@ -82,7 +97,7 @@ impl Git { extra_arguments: &Option, ) -> Result<()> { if repositories.repositories.is_empty() { - return Ok(()); + return Err(SkipStep.into()); } let git = self.git.as_ref().unwrap(); @@ -93,7 +108,7 @@ impl Git { repositories .repositories .iter() - .for_each(|repo| println!("Would pull {}", HumanizedPath::from(std::path::Path::new(&repo)))); + .for_each(|repo| println!("Would pull {}", &repo)); return Ok(()); } @@ -103,7 +118,7 @@ impl Git { .iter() .map(|repo| { let repo = repo.clone(); - let path = format!("{}", HumanizedPath::from(std::path::Path::new(&repo))); + let path = repo.to_string(); let before_revision = get_head_revision(git, &repo); let cloned_git = git.to_owned(); diff --git a/src/utils.rs b/src/utils.rs index 260b1d45..f684c19e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,8 +4,8 @@ use anyhow::Result; use log::{debug, error}; use std::env; use std::ffi::OsStr; -use std::fmt::{self, Debug}; -use std::path::{Component, Path, PathBuf}; +use std::fmt::Debug; +use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output}; use which_crate; @@ -93,93 +93,10 @@ pub fn sudo() -> Option { which("sudo").or_else(|| which("pkexec")) } -/// `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(()) - } -} - pub fn editor() -> String { env::var("EDITOR").unwrap_or_else(|_| String::from(if cfg!(windows) { "notepad" } else { "vi" })) } -#[cfg(test)] -#[cfg(windows)] -mod tests { - use super::*; - - fn humanize>(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")); - } -} - pub fn require + Debug>(binary_name: T) -> Result { match which_crate::which(&binary_name) { Ok(path) => {