diff --git a/src/main.rs b/src/main.rs index a72063c2..861eaf81 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,7 @@ use self::report::Report; use self::terminal::Terminal; use clap::{App, Arg}; use failure::Error; +use std::borrow::Cow; use std::env; use std::process::exit; @@ -51,6 +52,23 @@ struct StepFailed; #[fail(display = "Cannot find the user base directories")] struct NoBaseDirectories; +fn execute<'a, F, M>(func: F, terminal: &mut Terminal) -> Option<(M, bool)> +where + M: Into>, + F: Fn(&mut Terminal) -> Option<(M, bool)>, +{ + while let Some((key, success)) = func(terminal) { + if success { + return Some((key, success)); + } + + if !terminal.should_retry() { + return Some((key, success)); + } + } + None +} + fn run() -> Result<(), Error> { let matches = App::new("Topgrade") .version(crate_version!()) @@ -96,20 +114,20 @@ fn run() -> Result<(), Error> { let powershell = windows::Powershell::new(); #[cfg(windows)] - report.push_result(powershell.update_modules(&mut terminal)); + report.push_result(execute(|terminal| powershell.update_modules(terminal), &mut terminal)); #[cfg(target_os = "linux")] { if !(matches.is_present("no_system")) { - report.push_result(linux::upgrade(&sudo, &mut terminal)); + report.push_result(execute(|terminal| linux::upgrade(&sudo, terminal), &mut terminal)); } } #[cfg(windows)] - report.push_result(windows::run_chocolatey(&mut terminal)); + report.push_result(execute(|terminal| windows::run_chocolatey(terminal), &mut terminal)); #[cfg(unix)] - report.push_result(unix::run_homebrew(&mut terminal)); + report.push_result(execute(|terminal| unix::run_homebrew(terminal), &mut terminal)); git_repos.insert(base_dirs.home_dir().join(".emacs.d")); git_repos.insert(base_dirs.home_dir().join(".vim")); @@ -137,57 +155,81 @@ fn run() -> Result<(), Error> { } for repo in git_repos.repositories() { - report.push_result(git.pull(&repo, &mut terminal)); + report.push_result(execute(|terminal| git.pull(&repo, terminal), &mut terminal)); } #[cfg(unix)] { - report.push_result(unix::run_zplug(&base_dirs, &mut terminal)); - report.push_result(unix::run_fisherman(&base_dirs, &mut terminal)); - report.push_result(unix::run_tpm(&base_dirs, &mut terminal)); + report.push_result(execute(|terminal| unix::run_zplug(&base_dirs, terminal), &mut terminal)); + report.push_result(execute( + |terminal| unix::run_fisherman(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute(|terminal| unix::run_tpm(&base_dirs, terminal), &mut terminal)); } - report.push_result(generic::run_rustup(&base_dirs, &mut terminal)); - report.push_result(generic::run_cargo_update(&base_dirs, &mut terminal)); - report.push_result(generic::run_emacs(&base_dirs, &mut terminal)); - report.push_result(vim::upgrade_vim(&base_dirs, &mut terminal)); - report.push_result(vim::upgrade_neovim(&base_dirs, &mut terminal)); - report.push_result(node::run_npm_upgrade(&base_dirs, &mut terminal)); - report.push_result(node::yarn_global_update(&mut terminal)); - report.push_result(generic::run_apm(&mut terminal)); + report.push_result(execute( + |terminal| generic::run_rustup(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute( + |terminal| generic::run_cargo_update(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute( + |terminal| generic::run_emacs(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute( + |terminal| vim::upgrade_vim(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute( + |terminal| vim::upgrade_neovim(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute( + |terminal| node::run_npm_upgrade(&base_dirs, terminal), + &mut terminal, + )); + report.push_result(execute(|terminal| node::yarn_global_update(terminal), &mut terminal)); + report.push_result(execute(|terminal| generic::run_apm(terminal), &mut terminal)); #[cfg(target_os = "linux")] { - report.push_result(linux::run_flatpak(&mut terminal)); - report.push_result(linux::run_snap(&sudo, &mut terminal)); + report.push_result(execute(|terminal| linux::run_flatpak(terminal), &mut terminal)); + report.push_result(execute(|terminal| linux::run_snap(&sudo, terminal), &mut terminal)); } if let Some(commands) = config.commands() { for (name, command) in commands { - report.push_result(Some(( - name, - generic::run_custom_command(&name, &command, &mut terminal).is_ok(), - ))); + report.push_result(execute( + |terminal| Some((name, generic::run_custom_command(&name, &command, terminal).is_ok())), + &mut terminal, + )); } } #[cfg(target_os = "linux")] { - report.push_result(linux::run_fwupdmgr(&mut terminal)); - report.push_result(linux::run_needrestart(&sudo, &mut terminal)); + report.push_result(execute(|terminal| linux::run_fwupdmgr(terminal), &mut terminal)); + report.push_result(execute( + |terminal| linux::run_needrestart(&sudo, terminal), + &mut terminal, + )); } #[cfg(target_os = "macos")] { if !(matches.is_present("no_system")) { - report.push_result(macos::upgrade_macos(&mut terminal)); + report.push_result(execute(|terminal| macos::upgrade_macos(terminal), &mut terminal)); } } #[cfg(windows)] { if !(matches.is_present("no_system")) { - report.push_result(powershell.windows_update(&mut terminal)); + report.push_result(execute(|terminal| powershell.windows_update(terminal), &mut terminal)); } } diff --git a/src/terminal.rs b/src/terminal.rs index df2ab6e4..c11dae48 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,5 +1,5 @@ use std::cmp::{max, min}; -use std::io::Write; +use std::io::{stdin, Write}; use term_size; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -66,4 +66,31 @@ impl Terminal { let _ = self.stdout.reset(); let _ = self.stdout.flush(); } + + pub fn should_retry(&mut self) -> bool { + if self.width.is_none() { + return false; + } + println!(""); + loop { + let mut result = String::new(); + + let _ = self + .stdout + .set_color(ColorSpec::new().set_fg(Some(Color::Yellow)).set_bold(true)); + let _ = write!(&mut self.stdout, "Retry? [y/N] "); + let _ = self.stdout.reset(); + let _ = self.stdout.flush(); + + if let Ok(_) = stdin().read_line(&mut result) { + match result.as_str() { + "y\n" => return true, + "n\n" | "\n" => return false, + _ => (), + } + } else { + return false; + } + } + } }