use super::command::Command;
use crate::config::FnmConfig;
use crate::fs::remove_symlink_dir;
use crate::installed_versions;
use crate::outln;
use crate::user_version::UserVersion;
use crate::version::Version;
use crate::version_files::get_user_version_from_file;
use colored::Colorize;
use log::debug;
use snafu::{ensure, OptionExt, ResultExt, Snafu};
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
pub struct Uninstall {
version: Option<UserVersion>,
}
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 requested_version = self
.version
.or_else(|| {
let current_dir = std::env::current_dir().unwrap();
get_user_version_from_file(current_dir)
})
.context(CantInferVersion)?;
ensure!(
!matches!(requested_version, UserVersion::Full(Version::Bypassed)),
CantUninstallSystemVersion
);
let available_versions: Vec<&Version> = all_versions
.iter()
.filter(|v| requested_version.matches(v, &config))
.collect();
ensure!(
available_versions.len() < 2,
PleaseBeMoreSpecificToDelete {
matched_versions: available_versions
.iter()
.map(|x| x.v_str())
.collect::<Vec<_>>()
}
);
let version = requested_version
.to_version(&all_versions, &config)
.context(CantFindVersion)?;
let matching_aliases = version.find_aliases(&config).context(IoError)?;
let root_path = version
.root_path(&config)
.with_context(|| RootPathNotFound {
version: version.clone(),
})?;
debug!("Removing Node version from {:?}", root_path);
std::fs::remove_dir_all(root_path).context(CantDeleteNodeVersion)?;
outln!(config#Info, "Node version {} was removed successfuly", version.v_str().cyan());
for alias in matching_aliases {
debug!("Removing alias from {:?}", alias.path());
remove_symlink_dir(alias.path()).context(CantDeleteSymlink)?;
outln!(config#Info, "Alias {} was removed successfuly", alias.name().cyan());
}
Ok(())
}
}
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("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."
))]
CantInferVersion,
#[snafu(display("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")))]
PleaseBeMoreSpecificToDelete { matched_versions: Vec<String> },
#[snafu(display("Can't find a matching version"))]
CantFindVersion,
#[snafu(display("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))]
CantDeleteNodeVersion { source: std::io::Error },
#[snafu(display("Can't delete symlink: {}", source))]
CantDeleteSymlink { source: std::io::Error },
}