use crate::cognitive::CognitiveState;
use crate::error::PeError;
use std::collections::HashMap;
use std::sync::Arc;
#[async_trait::async_trait]
pub trait InternalTool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn execute(
&self,
state: &CognitiveState,
args: serde_json::Value,
) -> Result<serde_json::Value, PeError>;
}
pub struct InternalToolRegistry {
tools: HashMap<String, Arc<dyn InternalTool>>,
}
impl InternalToolRegistry {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
}
}
pub fn register(&mut self, tool: Arc<dyn InternalTool>) -> &mut Self {
self.tools.insert(tool.name().to_string(), tool);
self
}
pub fn unregister(&mut self, name: &str) -> Option<Arc<dyn InternalTool>> {
self.tools.remove(name)
}
pub fn get(&self, name: &str) -> Option<&Arc<dyn InternalTool>> {
self.tools.get(name)
}
pub fn list(&self) -> Vec<&str> {
self.tools.keys().map(|k| k.as_str()).collect()
}
pub fn len(&self) -> usize {
self.tools.len()
}
pub fn is_empty(&self) -> bool {
self.tools.is_empty()
}
}
impl Default for InternalToolRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct DummyTool {
tool_name: &'static str,
}
#[async_trait::async_trait]
impl InternalTool for DummyTool {
fn name(&self) -> &str {
self.tool_name
}
fn description(&self) -> &str {
"A dummy tool for testing"
}
async fn execute(
&self,
_state: &CognitiveState,
_args: serde_json::Value,
) -> Result<serde_json::Value, PeError> {
Ok(serde_json::json!({"status": "ok"}))
}
}
#[test]
fn test_registry_new_is_empty() {
let registry = InternalToolRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
assert!(registry.list().is_empty());
}
#[test]
fn test_registry_register_and_get() {
let mut registry = InternalToolRegistry::new();
registry.register(Arc::new(DummyTool { tool_name: "note" }));
assert_eq!(registry.len(), 1);
assert!(!registry.is_empty());
let tool = registry.get("note").expect("tool should exist");
assert_eq!(tool.name(), "note");
assert_eq!(tool.description(), "A dummy tool for testing");
}
#[test]
fn test_registry_get_missing_returns_none() {
let registry = InternalToolRegistry::new();
assert!(registry.get("nonexistent").is_none());
}
#[test]
fn test_registry_overwrite_same_name() {
let mut registry = InternalToolRegistry::new();
registry.register(Arc::new(DummyTool { tool_name: "note" }));
registry.register(Arc::new(DummyTool { tool_name: "note" }));
assert_eq!(registry.len(), 1);
}
#[test]
fn test_registry_list_returns_all_names() {
let mut registry = InternalToolRegistry::new();
registry.register(Arc::new(DummyTool { tool_name: "note" }));
registry.register(Arc::new(DummyTool {
tool_name: "read_notes",
}));
registry.register(Arc::new(DummyTool {
tool_name: "signal",
}));
let mut names = registry.list();
names.sort();
assert_eq!(names, vec!["note", "read_notes", "signal"]);
}
#[test]
fn test_registry_chained_register() {
let mut registry = InternalToolRegistry::new();
registry
.register(Arc::new(DummyTool { tool_name: "a" }))
.register(Arc::new(DummyTool { tool_name: "b" }));
assert_eq!(registry.len(), 2);
}
#[tokio::test]
async fn test_tool_execute() {
let tool = DummyTool { tool_name: "test" };
let state = CognitiveState::default();
let result = tool
.execute(&state, serde_json::json!({}))
.await
.expect("execute should succeed");
assert_eq!(result, serde_json::json!({"status": "ok"}));
}
}