use std::sync::Arc;
use async_trait::async_trait;
use lspkit::EngineApi;
use serde_json::Value;
use crate::tools::{ToolDescription, ToolRegistry};
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct ToolInvocation {
pub name: String,
pub output: Value,
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum AdapterError {
#[error("tool not found: {0}")]
UnknownTool(String),
#[error("invalid input for {tool}: {message}")]
InvalidInput {
tool: String,
message: String,
},
#[error("engine error: {0}")]
Engine(String),
}
#[async_trait]
pub trait ToolHandler<E: EngineApi>: Send + Sync + 'static {
async fn invoke(&self, engine: &E, input: Value) -> Result<Value, AdapterError>;
}
#[derive(Clone)]
pub struct Adapter<E: EngineApi> {
engine: Arc<E>,
registry: ToolRegistry,
handlers: Vec<(String, Arc<dyn ToolHandler<E>>)>,
}
impl<E: EngineApi> Adapter<E> {
#[must_use]
pub fn new(engine: Arc<E>) -> Self {
Self {
engine,
registry: ToolRegistry::new(),
handlers: Vec::new(),
}
}
pub fn register(&mut self, description: ToolDescription, handler: Arc<dyn ToolHandler<E>>) {
let name = description.name.clone();
self.registry.register(description);
if let Some(slot) = self.handlers.iter_mut().find(|(n, _)| n == &name) {
slot.1 = handler;
} else {
self.handlers.push((name, handler));
}
}
#[must_use]
pub fn tools(&self) -> &[ToolDescription] {
self.registry.list()
}
pub async fn invoke(&self, name: &str, input: Value) -> Result<ToolInvocation, AdapterError> {
let handler = self
.handlers
.iter()
.find(|(n, _)| n == name)
.map(|(_, h)| h.clone())
.ok_or_else(|| AdapterError::UnknownTool(name.to_owned()))?;
let output = handler.invoke(self.engine.as_ref(), input).await?;
Ok(ToolInvocation {
name: name.to_owned(),
output,
})
}
}
impl<E: EngineApi> std::fmt::Debug for Adapter<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Adapter")
.field("tools", &self.registry.list().len())
.finish_non_exhaustive()
}
}