use spawningpool::{EntityKind, Referrer, Registry};
use crate::cli::DeleteEntity;
pub(crate) fn delete(entity: DeleteEntity, yes: bool) -> Result<(), String> {
if let DeleteEntity::Tool { name } = &entity {
return delete_tool(name, yes);
}
let mut registry = spawningpool::store::load()?;
let (exists, what, kind, name, referrers) = match &entity {
DeleteEntity::Specialist { name } => (
registry.specialists.contains_key(name),
format!("specialist {name}"),
"specialist",
name.clone(),
Vec::new(),
),
DeleteEntity::Provider { name } => (
registry.providers.contains_key(name),
format!("provider {name}"),
"provider",
name.clone(),
referrers_of_provider(®istry, name),
),
DeleteEntity::Model { name } => (
registry.models.contains_key(name),
format!("model {name}"),
"model",
name.clone(),
referrers_of_model(®istry, name),
),
DeleteEntity::Tool { .. } => unreachable!("handled by delete_tool before load"),
};
if !exists {
return Err(format!("no such {what}"));
}
if !confirm_delete(&what, kind, &name, &referrers, yes)? {
return Ok(());
}
match entity {
DeleteEntity::Specialist { name } => {
registry.specialists.remove(&name);
}
DeleteEntity::Provider { name } => {
registry.providers.remove(&name);
}
DeleteEntity::Model { name } => {
registry.models.remove(&name);
}
DeleteEntity::Tool { .. } => unreachable!("handled by delete_tool before load"),
}
spawningpool::store::save(®istry)?;
println!("deleted {what}");
Ok(())
}
fn delete_tool(name: &str, yes: bool) -> Result<(), String> {
let registry = spawningpool::store::load()?;
let dir = spawningpool::store::tools_dir();
if !spawningpool::tools::exists(&dir, name) {
return Err(format!("no such tool {name}"));
}
let referrers = referrers_of_tool(®istry, name);
if !confirm_delete(&format!("tool {name}"), "tool", name, &referrers, yes)? {
return Ok(());
}
spawningpool::tools::remove(&dir, name)?;
println!("deleted tool {name}");
Ok(())
}
fn format_referrers(referrers: Vec<Referrer>) -> Vec<String> {
referrers
.into_iter()
.map(|r| format!("{} '{}'", r.kind, r.name))
.collect()
}
pub(crate) fn referrers_of_provider(registry: &Registry, name: &str) -> Vec<String> {
format_referrers(registry.referrers(EntityKind::Provider, name))
}
pub(crate) fn referrers_of_model(registry: &Registry, name: &str) -> Vec<String> {
format_referrers(registry.referrers(EntityKind::Model, name))
}
pub(crate) fn referrers_of_tool(registry: &Registry, name: &str) -> Vec<String> {
format_referrers(registry.referrers(EntityKind::Tool, name))
}
fn confirm_delete(
what: &str,
kind: &str,
name: &str,
referrers: &[String],
yes: bool,
) -> Result<bool, String> {
if !referrers.is_empty() {
for referrer in referrers {
eprintln!("warning: deleting {kind} '{name}' will orphan {referrer}");
}
eprintln!(
" Those references will fail at run time until you redefine {kind} '{name}' or repoint them."
);
}
if yes {
return Ok(true);
}
if prompt_yes_no(&format!("delete {what}?"))? {
Ok(true)
} else {
println!("cancelled");
Ok(false)
}
}
fn prompt_yes_no(question: &str) -> Result<bool, String> {
use std::io::Write;
print!("{question} [y/N] ");
std::io::stdout().flush().map_err(|e| e.to_string())?;
let mut answer = String::new();
std::io::stdin()
.read_line(&mut answer)
.map_err(|e| e.to_string())?;
Ok(affirmative(&answer))
}
pub(crate) fn affirmative(answer: &str) -> bool {
matches!(answer.trim(), "y" | "Y" | "yes" | "Yes")
}