use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
pub struct JsonBinding;
impl JsonBinding {
pub fn params_to_json<T: Serialize>(params: &T) -> Result<String> {
serde_json::to_string_pretty(params).map_err(Into::into)
}
pub fn json_to_params<'de, T: Deserialize<'de>>(json: &'de str) -> Result<T> {
serde_json::from_str(json).map_err(Into::into)
}
pub fn params_to_json_bytes<T: Serialize>(params: &T) -> Result<Vec<u8>> {
serde_json::to_vec(params).map_err(Into::into)
}
pub fn json_bytes_to_params<'de, T: Deserialize<'de>>(json: &'de [u8]) -> Result<T> {
serde_json::from_slice(json).map_err(Into::into)
}
}
pub struct ToolRegistry {
tools: HashMap<String, Arc<dyn Tool>>,
}
impl ToolRegistry {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
}
}
pub fn register(&mut self, name: String, tool: Arc<dyn Tool>) {
self.tools.insert(name, tool);
}
pub fn invoke(&self, name: &str, json: &str) -> Result<String> {
let tool = self
.tools
.get(name)
.ok_or_else(|| anyhow::anyhow!("Tool not found: {}", name))?;
tool.invoke_json(json)
}
pub fn list_tools(&self) -> Vec<&str> {
self.tools.keys().map(|k| k.as_str()).collect()
}
pub fn has_tool(&self, name: &str) -> bool {
self.tools.contains_key(name)
}
}
impl Default for ToolRegistry {
fn default() -> Self {
Self::new()
}
}
pub trait Tool: Send + Sync {
fn invoke_json(&self, json: &str) -> Result<String>;
fn name(&self) -> &str;
}
pub struct FunctionTool<F>
where
F: Fn(&str) -> Result<String> + Send + Sync,
{
name: String,
func: F,
}
impl<F> FunctionTool<F>
where
F: Fn(&str) -> Result<String> + Send + Sync,
{
pub fn new(name: String, func: F) -> Self {
Self { name, func }
}
}
impl<F> Tool for FunctionTool<F>
where
F: Fn(&str) -> Result<String> + Send + Sync,
{
fn invoke_json(&self, json: &str) -> Result<String> {
(self.func)(json)
}
fn name(&self) -> &str {
&self.name
}
}
pub fn typed_tool<I, O, F>(name: &str, f: F) -> Arc<dyn Tool>
where
I: for<'de> Deserialize<'de> + Send + 'static,
O: Serialize + Send + 'static,
F: Fn(I) -> O + Send + Sync + 'static,
{
let name = name.to_string();
Arc::new(FunctionTool::new(name.clone(), move |json| {
let input: I = serde_json::from_str(json)?;
let output = f(input);
Ok(serde_json::to_string(&output)?)
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_binding() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestParams {
message: String,
count: u32,
}
let params = TestParams {
message: "hello".to_string(),
count: 42,
};
let json = JsonBinding::params_to_json(¶ms).unwrap();
let decoded: TestParams = JsonBinding::json_to_params(&json).unwrap();
assert_eq!(params, decoded);
}
#[test]
fn test_tool_registry() {
let tool = typed_tool("echo", |input: String| -> String {
format!("echo: {}", input)
});
let mut registry = ToolRegistry::new();
registry.register("echo".to_string(), tool);
assert!(registry.has_tool("echo"));
assert_eq!(registry.list_tools(), vec!["echo"]);
let result = registry.invoke("echo", r#""hello""#).unwrap();
assert_eq!(result, r#""echo: hello""#);
}
#[test]
fn test_function_tool() {
let tool = FunctionTool::new("double".to_string(), |json: &str| {
let n: i32 = serde_json::from_str(json)?;
Ok(serde_json::to_string(&(n * 2))?)
});
let result = tool.invoke_json("21").unwrap();
assert_eq!(result, "42");
}
}