use async_trait::async_trait;
use serde_json::Value;
use thiserror::Error;
pub mod bash;
pub mod grep;
pub mod knowledge;
pub mod ls;
pub mod read;
pub mod write;
#[derive(Debug, Error)]
pub enum ToolError {
#[error("Execution failed: {0}")]
ExecutionError(String),
#[error("Invalid arguments: {0}")]
InvalidArguments(String),
}
pub type ToolResult<T> = Result<T, ToolError>;
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn input_schema(&self) -> Value;
async fn execute(&self, args: Value) -> ToolResult<Value>;
}
#[allow(dead_code)]
pub struct MockTool {
name: String,
description: String,
}
impl MockTool {
#[allow(dead_code)]
pub fn new(name: &str, description: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
}
}
}
#[async_trait]
impl Tool for MockTool {
fn name(&self) -> &str {
&self.name
}
fn description(&self) -> &str {
&self.description
}
fn input_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {}
})
}
async fn execute(&self, _args: Value) -> ToolResult<Value> {
Ok(serde_json::json!({"status": "success"}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_tool_metadata() {
let tool = MockTool::new("test_tool", "A test tool");
assert_eq!(tool.name(), "test_tool");
assert_eq!(tool.description(), "A test tool");
}
#[tokio::test]
async fn test_tool_execution() {
let tool = MockTool::new("test_tool", "A test tool");
let result = tool.execute(serde_json::json!({"input": "test"})).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), serde_json::json!({"status": "success"}));
}
#[tokio::test]
async fn test_error_variants() {
let err = ToolError::ExecutionError("failed".to_string());
assert_eq!(err.to_string(), "Execution failed: failed");
let err = ToolError::InvalidArguments("bad args".to_string());
assert_eq!(err.to_string(), "Invalid arguments: bad args");
}
}