use std::collections::{HashMap, HashSet};
use crate::core::agents::{AGENTS, Agent, Env, detect_installed_agents, get_agent};
use crate::core::install::{ScannedSkill, move_skill, remove_skill, sanitize_name};
use crate::error::{Result, SkillsError};
pub(crate) fn resolve_target_agents(names: &[String], env: &Env) -> Result<Vec<&'static Agent>> {
if names.iter().any(|a| a == "*") {
return Ok(AGENTS.iter().collect());
}
if !names.is_empty() {
let mut agents = Vec::new();
let mut invalid = Vec::new();
for name in names {
match get_agent(name) {
Some(a) => agents.push(a),
None => invalid.push(name.clone()),
}
}
if !invalid.is_empty() {
return Err(SkillsError::InvalidAgents(invalid.join(", ")));
}
return Ok(agents);
}
Ok(detect_installed_agents(env))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum SelectionOp {
Remove,
Disable,
Enable,
}
fn same_skill(skill: &ScannedSkill, name: &str) -> bool {
sanitize_name(&skill.name) == sanitize_name(name)
}
fn resolve_names(requested: &[String], sources: &[&[ScannedSkill]]) -> Vec<ScannedSkill> {
let mut identity: HashMap<String, ScannedSkill> = HashMap::new();
for source in sources {
for skill in *source {
identity
.entry(sanitize_name(&skill.name))
.or_insert_with(|| skill.clone());
}
}
let mut matched = HashSet::new();
for name in requested {
if let Some(hit) = identity.get(&sanitize_name(name)) {
matched.insert(hit.clone());
}
}
let mut v: Vec<ScannedSkill> = matched.into_iter().collect();
v.sort_by(|a, b| a.name.cmp(&b.name));
v
}
pub(crate) fn apply_selection(
op: SelectionOp,
requested: &[String],
sources: &[&[ScannedSkill]],
target_set: &[ScannedSkill],
env: &Env,
) -> Result<(Vec<String>, Vec<String>, Vec<String>)> {
let selected = resolve_names(requested, sources);
let mut already: Vec<String> = Vec::new();
let mut missing: Vec<String> = Vec::new();
for name in requested {
if selected.iter().any(|s| same_skill(s, name)) {
continue;
}
if target_set.iter().any(|d| same_skill(d, name)) {
already.push(name.clone());
} else {
missing.push(name.clone());
}
}
already.sort();
already.dedup();
missing.sort();
missing.dedup();
let mut applied = Vec::new();
match op {
SelectionOp::Remove => {
for skill in &selected {
if remove_skill(&skill.dir_name, env) {
applied.push(skill.name.clone());
}
}
}
SelectionOp::Disable | SelectionOp::Enable => {
let to_enabled = op == SelectionOp::Enable;
for skill in &selected {
move_skill(&skill.dir_name, to_enabled, env)?;
applied.push(skill.name.clone());
}
}
}
Ok((applied, already, missing))
}