use super::{Site, render, row_of};
use crate::registry::roster::ClientRow;
use crate::tool_host::loaded::{self, Entry};
pub fn load(
site: &Site,
rows: &[ClientRow],
client: &str,
names: &[String],
observed: &str,
) -> Result<String, String> {
let row = row_of(rows, client)?;
let mut entries = Vec::new();
for name in names {
let tool = row
.tools
.iter()
.find(|t| &t.name == name)
.ok_or_else(|| format!("client {client:?} advertises no tool {name:?}"))?;
let entry = Entry {
client: client.to_owned(),
tool: tool.clone(),
};
let presented = entry.presented();
if !loaded::callable(&presented) {
return Err(format!("{presented:?} is not a usable tool name"));
}
entries.push(entry);
}
let all = loaded::add(&site.state_root, &site.workspace, &site.agent, &entries)
.map_err(|e| format!("recording the load: {e}"))?;
Ok(render::load(observed, &entries, all.len()))
}
pub fn unload(
site: &Site,
client: &str,
names: Option<&[String]>,
observed: &str,
) -> Result<String, String> {
let held = loaded::read(&site.state_root, &site.workspace, &site.agent);
let gone = resolve(&held, client, names)?;
let kept = loaded::remove(&site.state_root, &site.workspace, &site.agent, &gone)
.map_err(|e| format!("recording the unload: {e}"))?;
Ok(render::unload(observed, &gone, kept.len()))
}
fn resolve(held: &[Entry], client: &str, names: Option<&[String]>) -> Result<Vec<Entry>, String> {
let mine: Vec<Entry> = held
.iter()
.filter(|e| e.client == client)
.cloned()
.collect();
let Some(names) = names else {
if mine.is_empty() {
return Err(format!(
"this conversation has no tool loaded from client {client:?}"
));
}
return Ok(mine);
};
names
.iter()
.map(|name| {
mine.iter()
.find(|e| &e.tool.name == name)
.cloned()
.ok_or_else(|| {
format!("this conversation has no tool {name:?} loaded from client {client:?}")
})
})
.collect()
}