You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

193 lines
6.5 KiB

use crate::alias::create_alias;
use crate::arch::get_safe_arch;
use crate::config::FnmConfig;
use crate::downloader::{install_node_dist, Error as DownloaderError};
use crate::lts::LtsType;
use crate::outln;
use crate::remote_node_index;
use crate::user_version::UserVersion;
use crate::version::Version;
use crate::version_files::get_user_version_for_directory;
use colored::Colorize;
use log::debug;
use thiserror::Error;
#[derive(clap::Parser, Debug, Default)]
pub struct Install {
/// A version string. Can be a partial semver or a LTS version name by the format lts/NAME
pub version: Option<UserVersion>,
/// Install latest LTS
#[clap(long, conflicts_with = "version")]
pub lts: bool,
}
impl Install {
fn version(self) -> Result<Option<UserVersion>, Error> {
match self {
Self {
version: Some(_),
lts: true,
} => Err(Error::TooManyVersionsProvided),
Self {
version: v,
lts: false,
} => Ok(v),
Self {
version: None,
lts: true,
} => Ok(Some(UserVersion::Full(Version::Lts(LtsType::Latest)))),
}
}
}
impl super::command::Command for Install {
type Error = Error;
fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> {
let current_dir = std::env::current_dir().unwrap();
let current_version = self
.version()?
.or_else(|| get_user_version_for_directory(current_dir, config))
.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(_))) => {
return Err(Error::UninstallableVersion { version: v });
}
UserVersion::Full(Version::Lts(lts_type)) => {
let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror)
.map_err(|source| Error::CantListRemoteVersions { source })?;
let picked_version = lts_type
.pick_latest(&available_versions)
.ok_or_else(|| Error::CantFindRelevantLts {
lts_type: lts_type.clone(),
})?
.version
.clone();
debug!(
"Resolved {} into Node version {}",
Version::Lts(lts_type).v_str().cyan(),
picked_version.v_str().cyan()
);
picked_version
}
current_version => {
let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror)
.map_err(|source| Error::CantListRemoteVersions { source })?
.drain(..)
.map(|x| x.version)
.collect();
current_version
.to_version(&available_versions, config)
.ok_or(Error::CantFindNodeVersion {
requested_version: current_version,
})?
.clone()
}
};
// Automatically swap Apple Silicon to x64 arch for appropriate versions.
let safe_arch = get_safe_arch(&config.arch, &version);
let version_str = format!("Node {}", &version);
outln!(
config,
Info,
"Installing {} ({})",
version_str.cyan(),
safe_arch.to_string()
);
match install_node_dist(
&version,
&config.node_dist_mirror,
config.installations_dir(),
safe_arch,
) {
Err(err @ DownloaderError::VersionAlreadyInstalled { .. }) => {
outln!(config, Error, "{} {}", "warning:".bold().yellow(), err);
}
other_err => other_err.map_err(|source| Error::DownloadError { source })?,
};
if let UserVersion::Full(Version::Lts(lts_type)) = current_version {
let alias_name = Version::Lts(lts_type).v_str();
debug!(
"Tagging {} as alias for {}",
alias_name.cyan(),
version.v_str().cyan()
);
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)?;
}
Ok(())
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("Can't download the requested binary: {}", source)]
DownloadError { source: DownloaderError },
#[error(transparent)]
IoError {
#[from]
source: std::io::Error,
},
#[error("Can't find version in dotfiles. Please provide a version manually to the command.")]
CantInferVersion,
#[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 },
#[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,
}
#[cfg(test)]
mod tests {
use super::super::command::Command;
use super::*;
use pretty_assertions::assert_eq;
use std::str::FromStr;
#[test]
fn test_set_default_on_new_installation() {
let base_dir = tempfile::tempdir().unwrap();
let config = FnmConfig::default().with_base_dir(Some(base_dir.path().to_path_buf()));
assert!(!config.default_version_dir().exists());
Install {
version: UserVersion::from_str("12.0.0").ok(),
lts: false,
}
.apply(&config)
.expect("Can't install");
assert!(config.default_version_dir().exists());
assert_eq!(
config.default_version_dir().canonicalize().ok(),
config
.installations_dir()
.join("v12.0.0")
.join("installation")
.canonicalize()
.ok()
);
}
}