From b24ed660b08c49ccf3fddd8e198a0c0f373baa38 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 10 Jan 2022 17:17:49 +0200 Subject: [PATCH] Replace `snafu` with `thiserror` (#630) --- Cargo.lock | 38 +++------------- Cargo.toml | 2 +- src/choose_version_for_user_input.rs | 24 +++++----- src/commands/alias.rs | 19 ++++---- src/commands/completions.rs | 10 ++--- src/commands/env.rs | 13 +++--- src/commands/exec.rs | 48 ++++++++++---------- src/commands/install.rs | 63 +++++++++++--------------- src/commands/ls_local.rs | 15 ++++--- src/commands/ls_remote.rs | 12 +++-- src/commands/unalias.rs | 13 +++--- src/commands/uninstall.rs | 67 ++++++++++++++-------------- src/commands/use.rs | 59 ++++++++++++------------ src/current_version.rs | 18 ++++---- src/downloader.rs | 59 +++++++++++------------- src/installed_versions.rs | 28 +++++++----- 16 files changed, 226 insertions(+), 262 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0337eb..88c0595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,12 +371,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dtoa" version = "0.4.8" @@ -498,12 +492,12 @@ dependencies = [ "serde_json", "serial_test", "shell-escape", - "snafu", "structopt", "sysinfo", "tar", "tempfile", "test-log", + "thiserror", "url", "xz2", "zip", @@ -1477,28 +1471,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" -[[package]] -name = "snafu" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" -dependencies = [ - "backtrace", - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "socket2" version = "0.4.2" @@ -1637,18 +1609,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d07305f..2ebdf76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ colored = "2.0.0" zip = "0.5.13" tempfile = "3.2.0" indoc = "1.0.3" -snafu = { version = "0.6.10", features = ["backtrace"] } log = "0.4.14" env_logger = "0.9.0" atty = "0.2.14" @@ -30,6 +29,7 @@ encoding_rs_io = "0.1.7" reqwest = { version = "0.11.8", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "brotli"], default-features = false } url = "2.2.2" sysinfo = "0.22.4" +thiserror = "1.0.30" [dev-dependencies] pretty_assertions = "1.0.0" diff --git a/src/choose_version_for_user_input.rs b/src/choose_version_for_user_input.rs index 18d9d3d..9c4de2c 100644 --- a/src/choose_version_for_user_input.rs +++ b/src/choose_version_for_user_input.rs @@ -6,8 +6,8 @@ use crate::user_version::UserVersion; use crate::version::Version; use colored::Colorize; use log::info; -use snafu::{ensure, ResultExt, Snafu}; use std::path::{Path, PathBuf}; +use thiserror::Error; #[derive(Debug)] pub struct ApplicableVersion { @@ -29,8 +29,8 @@ pub fn choose_version_for_user_input<'a>( requested_version: &'a UserVersion, config: &'a FnmConfig, ) -> Result, Error> { - let all_versions = - installed_versions::list(config.installations_dir()).context(VersionListing)?; + let all_versions = installed_versions::list(config.installations_dir()) + .map_err(|source| Error::VersionListing { source })?; let result = if let UserVersion::Full(Version::Bypassed) = requested_version { info!( @@ -54,18 +54,16 @@ pub fn choose_version_for_user_input<'a>( path: alias_path, version: Version::Bypassed, }) - } else { - ensure!( - alias_path.exists(), - CantFindVersion { - requested_version: requested_version.clone() - } - ); + } else if alias_path.exists() { info!("Using Node for alias {}", alias_name.cyan()); Some(ApplicableVersion { path: alias_path, version: Version::Alias(alias_name), }) + } else { + return Err(Error::CantFindVersion { + requested_version: requested_version.clone(), + }); } } else { let current_version = requested_version.to_version(&all_versions, config); @@ -86,10 +84,10 @@ pub fn choose_version_for_user_input<'a>( Ok(result) } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't find requested version: {}", requested_version))] + #[error("Can't find requested version: {}", requested_version)] CantFindVersion { requested_version: UserVersion }, - #[snafu(display("Can't list local installed versions: {}", source))] + #[error("Can't list local installed versions: {}", source)] VersionListing { source: installed_versions::Error }, } diff --git a/src/commands/alias.rs b/src/commands/alias.rs index 8d8ed59..6f6f5cb 100644 --- a/src/commands/alias.rs +++ b/src/commands/alias.rs @@ -4,10 +4,9 @@ use crate::choose_version_for_user_input::{ choose_version_for_user_input, Error as ApplicableVersionError, }; use crate::config::FnmConfig; -use crate::installed_versions; use crate::user_version::UserVersion; -use snafu::{OptionExt, ResultExt, Snafu}; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct Alias { @@ -20,26 +19,24 @@ impl Command for Alias { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { let applicable_version = choose_version_for_user_input(&self.to_version, config) - .context(CantUnderstandVersion)? - .context(VersionNotFound { + .map_err(|source| Error::CantUnderstandVersion { source })? + .ok_or(Error::VersionNotFound { version: self.to_version, })?; create_alias(config, &self.name, applicable_version.version()) - .context(CantCreateSymlink)?; + .map_err(|source| Error::CantCreateSymlink { source })?; Ok(()) } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't create symlink for alias: {}", source))] + #[error("Can't create symlink for alias: {}", source)] CantCreateSymlink { source: std::io::Error }, - #[snafu(display("Can't list local installed versions: {}", source))] - VersionListingError { source: installed_versions::Error }, - #[snafu(display("Version {} not found locally", version))] + #[error("Version {} not found locally", version)] VersionNotFound { version: UserVersion }, - #[snafu(display("{}", source))] + #[error(transparent)] CantUnderstandVersion { source: ApplicableVersionError }, } diff --git a/src/commands/completions.rs b/src/commands/completions.rs index 0c1289f..b63a248 100644 --- a/src/commands/completions.rs +++ b/src/commands/completions.rs @@ -2,9 +2,9 @@ use super::command::Command; use crate::cli::Cli; use crate::config::FnmConfig; use crate::shell::{infer_shell, AVAILABLE_SHELLS}; -use snafu::{OptionExt, Snafu}; use structopt::clap::Shell; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct Completions { @@ -21,21 +21,21 @@ impl Command for Completions { let shell = self .shell .or_else(|| infer_shell().map(Into::into)) - .context(CantInferShell)?; + .ok_or(Error::CantInferShell)?; Cli::clap().gen_completions_to(env!("CARGO_PKG_NAME"), shell, &mut stdio); Ok(()) } } -#[derive(Snafu, Debug)] +#[derive(Error, Debug)] pub enum Error { - #[snafu(display( + #[error( "{}\n{}\n{}\n{}", "Can't infer shell!", "fnm can't infer your shell based on the process tree.", "Maybe it is unsupported? we support the following shells:", shells_as_string() - ))] + )] CantInferShell, } diff --git a/src/commands/env.rs b/src/commands/env.rs index 423243a..06af10c 100644 --- a/src/commands/env.rs +++ b/src/commands/env.rs @@ -5,9 +5,9 @@ use crate::outln; use crate::path_ext::PathExt; use crate::shell::{infer_shell, Shell, AVAILABLE_SHELLS}; use colored::Colorize; -use snafu::{OptionExt, Snafu}; use std::fmt::Debug; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug, Default)] pub struct Env { @@ -59,7 +59,10 @@ impl Command for Env { ); } - let shell: Box = self.shell.or_else(&infer_shell).context(CantInferShell)?; + let shell: Box = self + .shell + .or_else(&infer_shell) + .ok_or(Error::CantInferShell)?; let multishell_path = make_symlink(config); let binary_path = if cfg!(windows) { multishell_path.clone() @@ -104,15 +107,15 @@ impl Command for Env { } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display( + #[error( "{}\n{}\n{}\n{}", "Can't infer shell!", "fnm can't infer your shell based on the process tree.", "Maybe it is unsupported? we support the following shells:", shells_as_string() - ))] + )] CantInferShell, } diff --git a/src/commands/exec.rs b/src/commands/exec.rs index 9f9cf59..dbe2cfd 100644 --- a/src/commands/exec.rs +++ b/src/commands/exec.rs @@ -7,9 +7,9 @@ use crate::outln; use crate::user_version::UserVersion; use crate::user_version_reader::UserVersionReader; use colored::Colorize; -use snafu::{OptionExt, ResultExt, Snafu}; use std::process::{Command, Stdio}; use structopt::StructOpt; +use thiserror::Error; #[derive(Debug, StructOpt)] #[structopt(setting = structopt::clap::AppSettings::TrailingVarArg)] @@ -38,7 +38,10 @@ impl Cmd for Exec { ); } - let (binary, arguments) = self.arguments.split_first().context(NoBinaryProvided)?; + let (binary, arguments) = self + .arguments + .split_first() + .ok_or(Error::NoBinaryProvided)?; let version = self .version @@ -47,11 +50,11 @@ impl Cmd for Exec { UserVersionReader::Path(current_dir) }) .into_user_version(config) - .context(CantInferVersion)?; + .ok_or(Error::CantInferVersion)?; let applicable_version = choose_version_for_user_input(&version, config) - .context(ApplicableVersionError)? - .context(VersionNotFound { version })?; + .map_err(|source| Error::ApplicableVersionError { source })? + .ok_or(Error::VersionNotFound { version })?; #[cfg(windows)] let bin_path = applicable_version.path().to_path_buf(); @@ -60,10 +63,11 @@ impl Cmd for Exec { let bin_path = applicable_version.path().join("bin"); let path_env = { - let paths_env = std::env::var_os("PATH").context(CantReadPathVariable)?; + let paths_env = std::env::var_os("PATH").ok_or(Error::CantReadPathVariable)?; let mut paths: Vec<_> = std::env::split_paths(&paths_env).collect(); paths.insert(0, bin_path); - std::env::join_paths(paths).context(CantAddPathToEnvironment)? + std::env::join_paths(paths) + .map_err(|source| Error::CantAddPathToEnvironment { source })? }; let exit_status = Command::new(&binary) @@ -77,34 +81,28 @@ impl Cmd for Exec { .wait() .expect("Failed to grab exit code"); - let code = exit_status.code().context(CantReadProcessExitCode)?; + let code = exit_status.code().ok_or(Error::CantReadProcessExitCode)?; std::process::exit(code); } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't read path environment variable"))] + #[error("Can't read path environment variable")] CantReadPathVariable, - #[snafu(display("Can't add path to environment variable: {}", source))] - CantAddPathToEnvironment { - source: std::env::JoinPathsError, - }, - #[snafu(display( - "Can't find version in dotfiles. Please provide a version manually to the command." - ))] + #[error("Can't add path to environment variable: {}", source)] + CantAddPathToEnvironment { source: std::env::JoinPathsError }, + #[error("Can't find version in dotfiles. Please provide a version manually to the command.")] CantInferVersion, - #[snafu(display("Requested version {} is not currently installed", version))] - VersionNotFound { - version: UserVersion, - }, + #[error("Requested version {} is not currently installed", version)] + VersionNotFound { version: UserVersion }, + #[error(transparent)] ApplicableVersionError { + #[from] source: UserInputError, }, - #[snafu(display( - "Can't read exit code from process.\nMaybe the process was killed using a signal?" - ))] + #[error("Can't read exit code from process.\nMaybe the process was killed using a signal?")] CantReadProcessExitCode, - #[snafu(display("command not provided. Please provide a command to run as an argument, like {} or {}.\n{} {}", "node".italic(), "bash".italic(), "example:".yellow().bold(), "fnm exec --using=12 node --version".italic().yellow()))] + #[error("command not provided. Please provide a command to run as an argument, like {} or {}.\n{} {}", "node".italic(), "bash".italic(), "example:".yellow().bold(), "fnm exec --using=12 node --version".italic().yellow())] NoBinaryProvided, } diff --git a/src/commands/install.rs b/src/commands/install.rs index 183ab20..a247bd6 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -10,8 +10,8 @@ use crate::version::Version; use crate::version_files::get_user_version_for_directory; use colored::Colorize; use log::debug; -use snafu::{ensure, OptionExt, ResultExt, Snafu}; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug, Default)] pub struct Install { @@ -51,20 +51,19 @@ impl super::command::Command for Install { let current_version = self .version()? .or_else(|| get_user_version_for_directory(current_dir, config)) - .context(CantInferVersion)?; + .ok_or(Error::CantInferVersion)?; let version = match current_version.clone() { UserVersion::Full(Version::Semver(actual_version)) => Version::Semver(actual_version), UserVersion::Full(v @ (Version::Bypassed | Version::Alias(_))) => { - ensure!(false, UninstallableVersion { version: v }); - unreachable!(); + return Err(Error::UninstallableVersion { version: v }); } UserVersion::Full(Version::Lts(lts_type)) => { let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror) - .context(CantListRemoteVersions)?; + .map_err(|source| Error::CantListRemoteVersions { source })?; let picked_version = lts_type .pick_latest(&available_versions) - .with_context(|| CantFindRelevantLts { + .ok_or_else(|| Error::CantFindRelevantLts { lts_type: lts_type.clone(), })? .version @@ -78,14 +77,14 @@ impl super::command::Command for Install { } current_version => { let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror) - .context(CantListRemoteVersions)? + .map_err(|source| Error::CantListRemoteVersions { source })? .drain(..) .map(|x| x.version) .collect(); current_version .to_version(&available_versions, config) - .context(CantFindNodeVersion { + .ok_or(Error::CantFindNodeVersion { requested_version: current_version, })? .clone() @@ -113,7 +112,7 @@ impl super::command::Command for Install { Err(err @ DownloaderError::VersionAlreadyInstalled { .. }) => { outln!(config, Error, "{} {}", "warning:".bold().yellow(), err); } - other_err => other_err.context(DownloadError)?, + other_err => other_err.map_err(|source| Error::DownloadError { source })?, }; if let UserVersion::Full(Version::Lts(lts_type)) = current_version { @@ -123,51 +122,41 @@ impl super::command::Command for Install { alias_name.cyan(), version.v_str().cyan() ); - create_alias(config, &alias_name, &version).context(IoError)?; + create_alias(config, &alias_name, &version)?; } if !config.default_version_dir().exists() { debug!("Tagging {} as the default version", version.v_str().cyan()); - create_alias(config, "default", &version).context(IoError)?; + create_alias(config, "default", &version)?; } Ok(()) } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't download the requested binary: {}", source))] - DownloadError { - source: DownloaderError, - }, + #[error("Can't download the requested binary: {}", source)] + DownloadError { source: DownloaderError }, + #[error(transparent)] IoError { + #[from] source: std::io::Error, }, - #[snafu(display( - "Can't find version in dotfiles. Please provide a version manually to the command." - ))] + #[error("Can't find version in dotfiles. Please provide a version manually to the command.")] CantInferVersion, - #[snafu(display("Having a hard time listing the remote versions: {}", source))] - CantListRemoteVersions { - source: crate::http::Error, - }, - #[snafu(display( + #[error("Having a hard time listing the remote versions: {}", source)] + CantListRemoteVersions { source: crate::http::Error }, + #[error( "Can't find a Node version that matches {} in remote", requested_version - ))] - CantFindNodeVersion { - requested_version: UserVersion, - }, - #[snafu(display("Can't find relevant LTS named {}", lts_type))] - CantFindRelevantLts { - lts_type: crate::lts::LtsType, - }, - #[snafu(display("The requested version is not installable: {}", version.v_str()))] - UninstallableVersion { - version: Version, - }, - #[snafu(display("Too many versions provided. Please don't use --lts with a version string."))] + )] + CantFindNodeVersion { requested_version: UserVersion }, + #[error("Can't find relevant LTS named {}", lts_type)] + CantFindRelevantLts { lts_type: crate::lts::LtsType }, + #[error("The requested version is not installable: {}", version.v_str())] + UninstallableVersion { version: Version }, + #[error("Too many versions provided. Please don't use --lts with a version string.")] TooManyVersionsProvided, } diff --git a/src/commands/ls_local.rs b/src/commands/ls_local.rs index bdbb1d1..b614a7e 100644 --- a/src/commands/ls_local.rs +++ b/src/commands/ls_local.rs @@ -3,9 +3,9 @@ use crate::config::FnmConfig; use crate::current_version::current_version; use crate::version::Version; use colored::Colorize; -use snafu::{ResultExt, Snafu}; use std::collections::HashMap; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct LsLocal {} @@ -15,11 +15,12 @@ impl super::command::Command for LsLocal { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { let base_dir = config.installations_dir(); - let mut versions = - crate::installed_versions::list(base_dir).context(CantListLocallyInstalledVersion)?; + let mut versions = crate::installed_versions::list(base_dir) + .map_err(|source| Error::CantListLocallyInstalledVersion { source })?; versions.insert(0, Version::Bypassed); versions.sort(); - let aliases_hash = generate_aliases_hash(config).context(CantReadAliases)?; + let aliases_hash = + generate_aliases_hash(config).map_err(|source| Error::CantReadAliases { source })?; let curr_version = current_version(config).ok().flatten(); for version in versions { @@ -60,12 +61,12 @@ fn generate_aliases_hash(config: &FnmConfig) -> std::io::Result Result<(), Self::Error> { - let all_versions = remote_node_index::list(&config.node_dist_mirror).context(HttpError)?; + let all_versions = remote_node_index::list(&config.node_dist_mirror)?; for version in all_versions { print!("{}", version.version); @@ -24,7 +24,11 @@ impl super::command::Command for LsRemote { } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - HttpError { source: crate::http::Error }, + #[error(transparent)] + HttpError { + #[from] + source: crate::http::Error, + }, } diff --git a/src/commands/unalias.rs b/src/commands/unalias.rs index 4bedabc..e6bc99e 100644 --- a/src/commands/unalias.rs +++ b/src/commands/unalias.rs @@ -3,8 +3,8 @@ use crate::fs::remove_symlink_dir; use crate::user_version::UserVersion; use crate::version::Version; use crate::{choose_version_for_user_input, config::FnmConfig}; -use snafu::{OptionExt, ResultExt, Snafu}; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct Unalias { @@ -21,20 +21,21 @@ impl Command for Unalias { ) .ok() .flatten() - .with_context(|| AliasNotFound { + .ok_or(Error::AliasNotFound { requested_alias: self.requested_alias, })?; - remove_symlink_dir(&requested_version.path()).context(CantDeleteSymlink)?; + remove_symlink_dir(&requested_version.path()) + .map_err(|source| Error::CantDeleteSymlink { source })?; Ok(()) } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't delete symlink: {}", source))] + #[error("Can't delete symlink: {}", source)] CantDeleteSymlink { source: std::io::Error }, - #[snafu(display("Requested alias {} not found", requested_alias))] + #[error("Requested alias {} not found", requested_alias)] AliasNotFound { requested_alias: String }, } diff --git a/src/commands/uninstall.rs b/src/commands/uninstall.rs index 2152a1b..700e881 100644 --- a/src/commands/uninstall.rs +++ b/src/commands/uninstall.rs @@ -8,8 +8,8 @@ use crate::version::Version; use crate::version_files::get_user_version_for_directory; use colored::Colorize; use log::debug; -use snafu::{ensure, OptionExt, ResultExt, Snafu}; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct Uninstall { @@ -20,49 +20,48 @@ impl Command for Uninstall { type Error = Error; fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { - let all_versions = - installed_versions::list(config.installations_dir()).context(VersionListingError)?; + let all_versions = installed_versions::list(config.installations_dir()) + .map_err(|source| Error::VersionListingError { source })?; let requested_version = self .version .or_else(|| { let current_dir = std::env::current_dir().unwrap(); get_user_version_for_directory(current_dir, config) }) - .context(CantInferVersion)?; + .ok_or(Error::CantInferVersion)?; - ensure!( - !matches!(requested_version, UserVersion::Full(Version::Bypassed)), - CantUninstallSystemVersion - ); + if matches!(requested_version, UserVersion::Full(Version::Bypassed)) { + return Err(Error::CantUninstallSystemVersion); + } let available_versions: Vec<&Version> = all_versions .iter() .filter(|v| requested_version.matches(v, config)) .collect(); - ensure!( - available_versions.len() < 2, - PleaseBeMoreSpecificToDelete { + if available_versions.len() >= 2 { + return Err(Error::PleaseBeMoreSpecificToDelete { matched_versions: available_versions .iter() - .map(|x| x.v_str()) - .collect::>() - } - ); + .map(std::string::ToString::to_string) + .collect(), + }); + } let version = requested_version .to_version(&all_versions, config) - .context(CantFindVersion)?; + .ok_or(Error::CantFindVersion)?; - let matching_aliases = version.find_aliases(config).context(IoError)?; + let matching_aliases = version.find_aliases(config)?; let root_path = version .root_path(config) - .with_context(|| RootPathNotFound { + .ok_or_else(|| Error::RootPathNotFound { version: version.clone(), })?; debug!("Removing Node version from {:?}", root_path); - std::fs::remove_dir_all(root_path).context(CantDeleteNodeVersion)?; + std::fs::remove_dir_all(root_path) + .map_err(|source| Error::CantDeleteNodeVersion { source })?; outln!( config, Info, @@ -72,7 +71,8 @@ impl Command for Uninstall { for alias in matching_aliases { debug!("Removing alias from {:?}", alias.path()); - remove_symlink_dir(alias.path()).context(CantDeleteSymlink)?; + remove_symlink_dir(alias.path()) + .map_err(|source| Error::CantDeleteSymlink { source })?; outln!( config, Info, @@ -85,26 +85,27 @@ impl Command for Uninstall { } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't get locally installed versions: {}", source))] + #[error("Can't get locally installed versions: {}", source)] VersionListingError { source: installed_versions::Error }, - #[snafu(display( - "Can't find version in dotfiles. Please provide a version manually to the command." - ))] + #[error("Can't find version in dotfiles. Please provide a version manually to the command.")] CantInferVersion, - #[snafu(display("Can't uninstall system version"))] + #[error("Can't uninstall system version")] CantUninstallSystemVersion, - #[snafu(display("Too many versions had matched, please be more specific.\nFound {} matching versions, expected 1:\n{}", matched_versions.len(), matched_versions.iter().map(|v| format!("* {}", v)).collect::>().join("\n")))] + #[error("Too many versions had matched, please be more specific.\nFound {} matching versions, expected 1:\n{}", matched_versions.len(), matched_versions.iter().map(|v| format!("* {}", v)).collect::>().join("\n"))] PleaseBeMoreSpecificToDelete { matched_versions: Vec }, - #[snafu(display("Can't find a matching version"))] + #[error("Can't find a matching version")] CantFindVersion, - #[snafu(display("Root path not found for version {}", version))] + #[error("Root path not found for version {}", version)] RootPathNotFound { version: Version }, - #[snafu(display("io error: {}", source))] - IoError { source: std::io::Error }, - #[snafu(display("Can't delete Node.js version: {}", source))] + #[error("io error: {}", source)] + IoError { + #[from] + source: std::io::Error, + }, + #[error("Can't delete Node.js version: {}", source)] CantDeleteNodeVersion { source: std::io::Error }, - #[snafu(display("Can't delete symlink: {}", source))] + #[error("Can't delete symlink: {}", source)] CantDeleteSymlink { source: std::io::Error }, } diff --git a/src/commands/use.rs b/src/commands/use.rs index f66096f..279dbee 100644 --- a/src/commands/use.rs +++ b/src/commands/use.rs @@ -10,9 +10,9 @@ use crate::version::Version; use crate::version_file_strategy::VersionFileStrategy; use crate::{config::FnmConfig, user_version_reader::UserVersionReader}; use colored::Colorize; -use snafu::{ensure, OptionExt, ResultExt, Snafu}; use std::path::Path; use structopt::StructOpt; +use thiserror::Error; #[derive(StructOpt, Debug)] pub struct Use { @@ -31,11 +31,11 @@ impl Command for Use { type Error = Error; fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { - let multishell_path = config.multishell_path().context(FnmEnvWasNotSourced)?; + let multishell_path = config.multishell_path().ok_or(Error::FnmEnvWasNotSourced)?; warn_if_multishell_path_not_in_path_env_var(multishell_path, config); - let all_versions = - installed_versions::list(config.installations_dir()).context(VersionListingError)?; + let all_versions = installed_versions::list(config.installations_dir()) + .map_err(|source| Error::VersionListingError { source })?; let requested_version = self .version .unwrap_or_else(|| { @@ -47,7 +47,7 @@ impl Command for Use { VersionFileStrategy::Local => InferVersionError::Local, VersionFileStrategy::Recursive => InferVersionError::Recursive, }) - .context(CantInferVersion)?; + .map_err(|source| Error::CantInferVersion { source })?; let (message, version_path) = if let UserVersion::Full(Version::Bypassed) = requested_version @@ -101,7 +101,8 @@ impl Command for Use { })?; } - replace_symlink(&version_path, multishell_path).context(SymlinkingCreationIssue)?; + replace_symlink(&version_path, multishell_path) + .map_err(|source| Error::SymlinkingCreationIssue { source })?; Ok(()) } @@ -120,19 +121,18 @@ fn install_new_version( config: &FnmConfig, install_if_missing: bool, ) -> Result<(), Error> { - ensure!( - install_if_missing || should_install_interactively(&requested_version), - CantFindVersion { - version: requested_version - } - ); + if !install_if_missing && !should_install_interactively(&requested_version) { + return Err(Error::CantFindVersion { + version: requested_version, + }); + } Install { version: Some(requested_version.clone()), ..Install::default() } .apply(config) - .context(InstallError)?; + .map_err(|source| Error::InstallError { source })?; Use { version: Some(UserVersionReader::Direct(requested_version)), @@ -207,37 +207,36 @@ fn warn_if_multishell_path_not_in_path_env_var( ); } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("Can't create the symlink: {}", source))] + #[error("Can't create the symlink: {}", source)] SymlinkingCreationIssue { source: std::io::Error }, - #[snafu(display("Can't read the symlink: {}", source))] - SymlinkReadFailed { source: std::io::Error }, - #[snafu(display("{}", source))] + #[error(transparent)] InstallError { source: ::Error }, - #[snafu(display("Can't get locally installed versions: {}", source))] + #[error("Can't get locally installed versions: {}", source)] VersionListingError { source: installed_versions::Error }, - #[snafu(display("Requested version {} is not currently installed", version))] + #[error("Requested version {} is not currently installed", version)] CantFindVersion { version: UserVersion }, - #[snafu(display("{}", source))] - CantInferVersion { source: InferVersionError }, - #[snafu(display( + #[error(transparent)] + CantInferVersion { + #[from] + source: InferVersionError, + }, + #[error( "{}\n{}\n{}", "We can't find the necessary environment variables to replace the Node version.", "You should setup your shell profile to evaluate `fnm env`, see https://github.com/Schniz/fnm#shell-setup on how to do this", "Check out our documentation for more information: https://fnm.vercel.app" - ))] + )] FnmEnvWasNotSourced, - #[snafu(display("Can't create the multishell directory: {}", path.display()))] + #[error("Can't create the multishell directory: {}", path.display())] MultishellDirectoryCreationIssue { path: std::path::PathBuf }, } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum InferVersionError { - #[snafu(display( - "Can't find version in dotfiles. Please provide a version manually to the command." - ))] + #[error("Can't find version in dotfiles. Please provide a version manually to the command.")] Local, - #[snafu(display("Could not find any version to use. Maybe you don't have a default version set?\nTry running `fnm default ` to set one,\nor create a .node-version file inside your project to declare a Node.js version."))] + #[error("Could not find any version to use. Maybe you don't have a default version set?\nTry running `fnm default ` to set one,\nor create a .node-version file inside your project to declare a Node.js version.")] Recursive, } diff --git a/src/current_version.rs b/src/current_version.rs index 6f7fab3..8dbf846 100644 --- a/src/current_version.rs +++ b/src/current_version.rs @@ -1,10 +1,11 @@ +use thiserror::Error; + use crate::config::FnmConfig; use crate::system_version; use crate::version::Version; -use snafu::{OptionExt, ResultExt, Snafu}; pub fn current_version(config: &FnmConfig) -> Result, Error> { - let multishell_path = config.multishell_path().context(EnvNotApplied)?; + let multishell_path = config.multishell_path().ok_or(Error::EnvNotApplied)?; if multishell_path.read_link().ok() == Some(system_version::path()) { return Ok(Some(Version::Bypassed)); @@ -19,20 +20,21 @@ pub fn current_version(config: &FnmConfig) -> Result, Error> { .expect("Can't get filename") .to_str() .expect("Invalid OS string"); - let version = Version::parse(file_name).context(VersionError { version: file_name })?; + let version = Version::parse(file_name).map_err(|source| Error::VersionError { + source, + version: file_name.to_string(), + })?; Ok(Some(version)) } else { Ok(None) } } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display( - "`fnm env` was not applied in this context.\nCan't find fnm's environment variables" - ))] + #[error("`fnm env` was not applied in this context.\nCan't find fnm's environment variables")] EnvNotApplied, - #[snafu(display("Can't read the version as a valid semver"))] + #[error("Can't read the version as a valid semver")] VersionError { source: semver::Error, version: String, diff --git a/src/downloader.rs b/src/downloader.rs index 2a653d3..7ba999b 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -4,38 +4,34 @@ use crate::archive::{Error as ExtractError, Extract}; use crate::directory_portal::DirectoryPortal; use crate::version::Version; use log::debug; -use snafu::{ensure, OptionExt, ResultExt, Snafu}; use std::path::Path; use std::path::PathBuf; +use thiserror::Error; use url::Url; -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { + #[error(transparent)] HttpError { + #[from] source: crate::http::Error, }, + #[error(transparent)] IoError { + #[from] source: std::io::Error, }, - #[snafu(display("Can't extract the file: {}", source))] + #[error("Can't extract the file: {}", source)] CantExtractFile { + #[from] source: ExtractError, }, - #[snafu(display("The downloaded archive is empty"))] + #[error("The downloaded archive is empty")] TarIsEmpty, - #[snafu(display( - "{} for {} not found upstream.\nYou can `fnm ls-remote` to see available versions or try a different `--arch`.", - version, - arch - ))] - VersionNotFound { - version: Version, - arch: Arch, - }, - #[snafu(display("Version already installed at {:?}", path))] - VersionAlreadyInstalled { - path: PathBuf, - }, + #[error("{} for {} not found upstream.\nYou can `fnm ls-remote` to see available versions or try a different `--arch`.", version, arch)] + VersionNotFound { version: Version, arch: Arch }, + #[error("Version already installed at {:?}", path)] + VersionAlreadyInstalled { path: PathBuf }, } #[cfg(unix)] @@ -75,7 +71,7 @@ pub fn extract_archive_into>( let extractor = archive::TarXz::new(response); #[cfg(windows)] let extractor = archive::Zip::new(response); - extractor.extract_into(path).context(CantExtractFile)?; + extractor.extract_into(path)?; Ok(()) } @@ -88,23 +84,22 @@ pub fn install_node_dist>( ) -> Result<(), Error> { let installation_dir = PathBuf::from(installations_dir.as_ref()).join(version.v_str()); - ensure!( - !installation_dir.exists(), - VersionAlreadyInstalled { - path: installation_dir - } - ); + if installation_dir.exists() { + return Err(Error::VersionAlreadyInstalled { + path: installation_dir, + }); + } - std::fs::create_dir_all(installations_dir.as_ref()).context(IoError)?; + std::fs::create_dir_all(installations_dir.as_ref())?; let temp_installations_dir = installations_dir.as_ref().join(".downloads"); - std::fs::create_dir_all(&temp_installations_dir).context(IoError)?; + std::fs::create_dir_all(&temp_installations_dir)?; let portal = DirectoryPortal::new_in(&temp_installations_dir, installation_dir); let url = download_url(node_dist_mirror, version, arch); debug!("Going to call for {}", &url); - let response = crate::http::get(url.as_str()).context(HttpError)?; + let response = crate::http::get(url.as_str())?; if response.status() == 404 { return Err(Error::VersionNotFound { @@ -117,17 +112,15 @@ pub fn install_node_dist>( extract_archive_into(&portal, response)?; debug!("Extraction completed"); - let installed_directory = std::fs::read_dir(&portal) - .context(IoError)? + let installed_directory = std::fs::read_dir(&portal)? .next() - .context(TarIsEmpty)? - .context(IoError)?; + .ok_or(Error::TarIsEmpty)??; let installed_directory = installed_directory.path(); let renamed_installation_dir = portal.join("installation"); - std::fs::rename(installed_directory, renamed_installation_dir).context(IoError)?; + std::fs::rename(installed_directory, renamed_installation_dir)?; - portal.teleport().context(IoError)?; + portal.teleport()?; Ok(()) } diff --git a/src/installed_versions.rs b/src/installed_versions.rs index 4d875c3..bf2a147 100644 --- a/src/installed_versions.rs +++ b/src/installed_versions.rs @@ -1,11 +1,11 @@ use crate::version::Version; -use snafu::{ResultExt, Snafu}; use std::path::Path; +use thiserror::Error; pub fn list>(installations_dir: P) -> Result, Error> { let mut vec = vec![]; - for result_entry in installations_dir.as_ref().read_dir().context(IoError)? { - let entry = result_entry.context(IoError)?; + for result_entry in installations_dir.as_ref().read_dir()? { + let entry = result_entry?; if entry .file_name() .to_str() @@ -17,19 +17,25 @@ pub fn list>(installations_dir: P) -> Result, Error> let path = entry.path(); let filename = path .file_name() - .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound)) - .context(IoError)? + .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))? .to_str() - .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound)) - .context(IoError)?; - let version = Version::parse(filename).context(SemverError)?; + .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?; + let version = Version::parse(filename)?; vec.push(version); } Ok(vec) } -#[derive(Debug, Snafu)] +#[derive(Debug, Error)] pub enum Error { - IoError { source: std::io::Error }, - SemverError { source: semver::Error }, + #[error(transparent)] + IoError { + #[from] + source: std::io::Error, + }, + #[error(transparent)] + SemverError { + #[from] + source: semver::Error, + }, }