use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use anyhow::Result;
pub type ToolHandler = Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value> + Send + Sync>;
#[derive(Debug, Clone)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
}
impl ToolDefinition {
pub fn new(name: &str, description: &str, parameters: serde_json::Value) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
parameters,
}
}
}
#[derive(Default)]
pub struct ToolRegistry {
definitions: Arc<Mutex<HashMap<String, ToolDefinition>>>,
handlers: Arc<Mutex<HashMap<String, SharedHandler>>>,
}
type SharedHandler = Arc<dyn Fn(serde_json::Value) -> Result<serde_json::Value> + Send + Sync>;
impl ToolRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&self, definition: ToolDefinition, handler: ToolHandler) {
let name = definition.name.clone();
self.definitions.lock().unwrap().insert(name.clone(), definition);
self.handlers.lock().unwrap().insert(name, Arc::from(handler));
}
#[allow(dead_code)]
pub fn get_definition(&self, name: &str) -> Option<ToolDefinition> {
self.definitions.lock().unwrap().get(name).cloned()
}
#[allow(dead_code)]
pub fn list_tools(&self) -> Vec<String> {
let defs = self.definitions.lock().unwrap();
let mut names: Vec<String> = defs.keys().cloned().collect();
names.sort();
names
}
pub fn definitions(&self) -> Vec<ToolDefinition> {
let defs = self.definitions.lock().unwrap();
let mut all: Vec<ToolDefinition> = defs.values().cloned().collect();
all.sort_by(|a, b| a.name.cmp(&b.name));
all
}
pub async fn execute_blocking(&self, name: &str, args: serde_json::Value) -> Result<serde_json::Value> {
let Some(handler) = self.handlers.lock().unwrap().get(name).cloned() else {
return Err(anyhow::anyhow!("Tool '{}' not found", name));
};
tokio::task::spawn_blocking(move || handler(args))
.await
.unwrap_or_else(|e| Err(anyhow::anyhow!("tool '{}' panicked: {e}", name)))
}
}