use std::{io::ErrorKind, path::PathBuf};
use super::{module, package::Package, state::InstallState};
pub struct Removed {
pub name: String,
pub file: PathBuf,
pub registry: String,
}
#[track_caller]
pub fn remove(
package: &Package,
components: &[String],
registry: Option<&str>,
) -> Result<Vec<Removed>, String> {
let mut state = InstallState::load(package)?;
let mut targets: Vec<(String, String)> = Vec::new();
for component in components {
let registry_name = resolve_registry(component, registry, &state)?;
targets.push((registry_name, component.clone()));
}
let components_dir = state.components_dir.clone();
module::check(&package.resolve(&components_dir))?;
let mut removed = Vec::new();
for (registry_name, component) in targets {
let registry = state
.registries
.get_mut(®istry_name)
.expect("registry resolved above");
let Some(installed) = registry.components.remove(&component) else {
continue;
};
let file = package.resolve(&installed.file);
match std::fs::remove_file(&file) {
Ok(()) => {}
Err(error) if error.kind() == ErrorKind::NotFound => {}
Err(error) => {
return Err(format!("failed to remove {}: {error}", file.display()));
}
}
if let Some(file_name) = installed.file.file_name().and_then(|name| name.to_str()) {
module::undeclare(&package.resolve(&components_dir), file_name)?;
}
if registry.components.is_empty() {
state.registries.remove(®istry_name);
}
removed.push(Removed {
name: component,
file: installed.file,
registry: registry_name,
});
}
state.save(package)?;
Ok(removed)
}
fn resolve_registry(
component: &str,
registry: Option<&str>,
state: &InstallState,
) -> Result<String, String> {
if let Some(name) = registry {
let registry = state
.registries
.get(name)
.ok_or_else(|| format!("unknown registry `{name}`"))?;
if !registry.components.contains_key(component) {
return Err(format!(
"component `{component}` is not installed from registry `{name}`"
));
}
return Ok(name.to_string());
}
let matches: Vec<&String> = state
.registries
.iter()
.filter(|(_, registry)| registry.components.contains_key(component))
.map(|(name, _)| name)
.collect();
match matches.as_slice() {
[] => Err(format!("component `{component}` is not installed")),
[name] => Ok((*name).clone()),
many => Err(format!(
"component `{component}` is installed from multiple registries ({}); pass --registry to choose",
many.iter()
.map(|name| name.as_str())
.collect::<Vec<_>>()
.join(", ")
)),
}
}