1use crate::cognitive::CognitiveState;
14use crate::error::PeError;
15use std::collections::HashMap;
16use std::sync::Arc;
17
18#[async_trait::async_trait]
47pub trait InternalTool: Send + Sync {
48 fn name(&self) -> &str;
50
51 fn description(&self) -> &str;
53
54 async fn execute(
56 &self,
57 state: &CognitiveState,
58 args: serde_json::Value,
59 ) -> Result<serde_json::Value, PeError>;
60}
61
62pub struct InternalToolRegistry {
77 tools: HashMap<String, Arc<dyn InternalTool>>,
78}
79
80impl InternalToolRegistry {
81 pub fn new() -> Self {
83 Self {
84 tools: HashMap::new(),
85 }
86 }
87
88 pub fn register(&mut self, tool: Arc<dyn InternalTool>) -> &mut Self {
90 self.tools.insert(tool.name().to_string(), tool);
91 self
92 }
93
94 pub fn unregister(&mut self, name: &str) -> Option<Arc<dyn InternalTool>> {
96 self.tools.remove(name)
97 }
98
99 pub fn get(&self, name: &str) -> Option<&Arc<dyn InternalTool>> {
101 self.tools.get(name)
102 }
103
104 pub fn list(&self) -> Vec<&str> {
106 self.tools.keys().map(|k| k.as_str()).collect()
107 }
108
109 pub fn len(&self) -> usize {
111 self.tools.len()
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.tools.is_empty()
117 }
118}
119
120impl Default for InternalToolRegistry {
121 fn default() -> Self {
122 Self::new()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 struct DummyTool {
131 tool_name: &'static str,
132 }
133
134 #[async_trait::async_trait]
135 impl InternalTool for DummyTool {
136 fn name(&self) -> &str {
137 self.tool_name
138 }
139 fn description(&self) -> &str {
140 "A dummy tool for testing"
141 }
142 async fn execute(
143 &self,
144 _state: &CognitiveState,
145 _args: serde_json::Value,
146 ) -> Result<serde_json::Value, PeError> {
147 Ok(serde_json::json!({"status": "ok"}))
148 }
149 }
150
151 #[test]
152 fn test_registry_new_is_empty() {
153 let registry = InternalToolRegistry::new();
154 assert!(registry.is_empty());
155 assert_eq!(registry.len(), 0);
156 assert!(registry.list().is_empty());
157 }
158
159 #[test]
160 fn test_registry_register_and_get() {
161 let mut registry = InternalToolRegistry::new();
162 registry.register(Arc::new(DummyTool { tool_name: "note" }));
163 assert_eq!(registry.len(), 1);
164 assert!(!registry.is_empty());
165
166 let tool = registry.get("note").expect("tool should exist");
167 assert_eq!(tool.name(), "note");
168 assert_eq!(tool.description(), "A dummy tool for testing");
169 }
170
171 #[test]
172 fn test_registry_get_missing_returns_none() {
173 let registry = InternalToolRegistry::new();
174 assert!(registry.get("nonexistent").is_none());
175 }
176
177 #[test]
178 fn test_registry_overwrite_same_name() {
179 let mut registry = InternalToolRegistry::new();
180 registry.register(Arc::new(DummyTool { tool_name: "note" }));
181 registry.register(Arc::new(DummyTool { tool_name: "note" }));
182 assert_eq!(registry.len(), 1);
183 }
184
185 #[test]
186 fn test_registry_list_returns_all_names() {
187 let mut registry = InternalToolRegistry::new();
188 registry.register(Arc::new(DummyTool { tool_name: "note" }));
189 registry.register(Arc::new(DummyTool {
190 tool_name: "read_notes",
191 }));
192 registry.register(Arc::new(DummyTool {
193 tool_name: "signal",
194 }));
195 let mut names = registry.list();
196 names.sort();
197 assert_eq!(names, vec!["note", "read_notes", "signal"]);
198 }
199
200 #[test]
201 fn test_registry_chained_register() {
202 let mut registry = InternalToolRegistry::new();
203 registry
204 .register(Arc::new(DummyTool { tool_name: "a" }))
205 .register(Arc::new(DummyTool { tool_name: "b" }));
206 assert_eq!(registry.len(), 2);
207 }
208
209 #[tokio::test]
210 async fn test_tool_execute() {
211 let tool = DummyTool { tool_name: "test" };
212 let state = CognitiveState::default();
213 let result = tool
214 .execute(&state, serde_json::json!({}))
215 .await
216 .expect("execute should succeed");
217 assert_eq!(result, serde_json::json!({"status": "ok"}));
218 }
219}