use kernel::{AxCode, AxError, Tool, ToolCall, ToolOutcome};
use super::{Admitted, ConcurrentInvoke};
pub(super) fn all_at_once(
tools: &dyn ConcurrentInvoke,
calls: &[&ToolCall],
admitted: &[Admitted],
early: &[Option<Result<ToolOutcome, AxError>>],
) -> Vec<Result<ToolOutcome, AxError>> {
let running: Vec<(&ToolCall, Result<&dyn Tool, AxError>)> = calls
.iter()
.zip(admitted)
.zip(early)
.filter_map(|((call, admission), cached)| match (admission, cached) {
(Admitted::Cleared(ticket), None) => Some((*call, tools.tool(ticket))),
(Admitted::Cleared(_), Some(_)) | (Admitted::Answered(_), _) => None,
})
.collect();
let run = |(call, tool): &(&ToolCall, Result<&dyn Tool, AxError>)| match tool {
Ok(tool) => tool.invoke(call),
Err(unheld) => Err(unheld.clone()),
};
let Some((first, rest)) = running.split_first() else {
return Vec::new();
};
std::thread::scope(|scope| {
let others: Vec<_> = rest
.iter()
.map(|job| scope.spawn(move || run(job)))
.collect();
std::iter::once(run(first))
.chain(
others
.into_iter()
.map(|worker| worker.join().unwrap_or_else(|_| Err(lost_answer()))),
)
.collect()
})
}
pub(in crate::turn) fn lost_answer() -> AxError {
AxError::failure(
AxCode::ToolUnavailable,
"run a read-only tool call",
"the tool stopped its thread without an answer",
)
.with_recovery("call the tool again; report it when it stops a second time")
}