From ef69dc01ba0ec4a6a1a05df38886a979e7b465b0 Mon Sep 17 00:00:00 2001 From: Roey Darwish Dror Date: Wed, 6 Jun 2018 15:32:38 +0300 Subject: [PATCH] Better error handling (fixes #15) --- src/git.rs | 14 ++-- src/main.rs | 158 +++++++---------------------------------- src/report.rs | 32 ++++++++- src/steps.rs | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+), 142 deletions(-) create mode 100644 src/steps.rs diff --git a/src/git.rs b/src/git.rs index 99d9c72b..044245d4 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,3 +1,4 @@ +use super::Check; use failure::Error; use std::path::{Path, PathBuf}; use std::process::Command; @@ -38,17 +39,18 @@ impl Git { None } - pub fn pull>(&self, path: P) -> Result, Error> { + pub fn pull>(&self, path: P) -> Result, Error> { if let Some(git) = &self.git { - if let Ok(mut command) = Command::new(&git) + Command::new(&git) .arg("pull") .arg("--rebase") .arg("--autostash") .current_dir(path) - .spawn() - { - return Ok(Some(command.wait()?.success())); - } + .spawn()? + .wait()? + .check()?; + + return Ok(Some(())); } Ok(None) diff --git a/src/main.rs b/src/main.rs index 2234a201..78762193 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ extern crate termion; mod git; mod report; +mod steps; mod terminal; use failure::Error; @@ -16,7 +17,8 @@ use report::{Report, Reporter}; use std::collections::HashSet; use std::env::home_dir; use std::path::PathBuf; -use std::process::{Command, ExitStatus}; +use std::process::ExitStatus; +use steps::*; use terminal::Terminal; use which::which; @@ -38,8 +40,6 @@ impl Check for ExitStatus { } } -const EMACS_UPGRADE: &str = include_str!("emacs.el"); - fn home_path(p: &str) -> PathBuf { let mut path = home_dir().unwrap(); path.push(p); @@ -88,79 +88,45 @@ fn main() -> Result<(), Error> { if cfg!(unix) { if let Ok(zsh) = which("zsh") { - terminal.print_separator("zplug"); if home_path(".zplug").exists() { - Command::new(&zsh) - .args(&["-c", "source ~/.zshrc && zplug update"]) - .spawn()? - .wait()? - .report("zplug", &mut reports); + terminal.print_separator("zplug"); + run_zplug(&zsh).report("zplug", &mut reports); } } if let Some(tpm) = tpm() { terminal.print_separator("tmux plugins"); - Command::new(&tpm) - .arg("all") - .spawn()? - .wait()? - .report("tmux", &mut reports); + run_tpm(&tpm).report("tmux", &mut reports); } } let cargo_upgrade = home_path(".cargo/bin/cargo-install-update"); if cargo_upgrade.exists() { terminal.print_separator("Cargo"); - Command::new(&cargo_upgrade) - .args(&["install-update", "--all"]) - .spawn()? - .wait()? - .report("Cargo", &mut reports); + run_cargo_update(&cargo_upgrade).report("Cargo", &mut reports); } if let Ok(emacs) = which("emacs") { - if home_path(".emacs.d").exists() { + let init_file = home_path(".emacs.d/init.el"); + if init_file.exists() { terminal.print_separator("Emacs"); - Command::new(&emacs) - .args(&[ - "--batch", - "-l", - home_path(".emacs.d/init.el").to_str().unwrap(), - "--eval", - EMACS_UPGRADE, - ]) - .spawn()? - .wait()? - .report("Emacs", &mut reports); + run_emacs(&emacs, &home_path(".emacs.d/init.el")).report("Emacs", &mut reports); } } if let Ok(gem) = which("gem") { terminal.print_separator("RubyGems"); - Command::new(&gem) - .args(&["update"]) - .spawn()? - .wait()? - .report("RubyGems", &mut reports); + run_gem(&gem).report("RubyGems", &mut reports); } if let Ok(npm) = which("npm") { terminal.print_separator("Node Package Manager"); - Command::new(&npm) - .args(&["update", "-g"]) - .spawn()? - .wait()? - .report("Node Package Manager", &mut reports); + run_npm(&npm).report("Node Package Manager", &mut reports); } if let Ok(apm) = which("apm") { terminal.print_separator("Atom Package Manager"); - Command::new(&apm) - .args(&["upgrade", "--confirm=false"]) - .spawn()? - .wait() - .map_err(Error::from)? - .report("Atom Package Manager", &mut reports); + run_apm(&apm).report("Atom Package Manager", &mut reports); } if cfg!(target_os = "linux") { @@ -168,87 +134,29 @@ fn main() -> Result<(), Error> { terminal.print_separator("System update"); match os_type::current_platform().os_type { - OSType::Arch => { - if let Ok(yay) = which("yay") { - Command::new(yay) - .spawn()? - .wait()? - .report("System upgrade", &mut reports); - } else { - if let Ok(sudo) = &sudo { - Command::new(&sudo) - .args(&["pacman", "-Syu"]) - .spawn()? - .wait()? - .report("System upgrade", &mut reports); - } else { - terminal.print_warning("No sudo or yay detected. Skipping system upgrade"); - } - } - } - - OSType::CentOS | OSType::Redhat => { - if let Ok(sudo) = &sudo { - Command::new(&sudo) - .args(&["yum", "upgrade"]) - .spawn()? - .wait()? - .report("System upgrade", &mut reports);; - } - } - - OSType::Ubuntu | OSType::Debian => { - if let Ok(sudo) = &sudo { - Command::new(&sudo) - .args(&["apt", "update"]) - .spawn()? - .wait()? - .check() - .and_then(|()| { - Command::new(&sudo) - .args(&["apt", "dist-upgrade"]) - .spawn()? - .wait() - .map_err(Error::from) - })? - .report("System upgrade", &mut reports);; - } - } - + OSType::Arch => Some(upgrade_arch_linux(&sudo, &terminal)), + OSType::CentOS | OSType::Redhat => Some(upgrade_redhat(&sudo, &terminal)), + OSType::Ubuntu | OSType::Debian => Some(upgrade_debian(&sudo, &terminal)), OSType::Unknown => { terminal.print_warning( "Could not detect your Linux distribution. Do you have lsb-release installed?", ); + + None } - _ => (), - } + _ => None, + }.report("System upgrade", &mut reports); if let Ok(fwupdmgr) = which("fwupdmgr") { terminal.print_separator("Firmware upgrades"); - Command::new(&fwupdmgr) - .arg("refresh") - .spawn()? - .wait()? - .check() - .and_then(|()| { - Command::new(&fwupdmgr) - .arg("get-updates") - .spawn()? - .wait() - .map_err(Error::from) - })? - .report("Firmware upgrade", &mut reports); + run_fwupdmgr(&fwupdmgr).report("Firmware upgrade", &mut reports); } if let Ok(sudo) = &sudo { - if let Ok(needrestart) = which("needrestart") { + if let Ok(_) = which("needrestart") { terminal.print_separator("Check for needed restarts"); - Command::new(&sudo) - .arg(&needrestart) - .spawn()? - .wait()? - .report("Restarts", &mut reports); + run_needrestart(&sudo).report("Restarts", &mut reports); } } } @@ -256,27 +164,11 @@ fn main() -> Result<(), Error> { if cfg!(target_os = "macos") { if let Ok(brew) = which("brew") { terminal.print_separator("Homebrew"); - Command::new(&brew) - .arg("update") - .spawn()? - .wait()? - .check() - .and_then(|()| { - Command::new(&brew) - .arg("upgrade") - .spawn()? - .wait() - .map_err(Error::from) - })? - .report("Homebrew", &mut reports); + run_homebrew(&brew).report("Homebrew", &mut reports); } terminal.print_separator("System update"); - Command::new("softwareupdate") - .args(&["--install", "--all"]) - .spawn()? - .wait()? - .report("System upgrade", &mut reports);; + upgrade_macos().report("System upgrade", &mut reports);; } let mut reports: Vec<_> = reports.into_iter().collect(); diff --git a/src/report.rs b/src/report.rs index b386246b..50a3c7dc 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::collections::HashMap; -use std::process::ExitStatus; pub type Report = HashMap; @@ -8,9 +7,30 @@ pub trait Reporter { fn report<'a, M: Into>>(&self, key: M, report: &mut Report); } -impl Reporter for ExitStatus { +impl Reporter for Result +where + T: Reporter, +{ fn report<'a, M: Into>>(&self, key: M, report: &mut Report) { - report.insert(key.into().into_owned(), self.success()); + match self { + Err(_) => { + report.insert(key.into().into_owned(), false); + } + Ok(item) => { + item.report(key, report); + } + } + } +} + +impl Reporter for Option +where + T: Reporter, +{ + fn report<'a, M: Into>>(&self, key: M, report: &mut Report) { + if let Some(item) = self { + item.report(key, report); + } } } @@ -19,3 +39,9 @@ impl Reporter for bool { report.insert(key.into().into_owned(), *self); } } + +impl Reporter for () { + fn report<'a, M: Into>>(&self, key: M, report: &mut Report) { + report.insert(key.into().into_owned(), true); + } +} diff --git a/src/steps.rs b/src/steps.rs new file mode 100644 index 00000000..6745f999 --- /dev/null +++ b/src/steps.rs @@ -0,0 +1,192 @@ +use super::terminal::Terminal; +use super::{which, Check}; +use failure; +use std::path::PathBuf; +use std::process::Command; + +const EMACS_UPGRADE: &str = include_str!("emacs.el"); + +pub fn run_zplug(zsh: &PathBuf) -> Result<(), failure::Error> { + Command::new(zsh) + .args(&["-c", "source ~/.zshrc && zplug update"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_tpm(tpm: &PathBuf) -> Result<(), failure::Error> { + Command::new(&tpm).arg("all").spawn()?.wait()?.check()?; + + Ok(()) +} + +pub fn run_cargo_update(cargo_update: &PathBuf) -> Result<(), failure::Error> { + Command::new(&cargo_update) + .args(&["install-update", "--all"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_emacs(emacs: &PathBuf, init: &PathBuf) -> Result<(), failure::Error> { + Command::new(&emacs) + .args(&[ + "--batch", + "-l", + init.to_str().unwrap(), + "--eval", + EMACS_UPGRADE, + ]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_gem(gem: &PathBuf) -> Result<(), failure::Error> { + Command::new(&gem) + .args(&["update"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_npm(npm: &PathBuf) -> Result<(), failure::Error> { + Command::new(&npm) + .args(&["update", "-g"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_apm(apm: &PathBuf) -> Result<(), failure::Error> { + Command::new(&apm) + .args(&["upgrade", "--confirm=false"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_needrestart(sudo: &PathBuf) -> Result<(), failure::Error> { + Command::new(&sudo) + .arg("needrestart") + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_fwupdmgr(fwupdmgr: &PathBuf) -> Result<(), failure::Error> { + Command::new(&fwupdmgr) + .arg("refresh") + .spawn()? + .wait()? + .check()?; + + Command::new(&fwupdmgr) + .arg("get-updates") + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn upgrade_macos() -> Result<(), failure::Error> { + Command::new("softwareupdate") + .args(&["--install", "--all"]) + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn run_homebrew(homebrew: &PathBuf) -> Result<(), failure::Error> { + Command::new(&homebrew) + .arg("update") + .spawn()? + .wait()? + .check()?; + + Command::new(&homebrew) + .arg("upgrade") + .spawn()? + .wait()? + .check()?; + + Ok(()) +} + +pub fn upgrade_arch_linux( + sudo: &Result, + terminal: &Terminal, +) -> Result<(), failure::Error> { + if let Ok(yay) = which("yay") { + Command::new(yay).spawn()?.wait()?.check()?; + } else { + if let Ok(sudo) = &sudo { + Command::new(&sudo) + .args(&["pacman", "-Syu"]) + .spawn()? + .wait()? + .check()?; + } else { + terminal.print_warning("No sudo or yay detected. Skipping system upgrade"); + } + } + + Ok(()) +} + +pub fn upgrade_redhat( + sudo: &Result, + terminal: &Terminal, +) -> Result<(), failure::Error> { + if let Ok(sudo) = &sudo { + Command::new(&sudo) + .args(&["yum", "upgrade"]) + .spawn()? + .wait()? + .check()?; + } else { + terminal.print_warning("No sudo detected. Skipping system upgrade"); + } + + Ok(()) +} + +pub fn upgrade_debian( + sudo: &Result, + terminal: &Terminal, +) -> Result<(), failure::Error> { + if let Ok(sudo) = &sudo { + Command::new(&sudo) + .args(&["apt", "update"]) + .spawn()? + .wait()? + .check()?; + + Command::new(&sudo) + .args(&["apt", "upgrade"]) + .spawn()? + .wait()? + .check()?; + } else { + terminal.print_warning("No sudo detected. Skipping system upgrade"); + } + + Ok(()) +}