Browse Source

Replace `snafu` with `thiserror` (#630)

remotes/origin/feat/support-install-latest
Gal Schlezinger 3 years ago committed by GitHub
parent
commit
b24ed660b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      Cargo.lock
  2. 2
      Cargo.toml
  3. 24
      src/choose_version_for_user_input.rs
  4. 19
      src/commands/alias.rs
  5. 10
      src/commands/completions.rs
  6. 13
      src/commands/env.rs
  7. 48
      src/commands/exec.rs
  8. 63
      src/commands/install.rs
  9. 15
      src/commands/ls_local.rs
  10. 12
      src/commands/ls_remote.rs
  11. 13
      src/commands/unalias.rs
  12. 67
      src/commands/uninstall.rs
  13. 59
      src/commands/use.rs
  14. 18
      src/current_version.rs
  15. 59
      src/downloader.rs
  16. 28
      src/installed_versions.rs

38
Cargo.lock generated

@ -371,12 +371,6 @@ dependencies = [ @@ -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 = [ @@ -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" @@ -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 = [ @@ -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",

2
Cargo.toml

@ -22,7 +22,6 @@ colored = "2.0.0" @@ -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" @@ -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"

24
src/choose_version_for_user_input.rs

@ -6,8 +6,8 @@ use crate::user_version::UserVersion; @@ -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>( @@ -29,8 +29,8 @@ pub fn choose_version_for_user_input<'a>(
requested_version: &'a UserVersion,
config: &'a FnmConfig,
) -> Result<Option<ApplicableVersion>, 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>( @@ -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>( @@ -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 },
}

19
src/commands/alias.rs

@ -4,10 +4,9 @@ use crate::choose_version_for_user_input::{ @@ -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 { @@ -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 },
}

10
src/commands/completions.rs

@ -2,9 +2,9 @@ use super::command::Command; @@ -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 { @@ -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,
}

13
src/commands/env.rs

@ -5,9 +5,9 @@ use crate::outln; @@ -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 { @@ -59,7 +59,10 @@ impl Command for Env {
);
}
let shell: Box<dyn Shell> = self.shell.or_else(&infer_shell).context(CantInferShell)?;
let shell: Box<dyn Shell> = 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 { @@ -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,
}

48
src/commands/exec.rs

@ -7,9 +7,9 @@ use crate::outln; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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,
}

63
src/commands/install.rs

@ -10,8 +10,8 @@ use crate::version::Version; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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,
}

15
src/commands/ls_local.rs

@ -3,9 +3,9 @@ use crate::config::FnmConfig; @@ -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 { @@ -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<HashMap<String, @@ -60,12 +61,12 @@ fn generate_aliases_hash(config: &FnmConfig) -> std::io::Result<HashMap<String,
Ok(hashmap)
}
#[derive(Debug, Snafu)]
#[derive(Debug, Error)]
pub enum Error {
#[snafu(display("Can't list locally installed versions: {}", source))]
#[error("Can't list locally installed versions: {}", source)]
CantListLocallyInstalledVersion {
source: crate::installed_versions::Error,
},
#[snafu(display("Can't read aliases: {}", source))]
#[error("Can't read aliases: {}", source)]
CantReadAliases { source: std::io::Error },
}

12
src/commands/ls_remote.rs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
use crate::config::FnmConfig;
use crate::remote_node_index;
use snafu::{ResultExt, Snafu};
use structopt::StructOpt;
use thiserror::Error;
#[derive(StructOpt, Debug)]
pub struct LsRemote {}
@ -10,7 +10,7 @@ impl super::command::Command for LsRemote { @@ -10,7 +10,7 @@ impl super::command::Command for LsRemote {
type Error = Error;
fn apply(self, config: &FnmConfig) -> 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 { @@ -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,
},
}

13
src/commands/unalias.rs

@ -3,8 +3,8 @@ use crate::fs::remove_symlink_dir; @@ -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 { @@ -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 },
}

67
src/commands/uninstall.rs

@ -8,8 +8,8 @@ use crate::version::Version; @@ -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 { @@ -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::<Vec<_>>()
}
);
.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 { @@ -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 { @@ -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::<Vec<_>>().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::<Vec<_>>().join("\n"))]
PleaseBeMoreSpecificToDelete { matched_versions: Vec<String> },
#[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 },
}

59
src/commands/use.rs

@ -10,9 +10,9 @@ use crate::version::Version; @@ -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 { @@ -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 { @@ -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 { @@ -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( @@ -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( @@ -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: <Install as Command>::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 <VERSION>` 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 <VERSION>` to set one,\nor create a .node-version file inside your project to declare a Node.js version.")]
Recursive,
}

18
src/current_version.rs

@ -1,10 +1,11 @@ @@ -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<Option<Version>, 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<Option<Version>, Error> { @@ -19,20 +20,21 @@ pub fn current_version(config: &FnmConfig) -> Result<Option<Version>, 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,

59
src/downloader.rs

@ -4,38 +4,34 @@ use crate::archive::{Error as ExtractError, Extract}; @@ -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<P: AsRef<Path>>( @@ -75,7 +71,7 @@ pub fn extract_archive_into<P: AsRef<Path>>(
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<P: AsRef<Path>>( @@ -88,23 +84,22 @@ pub fn install_node_dist<P: AsRef<Path>>(
) -> 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<P: AsRef<Path>>( @@ -117,17 +112,15 @@ pub fn install_node_dist<P: AsRef<Path>>(
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(())
}

28
src/installed_versions.rs

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
use crate::version::Version;
use snafu::{ResultExt, Snafu};
use std::path::Path;
use thiserror::Error;
pub fn list<P: AsRef<Path>>(installations_dir: P) -> Result<Vec<Version>, 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<P: AsRef<Path>>(installations_dir: P) -> Result<Vec<Version>, Error> @@ -17,19 +17,25 @@ pub fn list<P: AsRef<Path>>(installations_dir: P) -> Result<Vec<Version>, 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,
},
}

Loading…
Cancel
Save