diff --git a/Cargo.lock b/Cargo.lock index 6a10da1d..7048103f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "anyhow" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arc-swap" version = "0.4.4" @@ -392,7 +397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -996,7 +1001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1382,7 +1387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1489,7 +1494,7 @@ dependencies = [ "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1508,12 +1513,12 @@ dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1528,7 +1533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1589,6 +1594,24 @@ dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thiserror" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -1832,11 +1855,10 @@ dependencies = [ name = "topgrade" version = "3.5.0" dependencies = [ + "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1850,6 +1872,7 @@ dependencies = [ "shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-process 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2083,6 +2106,7 @@ dependencies = [ "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" "checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" @@ -2251,7 +2275,7 @@ dependencies = [ "checksum structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e" "checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" "checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" -"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" +"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "b3196bfbffbba3e57481b6ea32249fbaf590396a52505a2615adbb79d9d826d3" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" @@ -2259,6 +2283,8 @@ dependencies = [ "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" +"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" diff --git a/Cargo.toml b/Cargo.toml index 94f5f483..63ea7edd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,6 @@ readme = "README.md" [dependencies] directories = "2.0.1" -failure = "0.1.5" -failure_derive = "0.1.5" serde = { version = "1.0.92", features = ["derive"] } toml = "0.5.1" which_crate = { version = "2.0.1", package = "which" } @@ -34,6 +32,8 @@ openssl-probe = { version = "0.1.2", optional = true } pretty_env_logger = "0.3.0" glob = "0.3.0" strum = { version = "0.16.0", features = ["derive"]} +thiserror = "1.0.9" +anyhow = "1.0.25" [target.'cfg(unix)'.dependencies] nix = "0.15.0" diff --git a/src/config.rs b/src/config.rs index a114a234..9357d62e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,6 @@ -use super::error::{Error, ErrorKind}; use super::utils::editor; +use anyhow::Result; use directories::BaseDirs; -use failure::ResultExt; -use strum::{EnumIter, EnumString, EnumVariantNames, IntoEnumIterator}; - use log::{debug, LevelFilter}; use pretty_env_logger::formatted_timed_builder; use serde::Deserialize; @@ -14,6 +11,7 @@ use std::path::PathBuf; use std::process::Command; use std::{env, fs}; use structopt::StructOpt; +use strum::{EnumIter, EnumString, EnumVariantNames, IntoEnumIterator}; use toml; type Commands = BTreeMap; @@ -70,20 +68,18 @@ pub struct ConfigFile { } impl ConfigFile { - fn ensure(base_dirs: &BaseDirs) -> Result { + fn ensure(base_dirs: &BaseDirs) -> Result { let config_path = base_dirs.config_dir().join("topgrade.toml"); if !config_path.exists() { - write(&config_path, include_str!("../config.example.toml")) - .map_err(|e| { - debug!( - "Unable to write the example configuration file to {}: {}. Using blank config.", - config_path.display(), - e - ); - e - }) - .context(ErrorKind::Configuration)?; debug!("No configuration exists"); + write(&config_path, include_str!("../config.example.toml")).map_err(|e| { + debug!( + "Unable to write the example configuration file to {}: {}. Using blank config.", + config_path.display(), + e + ); + e + })?; } Ok(config_path) @@ -92,22 +88,18 @@ impl ConfigFile { /// Read the configuration file. /// /// If the configuration file does not exist the function returns the default ConfigFile. - fn read(base_dirs: &BaseDirs) -> Result { + fn read(base_dirs: &BaseDirs) -> Result { let config_path = Self::ensure(base_dirs)?; - let contents = fs::read_to_string(&config_path) - .map_err(|e| { - log::error!("Unable to read {}", config_path.display()); - e - }) - .context(ErrorKind::Configuration)?; + let contents = fs::read_to_string(&config_path).map_err(|e| { + log::error!("Unable to read {}", config_path.display()); + e + })?; - let mut result: Self = toml::from_str(&contents) - .map_err(|e| { - log::error!("Failed to deserialize {}", config_path.display()); - e - }) - .context(ErrorKind::Configuration)?; + let mut result: Self = toml::from_str(&contents).map_err(|e| { + log::error!("Failed to deserialize {}", config_path.display()); + e + })?; if let Some(ref mut paths) = &mut result.git_repos { for path in paths.iter_mut() { @@ -120,7 +112,7 @@ impl ConfigFile { Ok(result) } - fn edit(base_dirs: &BaseDirs) -> Result<(), Error> { + fn edit(base_dirs: &BaseDirs) -> Result<()> { let config_path = Self::ensure(base_dirs)?; let editor = editor(); @@ -128,8 +120,7 @@ impl ConfigFile { Command::new(editor) .arg(config_path) .spawn() - .and_then(|mut p| p.wait()) - .context(ErrorKind::Configuration)?; + .and_then(|mut p| p.wait())?; Ok(()) } } @@ -200,7 +191,7 @@ impl Config { /// Load the configuration. /// /// The function parses the command line arguments and reading the configuration file. - pub fn load(base_dirs: &BaseDirs, opt: CommandLineArgs) -> Result { + pub fn load(base_dirs: &BaseDirs, opt: CommandLineArgs) -> Result { let mut builder = formatted_timed_builder(); if opt.verbose { @@ -210,18 +201,9 @@ impl Config { builder.init(); let config_file = ConfigFile::read(base_dirs).unwrap_or_else(|e| { - use failure::Fail; - // Inform the user about errors when loading the configuration, // but fallback to the default config to at least attempt to do something log::error!("failed to load configuration: {}", e); - - let mut err = e.cause(); - while let Some(e) = err { - log::error!("{}", e); - err = e.cause(); - } - ConfigFile::default() }); @@ -235,7 +217,7 @@ impl Config { } /// Launch an editor to edit the configuration - pub fn edit(base_dirs: &BaseDirs) -> Result<(), Error> { + pub fn edit(base_dirs: &BaseDirs) -> Result<()> { ConfigFile::edit(base_dirs) } diff --git a/src/error.rs b/src/error.rs index 73096595..1c2325cd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,92 +1,28 @@ -use failure::{Backtrace, Context, Fail}; -use std::fmt::{self, Display}; use std::process::ExitStatus; +use thiserror::Error; -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "Error asking the user for retry")] - Retry, - - #[fail(display = "Cannot find the user base directories")] - NoBaseDirectories, - - #[fail(display = "A step failed")] - StepFailed, - - #[fail(display = "Error reading the configuration")] - Configuration, - - #[fail(display = "A custom pre-command failed")] - PreCommand, - - #[fail(display = "{}", _0)] +#[derive(Error, Debug, PartialEq)] +pub enum TopgradeError { + #[error("{0}")] ProcessFailed(ExitStatus), - #[fail(display = "Unknown Linux Distribution")] + #[error("Unknown Linux Distribution")] #[cfg(target_os = "linux")] UnknownLinuxDistribution, - #[fail(display = "Process execution failure")] - ProcessExecution, - - #[fail(display = "Self-update failure")] - #[cfg(feature = "self-update")] - SelfUpdate, - - #[fail(display = "A step should be skipped")] - SkipStep, - - #[cfg(all(windows, feature = "self-update"))] - #[fail(display = "Topgrade Upgraded")] - Upgraded(ExitStatus), + #[error("A pull action was failed")] + PullFailed, } -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } +#[derive(Error, Debug)] +#[error("A step failed")] +pub struct StepFailed; - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} +#[derive(Error, Debug)] +#[error("A step should be skipped")] +pub struct SkipStep; -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Error { - pub fn kind(&self) -> ErrorKind { - *self.inner.get_context() - } - - #[cfg(all(windows, feature = "self-update"))] - pub fn upgraded(&self) -> bool { - if let ErrorKind::Upgraded(_) = self.kind() { - true - } else { - false - } - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Error { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Error { - Error { inner } - } -} +#[cfg(all(windows, feature = "self-update"))] +#[derive(Error, Debug)] +#[error("Topgrade Upgraded")] +pub struct Upgraded(pub ExitStatus); diff --git a/src/executor.rs b/src/executor.rs index 055e03ca..ef0f1cc7 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,7 +1,7 @@ //! Utilities for command execution -use super::error::{Error, ErrorKind}; -use super::utils::Check; -use failure::ResultExt; +use crate::error::TopgradeError; +use crate::utils::Check; +use anyhow::Result; use log::trace; use std::ffi::{OsStr, OsString}; use std::path::Path; @@ -119,9 +119,9 @@ impl Executor { } /// See `std::process::Command::spawn` - pub fn spawn(&mut self) -> Result { + pub fn spawn(&mut self) -> Result { let result = match self { - Executor::Wet(c) => c.spawn().context(ErrorKind::ProcessExecution).map(ExecutorChild::Wet)?, + Executor::Wet(c) => c.spawn().map(ExecutorChild::Wet)?, Executor::Dry(c) => { c.dry_run(); ExecutorChild::Dry @@ -132,9 +132,9 @@ impl Executor { } /// See `std::process::Command::output` - pub fn output(&mut self) -> Result { + pub fn output(&mut self) -> Result { match self { - Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output().context(ErrorKind::ProcessExecution)?)), + Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output()?)), Executor::Dry(c) => { c.dry_run(); Ok(ExecutorOutput::Dry) @@ -145,7 +145,7 @@ impl Executor { /// A convinence method for `spawn().wait().check()`. /// Returns an error if something went wrong during the execution or if the /// process exited with failure. - pub fn check_run(&mut self) -> Result<(), Error> { + pub fn check_run(&mut self) -> Result<()> { self.spawn()?.wait()?.check() } } @@ -189,12 +189,9 @@ pub enum ExecutorChild { impl ExecutorChild { /// See `std::process::Child::wait` - pub fn wait(&mut self) -> Result { + pub fn wait(&mut self) -> Result { let result = match self { - ExecutorChild::Wet(c) => c - .wait() - .context(ErrorKind::ProcessExecution) - .map(ExecutorExitStatus::Wet)?, + ExecutorChild::Wet(c) => c.wait().map(ExecutorExitStatus::Wet)?, ExecutorChild::Dry => ExecutorExitStatus::Dry, }; @@ -209,7 +206,7 @@ pub enum ExecutorExitStatus { } impl Check for ExecutorExitStatus { - fn check(self) -> Result<(), Error> { + fn check(self) -> Result<()> { match self { ExecutorExitStatus::Wet(e) => e.check(), ExecutorExitStatus::Dry => Ok(()), @@ -220,17 +217,17 @@ impl Check for ExecutorExitStatus { /// Extension methods for `std::process::Command` pub trait CommandExt { /// Run the command, wait for it to complete, check the return code and decode the output as UTF-8. - fn check_output(&mut self) -> Result; + fn check_output(&mut self) -> Result; } impl CommandExt for Command { - fn check_output(&mut self) -> Result { - let output = self.output().context(ErrorKind::ProcessExecution)?; + fn check_output(&mut self) -> Result { + let output = self.output()?; trace!("Output of {:?}: {:?}", self, output); let status = output.status; if !status.success() { - return Err(ErrorKind::ProcessFailed(status).into()); + return Err(TopgradeError::ProcessFailed(status).into()); } - Ok(String::from_utf8(output.stdout).context(ErrorKind::ProcessExecution)?) + Ok(String::from_utf8(output.stdout)?) } } diff --git a/src/main.rs b/src/main.rs index 472f1e21..312976e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,13 @@ mod terminal; mod utils; use self::config::{CommandLineArgs, Config, Step}; -use self::error::{Error, ErrorKind}; +#[cfg(all(windows, feature = "self-update"))] +use self::error::Upgraded; +use self::error::{SkipStep, StepFailed}; use self::report::Report; use self::steps::*; use self::terminal::*; -use failure::{Fail, ResultExt}; +use anyhow::{anyhow, Result}; use log::debug; #[cfg(feature = "self-update")] use openssl_probe; @@ -26,9 +28,9 @@ use std::io; use std::process::exit; use structopt::StructOpt; -fn execute<'a, F, M>(report: &mut Report<'a>, key: M, func: F, no_retry: bool) -> Result<(), Error> +fn execute<'a, F, M>(report: &mut Report<'a>, key: M, func: F, no_retry: bool) -> Result<()> where - F: Fn() -> Result<(), Error>, + F: Fn() -> Result<()>, M: Into> + Debug, { debug!("Step {:?}", key); @@ -39,7 +41,7 @@ where report.push_result(Some((key, true))); break; } - Err(ref e) if e.kind() == ErrorKind::SkipStep => { + Err(e) if e.downcast_ref::().is_some() => { break; } Err(_) => { @@ -49,7 +51,7 @@ where } let should_ask = interrupted || !no_retry; - let should_retry = should_ask && should_retry(interrupted).context(ErrorKind::Retry)?; + let should_retry = should_ask && should_retry(interrupted)?; if !should_retry { report.push_result(Some((key, false))); @@ -62,10 +64,10 @@ where Ok(()) } -fn run() -> Result<(), Error> { +fn run() -> Result<()> { ctrlc::set_handler(); - let base_dirs = directories::BaseDirs::new().ok_or(ErrorKind::NoBaseDirectories)?; + let base_dirs = directories::BaseDirs::new().ok_or_else(|| anyhow!("No base directories"))?; let opt = CommandLineArgs::from_args(); if opt.edit_config() { @@ -98,29 +100,21 @@ fn run() -> Result<(), Error> { if !run_type.dry() && env::var("TOPGRADE_NO_SELF_UPGRADE").is_err() { let result = self_update::self_update(); - #[cfg(windows)] - { - let upgraded = match &result { - Ok(()) => false, - Err(e) => e.upgraded(), - }; - if upgraded { - return result; + if let Err(e) = &result { + #[cfg(windows)] + { + if e.downcast_ref::().is_some() { + return result; + } } - } - - if let Err(e) = result { print_warning(format!("Self update error: {}", e)); - if let Some(cause) = e.cause() { - print_warning(format!("Caused by: {}", cause)); - } } } } if let Some(commands) = config.pre_commands() { for (name, command) in commands { - generic::run_custom_command(&name, &command, run_type).context(ErrorKind::PreCommand)?; + generic::run_custom_command(&name, &command, run_type)?; } } @@ -661,7 +655,7 @@ fn run() -> Result<(), Error> { if report.data().iter().all(|(_, succeeded)| *succeeded) { Ok(()) } else { - Err(ErrorKind::StepFailed.into()) + Err(StepFailed.into()) } } @@ -673,26 +667,19 @@ fn main() { Err(error) => { #[cfg(all(windows, feature = "self-update"))] { - if let ErrorKind::Upgraded(status) = error.kind() { + if let Some(Upgraded(status)) = error.downcast_ref::() { exit(status.code().unwrap()); } } - let should_print = match error.kind() { - ErrorKind::StepFailed => false, - ErrorKind::Retry => error - .cause() - .and_then(|cause| cause.downcast_ref::()) + let skip_print = (error.downcast_ref::().is_some()) + || (error + .downcast_ref::() .filter(|io_error| io_error.kind() == io::ErrorKind::Interrupted) - .is_none(), - _ => true, - }; + .is_some()); - if should_print { + if !skip_print { println!("Error: {}", error); - if let Some(cause) = error.cause() { - println!("Caused by: {}", cause); - } } exit(1); } diff --git a/src/self_update.rs b/src/self_update.rs index 0eb48b96..b606a167 100644 --- a/src/self_update.rs +++ b/src/self_update.rs @@ -1,6 +1,7 @@ -use super::error::{Error, ErrorKind}; use super::terminal::*; -use failure::ResultExt; +#[cfg(windows)] +use crate::error::Upgraded; +use anyhow::{bail, Result}; use self_update_crate; use self_update_crate::backends::github::{GitHubUpdateStatus, Update}; use std::env; @@ -8,7 +9,7 @@ use std::env; use std::os::unix::process::CommandExt; use std::process::Command; -pub fn self_update() -> Result<(), Error> { +pub fn self_update() -> Result<()> { print_separator("Self update"); let current_exe = env::current_exe(); @@ -23,8 +24,7 @@ pub fn self_update() -> Result<(), Error> { .current_version(self_update_crate::cargo_crate_version!()) .no_confirm(true) .build() - .and_then(Update::update_extended) - .context(ErrorKind::SelfUpdate)?; + .and_then(Update::update_extended)?; if let GitHubUpdateStatus::Updated(release) = &result { println!("\nTopgrade upgraded to {}:\n", release.version()); @@ -36,22 +36,19 @@ pub fn self_update() -> Result<(), Error> { { if result.updated() { print_warning("Respawning..."); - let mut command = Command::new(current_exe.context(ErrorKind::SelfUpdate)?); + let mut command = Command::new(current_exe?); command.args(env::args().skip(1)).env("TOPGRADE_NO_SELF_UPGRADE", ""); #[cfg(unix)] { let err = command.exec(); - Err(err).context(ErrorKind::SelfUpdate)? + bail!(err); } #[cfg(windows)] { - let status = command - .spawn() - .and_then(|mut c| c.wait()) - .context(ErrorKind::SelfUpdate)?; - return Err(ErrorKind::Upgraded(status).into()); + let status = command.spawn().and_then(|mut c| c.wait())?; + bail!(Upgraded(status)); } } } diff --git a/src/steps/emacs.rs b/src/steps/emacs.rs index 65ae2cce..2ef09f56 100644 --- a/src/steps/emacs.rs +++ b/src/steps/emacs.rs @@ -1,7 +1,7 @@ -use crate::error::Error; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::{require, require_option, PathExt}; +use anyhow::Result; use directories::BaseDirs; #[cfg(windows)] use std::env; @@ -35,7 +35,7 @@ impl Emacs { self.directory.as_ref() } - pub fn upgrade(&self, run_type: RunType) -> Result<(), Error> { + pub fn upgrade(&self, run_type: RunType) -> Result<()> { let emacs = require("emacs")?; let init_file = require_option(self.directory.as_ref())?.join("init.el").require()?; diff --git a/src/steps/generic.rs b/src/steps/generic.rs index 06cb9c2f..d37da8ad 100644 --- a/src/steps/generic.rs +++ b/src/steps/generic.rs @@ -1,14 +1,14 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::SkipStep; use crate::executor::{CommandExt, RunType}; use crate::terminal::{print_separator, shell}; use crate::utils::{self, PathExt}; +use anyhow::Result; use directories::BaseDirs; -use failure::ResultExt; use std::env; use std::path::PathBuf; use std::process::Command; -pub fn run_cargo_update(run_type: RunType) -> Result<(), Error> { +pub fn run_cargo_update(run_type: RunType) -> Result<()> { let cargo_update = utils::require("cargo-install-update")?; print_separator("Cargo"); @@ -19,14 +19,14 @@ pub fn run_cargo_update(run_type: RunType) -> Result<(), Error> { .check_run() } -pub fn run_flutter_upgrade(run_type: RunType) -> Result<(), Error> { +pub fn run_flutter_upgrade(run_type: RunType) -> Result<()> { let flutter = utils::require("flutter")?; print_separator("Flutter"); run_type.execute(&flutter).arg("upgrade").check_run() } -pub fn run_go(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_go(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let go = utils::require("go")?; env::var("GOPATH") .unwrap_or_else(|_| base_dirs.home_dir().join("go").to_str().unwrap().to_string()) @@ -36,7 +36,7 @@ pub fn run_go(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { run_type.execute(&go).arg("get").arg("-u").arg("all").check_run() } -pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let gem = utils::require("gem")?; base_dirs.home_dir().join(".gem").require()?; @@ -51,7 +51,7 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { target_os = "netbsd", target_os = "dragonfly" )))] -pub fn run_apm(run_type: RunType) -> Result<(), Error> { +pub fn run_apm(run_type: RunType) -> Result<()> { let apm = utils::require("apm")?; print_separator("Atom Package Manager"); @@ -59,23 +59,19 @@ pub fn run_apm(run_type: RunType) -> Result<(), Error> { run_type.execute(&apm).args(&["upgrade", "--confirm=false"]).check_run() } -pub fn run_rustup(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_rustup(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let rustup = utils::require("rustup")?; print_separator("rustup"); - if rustup - .canonicalize() - .context(ErrorKind::StepFailed)? - .is_descendant_of(base_dirs.home_dir()) - { + if rustup.canonicalize()?.is_descendant_of(base_dirs.home_dir()) { run_type.execute(&rustup).args(&["self", "update"]).check_run()?; } run_type.execute(&rustup).arg("update").check_run() } -pub fn run_jetpack(run_type: RunType) -> Result<(), Error> { +pub fn run_jetpack(run_type: RunType) -> Result<()> { let jetpack = utils::require("jetpack")?; print_separator("Jetpack"); @@ -83,7 +79,7 @@ pub fn run_jetpack(run_type: RunType) -> Result<(), Error> { run_type.execute(&jetpack).args(&["global", "update"]).check_run() } -pub fn run_opam_update(run_type: RunType) -> Result<(), Error> { +pub fn run_opam_update(run_type: RunType) -> Result<()> { let opam = utils::require("opam")?; print_separator("OCaml Package Manager"); @@ -92,28 +88,28 @@ pub fn run_opam_update(run_type: RunType) -> Result<(), Error> { run_type.execute(&opam).arg("upgrade").check_run() } -pub fn run_vcpkg_update(run_type: RunType) -> Result<(), Error> { +pub fn run_vcpkg_update(run_type: RunType) -> Result<()> { let vcpkg = utils::require("vcpkg")?; print_separator("vcpkg"); run_type.execute(&vcpkg).args(&["upgrade", "--no-dry-run"]).check_run() } -pub fn run_pipx_update(run_type: RunType) -> Result<(), Error> { +pub fn run_pipx_update(run_type: RunType) -> Result<()> { let pipx = utils::require("pipx")?; print_separator("pipx"); run_type.execute(&pipx).arg("upgrade-all").check_run() } -pub fn run_stack_update(run_type: RunType) -> Result<(), Error> { +pub fn run_stack_update(run_type: RunType) -> Result<()> { let stack = utils::require("stack")?; print_separator("stack"); run_type.execute(&stack).arg("upgrade").check_run() } -pub fn run_myrepos_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_myrepos_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let myrepos = utils::require("mr")?; base_dirs.home_dir().join(".mrconfig").require()?; @@ -133,22 +129,22 @@ pub fn run_myrepos_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), .check_run() } -pub fn run_custom_command(name: &str, command: &str, run_type: RunType) -> Result<(), Error> { +pub fn run_custom_command(name: &str, command: &str, run_type: RunType) -> Result<()> { print_separator(name); run_type.execute(shell()).arg("-c").arg(command).check_run() } -pub fn run_composer_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_composer_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let composer = utils::require("composer")?; let composer_home = Command::new(&composer) .args(&["global", "config", "--absolute", "--quiet", "home"]) .check_output() - .map_err(|_| Error::from(ErrorKind::SkipStep)) - .map(|s| PathBuf::from(s.trim())) - .and_then(PathExt::require)?; + .map_err(|_| (SkipStep)) + .map(|s| PathBuf::from(s.trim()))? + .require()?; if !composer_home.is_descendant_of(base_dirs.home_dir()) { - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } print_separator("Composer"); @@ -168,14 +164,14 @@ pub fn run_remote_topgrade( ssh_arguments: &Option, run_in_tmux: bool, _tmux_arguments: &Option, -) -> Result<(), Error> { +) -> Result<()> { let ssh = utils::require("ssh")?; if run_in_tmux && !run_type.dry() { #[cfg(unix)] { crate::tmux::run_remote_topgrade(hostname, &ssh, _tmux_arguments)?; - Err(ErrorKind::SkipStep.into()) + Err(SkipStep.into()) } #[cfg(not(unix))] diff --git a/src/steps/git.rs b/src/steps/git.rs index 08f364fc..56f6df0d 100644 --- a/src/steps/git.rs +++ b/src/steps/git.rs @@ -1,7 +1,8 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::TopgradeError; use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; use crate::utils::{which, HumanizedPath}; +use anyhow::Result; use console::style; use futures::future::{join_all, Future}; use glob::{glob_with, MatchOptions}; @@ -79,7 +80,7 @@ impl Git { repositories: &Repositories, run_type: RunType, extra_arguments: &Option, - ) -> Result<(), Error> { + ) -> Result<()> { if repositories.repositories.is_empty() { return Ok(()); } @@ -143,7 +144,7 @@ impl Git { println!("{} {}", style("Up-to-date").green().bold(), path); } } - Ok(true) as Result + Ok(true) as Result } else { println!("{} pulling {}", style("Failed").red().bold(), path); if let Ok(text) = std::str::from_utf8(&output.stderr) { @@ -163,7 +164,7 @@ impl Git { let mut runtime = Runtime::new().unwrap(); let results: Vec = runtime.block_on(join_all(futures))?; if results.into_iter().any(|success| !success) { - Err(ErrorKind::StepFailed.into()) + Err(TopgradeError::PullFailed.into()) } else { Ok(()) } diff --git a/src/steps/node.rs b/src/steps/node.rs index bb5adaf6..37ff301a 100644 --- a/src/steps/node.rs +++ b/src/steps/node.rs @@ -1,7 +1,9 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::SkipStep; use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; use crate::utils::{require, PathExt}; +use anyhow::Result; + use directories::BaseDirs; use std::path::PathBuf; use std::process::Command; @@ -15,32 +17,32 @@ impl NPM { Self { command } } - fn root(&self) -> Result { + fn root(&self) -> Result { Command::new(&self.command) .args(&["root", "-g"]) .check_output() .map(PathBuf::from) } - fn upgrade(&self, run_type: RunType) -> Result<(), Error> { + fn upgrade(&self, run_type: RunType) -> Result<()> { run_type.execute(&self.command).args(&["update", "-g"]).check_run()?; Ok(()) } } -pub fn run_npm_upgrade(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_npm_upgrade(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let npm = require("npm").map(NPM::new)?; let npm_root = npm.root()?; if !npm_root.is_descendant_of(base_dirs.home_dir()) { - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } print_separator("Node Package Manager"); npm.upgrade(run_type) } -pub fn yarn_global_update(run_type: RunType) -> Result<(), Error> { +pub fn yarn_global_update(run_type: RunType) -> Result<()> { let yarn = require("yarn")?; print_separator("Yarn"); diff --git a/src/steps/os/dragonfly.rs b/src/steps/os/dragonfly.rs index c09910ef..9bd625fc 100644 --- a/src/steps/os/dragonfly.rs +++ b/src/steps/os/dragonfly.rs @@ -1,4 +1,4 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::TopgradeError; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::require_option; @@ -6,7 +6,7 @@ use failure::ResultExt; use std::path::PathBuf; use std::process::Command; -pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; print_separator("DrgaonFly BSD Packages"); run_type @@ -15,7 +15,7 @@ pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), .check_run() } -pub fn audit_packages(sudo: &Option) -> Result<(), Error> { +pub fn audit_packages(sudo: &Option) -> Result<()> { if let Some(sudo) = sudo { println!(); Command::new(sudo) diff --git a/src/steps/os/freebsd.rs b/src/steps/os/freebsd.rs index 1aed91b6..8b0d9c49 100644 --- a/src/steps/os/freebsd.rs +++ b/src/steps/os/freebsd.rs @@ -1,4 +1,4 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::TopgradeError; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::require_option; @@ -6,7 +6,7 @@ use failure::ResultExt; use std::path::PathBuf; use std::process::Command; -pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; print_separator("FreeBSD Update"); run_type @@ -15,13 +15,13 @@ pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), .check_run() } -pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; print_separator("FreeBSD Packages"); run_type.execute(sudo).args(&["/usr/sbin/pkg", "upgrade"]).check_run() } -pub fn audit_packages(sudo: &Option) -> Result<(), Error> { +pub fn audit_packages(sudo: &Option) -> Result<()> { if let Some(sudo) = sudo { println!(); Command::new(sudo) diff --git a/src/steps/os/linux.rs b/src/steps/os/linux.rs index bb440b20..a1a85b18 100644 --- a/src/steps/os/linux.rs +++ b/src/steps/os/linux.rs @@ -1,9 +1,9 @@ use crate::config::Config; -use crate::error::{Error, ErrorKind}; +use crate::error::{SkipStep, TopgradeError}; use crate::executor::{ExecutorExitStatus, RunType}; use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, require_option, which, PathExt}; -use failure::ResultExt; +use anyhow::Result; use ini::Ini; use log::debug; use serde::Deserialize; @@ -37,7 +37,7 @@ pub enum Distribution { } impl Distribution { - fn parse_os_release(os_release: &ini::Ini) -> Result { + fn parse_os_release(os_release: &ini::Ini) -> Result { let section = os_release.general_section(); let id = section.get("ID").map(String::as_str); let id_like: Option> = section @@ -65,22 +65,22 @@ impl Distribution { Some("gentoo") => Distribution::Gentoo, Some("exherbo") => Distribution::Exherbo, Some("nixos") => Distribution::NixOS, - _ => return Err(ErrorKind::UnknownLinuxDistribution.into()), + _ => return Err(TopgradeError::UnknownLinuxDistribution.into()), }) } - pub fn detect() -> Result { + pub fn detect() -> Result { if PathBuf::from(OS_RELEASE_PATH).exists() { - let os_release = Ini::load_from_file(OS_RELEASE_PATH).context(ErrorKind::UnknownLinuxDistribution)?; + let os_release = Ini::load_from_file(OS_RELEASE_PATH)?; return Self::parse_os_release(&os_release); } - Err(ErrorKind::UnknownLinuxDistribution.into()) + Err(TopgradeError::UnknownLinuxDistribution.into()) } #[must_use] - pub fn upgrade(self, sudo: &Option, run_type: RunType, config: &Config) -> Result<(), Error> { + pub fn upgrade(self, sudo: &Option, run_type: RunType, config: &Config) -> Result<()> { print_separator("System update"); let yes = config.yes(); @@ -134,7 +134,7 @@ fn upgrade_arch_linux( run_type: RunType, yes: bool, yay_arguments: &str, -) -> Result<(), Error> { +) -> Result<()> { let pacman = which("powerpill").unwrap_or_else(|| PathBuf::from("/usr/bin/pacman")); let path = { @@ -197,7 +197,7 @@ fn upgrade_arch_linux( Ok(()) } -fn upgrade_redhat(sudo: &Option, run_type: RunType, yes: bool) -> Result<(), Error> { +fn upgrade_redhat(sudo: &Option, run_type: RunType, yes: bool) -> Result<()> { if let Some(sudo) = &sudo { let mut command = run_type.execute(&sudo); command @@ -219,7 +219,7 @@ fn upgrade_redhat(sudo: &Option, run_type: RunType, yes: bool) -> Resul Ok(()) } -fn upgrade_suse(sudo: &Option, run_type: RunType) -> Result<(), Error> { +fn upgrade_suse(sudo: &Option, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { run_type .execute(&sudo) @@ -237,7 +237,7 @@ fn upgrade_suse(sudo: &Option, run_type: RunType) -> Result<(), Error> Ok(()) } -fn upgrade_void(sudo: &Option, run_type: RunType) -> Result<(), Error> { +fn upgrade_void(sudo: &Option, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { for _ in 0..2 { run_type @@ -252,7 +252,7 @@ fn upgrade_void(sudo: &Option, run_type: RunType) -> Result<(), Error> Ok(()) } -fn upgrade_gentoo(sudo: &Option, run_type: RunType) -> Result<(), Error> { +fn upgrade_gentoo(sudo: &Option, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { if let Some(layman) = which("layman") { run_type.execute(&sudo).arg(layman).args(&["-s", "ALL"]).check_run()?; @@ -281,7 +281,7 @@ fn upgrade_gentoo(sudo: &Option, run_type: RunType) -> Result<(), Error Ok(()) } -fn upgrade_debian(sudo: &Option, cleanup: bool, run_type: RunType, yes: bool) -> Result<(), Error> { +fn upgrade_debian(sudo: &Option, cleanup: bool, run_type: RunType, yes: bool) -> Result<()> { if let Some(sudo) = &sudo { let apt = which("apt-fast").unwrap_or_else(|| PathBuf::from("/usr/bin/apt")); run_type.execute(&sudo).arg(&apt).arg("update").check_run()?; @@ -310,7 +310,7 @@ fn upgrade_debian(sudo: &Option, cleanup: bool, run_type: RunType, yes: Ok(()) } -fn upgrade_solus(sudo: &Option, run_type: RunType) -> Result<(), Error> { +fn upgrade_solus(sudo: &Option, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { run_type .execute(&sudo) @@ -323,7 +323,7 @@ fn upgrade_solus(sudo: &Option, run_type: RunType) -> Result<(), Error> Ok(()) } -fn upgrade_clearlinux(sudo: &Option, run_type: RunType) -> Result<(), Error> { +fn upgrade_clearlinux(sudo: &Option, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { run_type .execute(&sudo) @@ -336,7 +336,7 @@ fn upgrade_clearlinux(sudo: &Option, run_type: RunType) -> Result<(), E Ok(()) } -fn upgrade_exherbo(sudo: &Option, cleanup: bool, run_type: RunType) -> Result<(), Error> { +fn upgrade_exherbo(sudo: &Option, cleanup: bool, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { run_type.execute(&sudo).args(&["/usr/bin/cave", "sync"]).check_run()?; @@ -368,7 +368,7 @@ fn upgrade_exherbo(sudo: &Option, cleanup: bool, run_type: RunType) -> Ok(()) } -fn upgrade_nixos(sudo: &Option, cleanup: bool, run_type: RunType) -> Result<(), Error> { +fn upgrade_nixos(sudo: &Option, cleanup: bool, run_type: RunType) -> Result<()> { if let Some(sudo) = &sudo { run_type .execute(&sudo) @@ -388,7 +388,7 @@ fn upgrade_nixos(sudo: &Option, cleanup: bool, run_type: RunType) -> Re Ok(()) } -pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; let needrestart = require("needrestart")?; @@ -400,7 +400,7 @@ pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), } #[must_use] -pub fn run_fwupdmgr(run_type: RunType) -> Result<(), Error> { +pub fn run_fwupdmgr(run_type: RunType) -> Result<()> { let fwupdmgr = require("fwupdmgr")?; print_separator("Firmware upgrades"); @@ -410,7 +410,7 @@ pub fn run_fwupdmgr(run_type: RunType) -> Result<(), Error> { if let ExecutorExitStatus::Wet(e) = exit_status { if !(e.success() || e.code().map(|c| c == 2).unwrap_or(false)) { - return Err(ErrorKind::ProcessFailed(e).into()); + return Err(TopgradeError::ProcessFailed(e).into()); } } @@ -418,7 +418,7 @@ pub fn run_fwupdmgr(run_type: RunType) -> Result<(), Error> { } #[must_use] -pub fn flatpak_update(run_type: RunType) -> Result<(), Error> { +pub fn flatpak_update(run_type: RunType) -> Result<()> { let flatpak = require("flatpak")?; print_separator("Flatpak User Packages"); @@ -433,12 +433,12 @@ pub fn flatpak_update(run_type: RunType) -> Result<(), Error> { } #[must_use] -pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; let snap = require("snap")?; if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() { - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } print_separator("snap"); @@ -446,7 +446,7 @@ pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> } #[must_use] -pub fn run_rpi_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn run_rpi_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; let rpi_update = require("rpi-update")?; @@ -456,7 +456,7 @@ pub fn run_rpi_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), E } #[must_use] -pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; let pihole = require("pihole")?; @@ -466,7 +466,7 @@ pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<() } #[must_use] -pub fn run_etc_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<(), Error> { +pub fn run_etc_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo)?; let etc_update = require("etc-update")?; print_separator("etc-update"); diff --git a/src/steps/os/macos.rs b/src/steps/os/macos.rs index 150e3c2d..8a3ae933 100644 --- a/src/steps/os/macos.rs +++ b/src/steps/os/macos.rs @@ -1,9 +1,9 @@ -use crate::error::Error; use crate::executor::RunType; use crate::terminal::print_separator; +use anyhow::Result; #[must_use] -pub fn upgrade_macos(run_type: RunType) -> Result<(), Error> { +pub fn upgrade_macos(run_type: RunType) -> Result<()> { print_separator("App Store"); run_type diff --git a/src/steps/os/unix.rs b/src/steps/os/unix.rs index 85744b21..9a3d45bb 100644 --- a/src/steps/os/unix.rs +++ b/src/steps/os/unix.rs @@ -1,15 +1,15 @@ -use crate::error::Error; #[cfg(target_os = "linux")] -use crate::error::ErrorKind; +use crate::error::SkipStep; use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; use crate::utils::{require, PathExt}; +use anyhow::Result; use directories::BaseDirs; use std::env; use std::path::{Path, PathBuf}; use std::process::Command; -pub fn run_fisher(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_fisher(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let fish = require("fish")?; base_dirs .home_dir() @@ -26,7 +26,7 @@ pub fn run_fisher(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> } #[must_use] -pub fn run_homebrew(cleanup: bool, run_type: RunType) -> Result<(), Error> { +pub fn run_homebrew(cleanup: bool, run_type: RunType) -> Result<()> { let brew = require("brew")?; print_separator("Brew"); @@ -52,7 +52,7 @@ pub fn run_homebrew(cleanup: bool, run_type: RunType) -> Result<(), Error> { } #[must_use] -pub fn run_nix(run_type: RunType) -> Result<(), Error> { +pub fn run_nix(run_type: RunType) -> Result<()> { let nix = require("nix")?; let nix_channel = require("nix-channel")?; let nix_env = require("nix-env")?; @@ -65,7 +65,7 @@ pub fn run_nix(run_type: RunType) -> Result<(), Error> { if let Ok(Distribution::NixOS) = Distribution::detect() { debug!("Nix on NixOS must be upgraded via 'nixos-rebuild switch', skipping."); - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } } @@ -74,21 +74,21 @@ pub fn run_nix(run_type: RunType) -> Result<(), Error> { run_type.execute(&nix_env).arg("--upgrade").check_run() } -pub fn run_home_manager(run_type: RunType) -> Result<(), Error> { +pub fn run_home_manager(run_type: RunType) -> Result<()> { let home_manager = require("home-manager")?; print_separator("home-manager"); run_type.execute(&home_manager).arg("switch").check_run() } -pub fn run_pearl(run_type: RunType) -> Result<(), Error> { +pub fn run_pearl(run_type: RunType) -> Result<()> { let pearl = require("pearl")?; print_separator("pearl"); run_type.execute(&pearl).arg("update").check_run() } -pub fn run_sdkman(base_dirs: &BaseDirs, cleanup: bool, run_type: RunType) -> Result<(), Error> { +pub fn run_sdkman(base_dirs: &BaseDirs, cleanup: bool, run_type: RunType) -> Result<()> { let bash = require("bash")?; let sdkman_init_path = env::var("SDKMAN_DIR") diff --git a/src/steps/os/windows.rs b/src/steps/os/windows.rs index 85606a4b..5fad23a0 100644 --- a/src/steps/os/windows.rs +++ b/src/steps/os/windows.rs @@ -1,17 +1,18 @@ -use crate::error::{Error, ErrorKind}; +use crate::error::SkipStep; use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; use crate::utils::require; +use anyhow::Result; use std::process::Command; -pub fn run_chocolatey(run_type: RunType) -> Result<(), Error> { +pub fn run_chocolatey(run_type: RunType) -> Result<()> { let choco = require("choco")?; print_separator("Chocolatey"); run_type.execute(&choco).args(&["upgrade", "all"]).check_run() } -pub fn run_scoop(run_type: RunType) -> Result<(), Error> { +pub fn run_scoop(run_type: RunType) -> Result<()> { let scoop = require("scoop")?; print_separator("Scoop"); @@ -20,12 +21,12 @@ pub fn run_scoop(run_type: RunType) -> Result<(), Error> { run_type.execute(&scoop).args(&["update", "*"]).check_run() } -pub fn run_wsl_topgrade(run_type: RunType) -> Result<(), Error> { +pub fn run_wsl_topgrade(run_type: RunType) -> Result<()> { let wsl = require("wsl")?; let topgrade = Command::new(&wsl) .args(&["bash", "-l", "which", "topgrade"]) .check_output() - .map_err(|_| ErrorKind::SkipStep)?; + .map_err(|_| SkipStep)?; run_type .execute(&wsl) diff --git a/src/steps/powershell.rs b/src/steps/powershell.rs index 004eb020..13d6e1cb 100644 --- a/src/steps/powershell.rs +++ b/src/steps/powershell.rs @@ -1,9 +1,9 @@ -use crate::error::Error; #[cfg(windows)] -use crate::error::ErrorKind; +use crate::error::SkipStep; use crate::executor::{CommandExt, RunType}; use crate::terminal::{is_dumb, print_separator}; use crate::utils::{require_option, which, PathExt}; +use anyhow::Result; use std::path::PathBuf; use std::process::Command; @@ -53,7 +53,7 @@ impl Powershell { self.profile.as_ref() } - pub fn update_modules(&self, run_type: RunType) -> Result<(), Error> { + pub fn update_modules(&self, run_type: RunType) -> Result<()> { let powershell = require_option(self.path.as_ref())?; print_separator("Powershell Modules Update"); @@ -65,11 +65,11 @@ impl Powershell { } #[cfg(windows)] - pub fn windows_update(&self, run_type: RunType) -> Result<(), Error> { + pub fn windows_update(&self, run_type: RunType) -> Result<()> { let powershell = require_option(self.path.as_ref())?; if !Self::has_module(&powershell, "PSWindowsUpdate") { - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } print_separator("Windows Update"); diff --git a/src/steps/tmux.rs b/src/steps/tmux.rs index 4f4beaff..a5c1ada0 100644 --- a/src/steps/tmux.rs +++ b/src/steps/tmux.rs @@ -1,16 +1,15 @@ -use crate::error::{Error, ErrorKind}; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::{which, Check, PathExt}; +use anyhow::Result; use directories::BaseDirs; -use failure::ResultExt; use std::env; use std::io; use std::os::unix::process::CommandExt; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; -pub fn run_tpm(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_tpm(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let tpm = base_dirs .home_dir() .join(".tmux/plugins/tpm/bin/update_plugins") @@ -62,13 +61,11 @@ impl Tmux { .success()) } - fn run_in_session(&self, command: &str) -> Result<(), Error> { + fn run_in_session(&self, command: &str) -> Result<()> { self.build() .args(&["new-window", "-a", "-t", "topgrade:1", command]) - .spawn() - .context(ErrorKind::ProcessExecution)? - .wait() - .context(ErrorKind::ProcessExecution)? + .spawn()? + .wait()? .check()?; Ok(()) @@ -107,7 +104,7 @@ pub fn run_in_tmux(args: &Option) -> ! { } } -pub fn run_remote_topgrade(hostname: &str, ssh: &Path, tmux_args: &Option) -> Result<(), Error> { +pub fn run_remote_topgrade(hostname: &str, ssh: &Path, tmux_args: &Option) -> Result<()> { let command = format!( "{ssh} -t {hostname} env TOPGRADE_PREFIX={hostname} TOPGRADE_KEEP_END=1 topgrade", ssh = ssh.display(), @@ -117,9 +114,7 @@ pub fn run_remote_topgrade(hostname: &str, ssh: &Path, tmux_args: &Option Result<(), Error> { +) -> Result<()> { let output = run_type .execute(&vim) .args(&["-N", "-u"]) @@ -95,7 +97,7 @@ fn upgrade( if !status.success() { io::stdout().write(&output.stdout).ok(); io::stderr().write(&output.stderr).ok(); - return Err(ErrorKind::ProcessFailed(status).into()); + return Err(TopgradeError::ProcessFailed(status).into()); } else { println!("Plugins upgraded") } @@ -105,12 +107,12 @@ fn upgrade( } #[must_use] -pub fn upgrade_vim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> Result<(), Error> { +pub fn upgrade_vim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> Result<()> { let vim = require("vim")?; let output = Command::new(&vim).arg("--version").check_output()?; if !output.starts_with("VIM") { - return Err(ErrorKind::SkipStep.into()); + return Err(SkipStep.into()); } let vimrc = require_option(vimrc(&base_dirs))?; @@ -121,7 +123,7 @@ pub fn upgrade_vim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> Re } #[must_use] -pub fn upgrade_neovim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> Result<(), Error> { +pub fn upgrade_neovim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> Result<()> { let nvim = require("nvim")?; let nvimrc = require_option(nvimrc(&base_dirs))?; let plugin_framework = require_option(PluginFramework::detect(&nvimrc))?; @@ -130,7 +132,7 @@ pub fn upgrade_neovim(base_dirs: &BaseDirs, run_type: RunType, cleanup: bool) -> upgrade(&nvim, &nvimrc, plugin_framework, run_type, cleanup) } -pub fn run_voom(_base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_voom(_base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let voom = require("voom")?; print_separator("voom"); diff --git a/src/steps/zsh.rs b/src/steps/zsh.rs index c3adb31e..2c513467 100644 --- a/src/steps/zsh.rs +++ b/src/steps/zsh.rs @@ -1,12 +1,12 @@ -use crate::error::Error; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::{require, PathExt}; +use anyhow::Result; use directories::BaseDirs; use std::env; use std::path::{Path, PathBuf}; -pub fn run_zr(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_zr(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; env::var("ZR_HOME") @@ -26,7 +26,7 @@ pub fn zshrc(base_dirs: &BaseDirs) -> PathBuf { .unwrap_or_else(|_| base_dirs.home_dir().join(".zshrc")) } -pub fn run_antigen(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_antigen(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; let zshrc = zshrc(base_dirs).require()?; env::var("ADOTDIR") @@ -40,7 +40,7 @@ pub fn run_antigen(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> run_type.execute(zsh).args(&["-c", cmd.as_str()]).check_run() } -pub fn run_zplug(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_zplug(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; let zshrc = zshrc(base_dirs).require()?; @@ -55,7 +55,7 @@ pub fn run_zplug(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { run_type.execute(zsh).args(&["-c", cmd.as_str()]).check_run() } -pub fn run_zplugin(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_zplugin(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; let zshrc = zshrc(base_dirs).require()?; @@ -73,7 +73,7 @@ pub fn run_zplugin(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> run_type.execute(zsh).args(&["-c", cmd.as_str()]).check_run() } -pub fn run_oh_my_zsh(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { +pub fn run_oh_my_zsh(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; let zshrc = zshrc(base_dirs).require()?; base_dirs.home_dir().join(".oh-my-zsh").require()?; diff --git a/src/utils.rs b/src/utils.rs index cbcbbbbc..da5a2d09 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,6 @@ -use super::error::{Error, ErrorKind}; +use crate::error::{SkipStep, TopgradeError}; +use anyhow::Result; + use log::{debug, error}; use std::env; use std::ffi::OsStr; @@ -8,21 +10,21 @@ use std::process::{ExitStatus, Output}; use which_crate; pub trait Check { - fn check(self) -> Result<(), Error>; + fn check(self) -> Result<()>; } impl Check for ExitStatus { - fn check(self) -> Result<(), Error> { + fn check(self) -> Result<()> { if self.success() { Ok(()) } else { - Err(ErrorKind::ProcessFailed(self).into()) + Err(TopgradeError::ProcessFailed(self).into()) } } } impl Check for Output { - fn check(self) -> Result<(), Error> { + fn check(self) -> Result<()> { self.status.check() } } @@ -35,7 +37,7 @@ where fn is_descendant_of(&self, ancestor: &Path) -> bool; /// Returns the path if it exists or ErrorKind::SkipStep otherwise - fn require(self) -> Result; + fn require(self) -> Result; } impl PathExt for T @@ -54,13 +56,13 @@ where self.as_ref().iter().zip(ancestor.iter()).all(|(a, b)| a == b) } - fn require(self) -> Result { + fn require(self) -> Result { if self.as_ref().exists() { debug!("Path {:?} exists", self.as_ref()); Ok(self) } else { debug!("Path {:?} doesn't exist", self.as_ref()); - Err(ErrorKind::SkipStep.into()) + Err(SkipStep.into()) } } } @@ -178,7 +180,7 @@ mod tests { } } -pub fn require + Debug>(binary_name: T) -> Result { +pub fn require + Debug>(binary_name: T) -> Result { match which_crate::which(&binary_name) { Ok(path) => { debug!("Detected {:?} as {:?}", &path, &binary_name); @@ -187,7 +189,7 @@ pub fn require + Debug>(binary_name: T) -> Result match e.kind() { which_crate::ErrorKind::CannotFindBinaryPath => { debug!("Cannot find {:?}", &binary_name); - Err(ErrorKind::SkipStep.into()) + Err(SkipStep.into()) } _ => { panic!("Detecting {:?} failed: {}", &binary_name, e); @@ -197,10 +199,10 @@ pub fn require + Debug>(binary_name: T) -> Result(option: Option) -> Result { +pub fn require_option(option: Option) -> Result { if let Some(value) = option { Ok(value) } else { - Err(ErrorKind::SkipStep.into()) + Err(SkipStep.into()) } }