use super::metadata::{BinConfig, PackageConfig};
use crate::error::{Context, ErrorKind, Fallible};
use crate::fs::{
dir_entry_match, ok_if_not_found, read_dir_eager, remove_dir_if_exists, remove_file_if_exists,
};
use crate::layout::volta_home;
use crate::shim;
use crate::style::success_prefix;
use crate::sync::VoltaLock;
use log::{info, warn};
pub fn uninstall(name: &str) -> Fallible<()> {
let home = volta_home()?;
let _lock = VoltaLock::acquire();
let package_config_file = home.default_package_config_file(name);
let package_found = match PackageConfig::from_file_if_exists(&package_config_file)? {
None => {
let package_binary_list = binaries_from_package(name)?;
if !package_binary_list.is_empty() {
for bin_name in package_binary_list {
remove_config_and_shim(&bin_name, name)?;
}
true
} else {
false
}
}
Some(package_config) => {
for bin_name in package_config.bins {
remove_config_and_shim(&bin_name, name)?;
}
remove_file_if_exists(package_config_file)?;
true
}
};
remove_shared_link_dir(name)?;
let package_image_dir = home.package_image_dir(name);
remove_dir_if_exists(package_image_dir)?;
if package_found {
info!("{} package '{}' uninstalled", success_prefix(), name);
} else {
warn!("No package '{}' found to uninstall", name);
}
Ok(())
}
fn remove_config_and_shim(bin_name: &str, pkg_name: &str) -> Fallible<()> {
shim::delete(bin_name)?;
let config_file = volta_home()?.default_tool_bin_config(bin_name);
remove_file_if_exists(config_file)?;
info!(
"Removed executable '{}' installed by '{}'",
bin_name, pkg_name
);
Ok(())
}
fn binaries_from_package(package: &str) -> Fallible<Vec<String>> {
let bin_config_dir = volta_home()?.default_bin_dir();
dir_entry_match(bin_config_dir, |entry| {
let path = entry.path();
if let Ok(config) = BinConfig::from_file(path) {
if config.package == package {
return Some(config.name);
}
}
None
})
.or_else(ok_if_not_found)
.with_context(|| ErrorKind::ReadBinConfigDirError {
dir: bin_config_dir.to_owned(),
})
}
fn remove_shared_link_dir(name: &str) -> Fallible<()> {
let mut shared_lib_dir = volta_home()?.shared_lib_dir(name);
remove_dir_if_exists(&shared_lib_dir)?;
if name.starts_with('@') {
shared_lib_dir.pop();
if let Ok(mut entries) = read_dir_eager(&shared_lib_dir) {
if entries.next().is_none() {
remove_dir_if_exists(&shared_lib_dir)?;
}
}
}
Ok(())
}