coro_core/tools/
registry.rs1use crate::tools::{Tool, ToolExecutor};
4use std::collections::HashMap;
5
6pub struct ToolRegistry {
8 factories: HashMap<String, Box<dyn ToolFactory>>,
9}
10
11pub trait ToolFactory: Send + Sync {
13 fn create(&self) -> Box<dyn Tool>;
15
16 fn tool_name(&self) -> &str;
18
19 fn tool_description(&self) -> &str;
21}
22
23impl ToolRegistry {
24 pub fn new() -> Self {
26 Self {
27 factories: HashMap::new(),
28 }
29 }
30
31 pub fn register_factory(&mut self, factory: Box<dyn ToolFactory>) {
33 self.factories
34 .insert(factory.tool_name().to_string(), factory);
35 }
36
37 pub fn create_tool(&self, name: &str) -> Option<Box<dyn Tool>> {
39 self.factories.get(name).map(|factory| factory.create())
40 }
41
42 pub fn list_tools(&self) -> Vec<&str> {
44 self.factories.keys().map(|s| s.as_str()).collect()
45 }
46
47 pub fn get_tool_info(&self, name: &str) -> Option<(&str, &str)> {
49 self.factories
50 .get(name)
51 .map(|factory| (factory.tool_name(), factory.tool_description()))
52 }
53
54 pub fn create_executor(&self, tool_names: &[String]) -> ToolExecutor {
56 let mut executor = ToolExecutor::new();
57
58 for name in tool_names {
59 if let Some(tool) = self.create_tool(name) {
60 executor.register_tool(tool);
61 }
62 }
63
64 executor
65 }
66
67 pub fn create_executor_with_all(&self) -> ToolExecutor {
69 let mut executor = ToolExecutor::new();
70
71 for factory in self.factories.values() {
72 executor.register_tool(factory.create());
73 }
74
75 executor
76 }
77}
78
79impl Default for ToolRegistry {
80 fn default() -> Self {
81 let mut registry = Self::new();
82
83 registry.register_factory(Box::new(crate::tools::builtin::ThinkingToolFactory));
85 registry.register_factory(Box::new(crate::tools::builtin::TaskDoneToolFactory));
86 registry.register_factory(Box::new(crate::tools::builtin::McpToolFactory));
87
88 registry
89 }
90}
91
92#[macro_export]
94macro_rules! impl_tool_factory {
95 ($factory:ident, $tool:ident, $name:expr, $description:expr) => {
96 pub struct $factory;
97
98 impl $crate::tools::ToolFactory for $factory {
99 fn create(&self) -> Box<dyn $crate::tools::Tool> {
100 Box::new($tool::new())
101 }
102
103 fn tool_name(&self) -> &str {
104 $name
105 }
106
107 fn tool_description(&self) -> &str {
108 $description
109 }
110 }
111 };
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::tools::registry::ToolRegistry;
117
118 #[test]
119 fn test_default_registry_has_all_tools() {
120 let registry = ToolRegistry::default();
121 let tools = registry.list_tools();
122
123 let expected_tools = vec!["sequentialthinking", "task_done", "mcp_tool"];
125
126 println!("Available tools: {:?}", tools);
127
128 for expected_tool in &expected_tools {
130 assert!(
131 tools.contains(expected_tool),
132 "Tool '{}' is not registered in the default registry",
133 expected_tool
134 );
135 }
136
137 assert_eq!(
139 tools.len(),
140 expected_tools.len(),
141 "Expected {} tools, but found {}. Tools: {:?}",
142 expected_tools.len(),
143 tools.len(),
144 tools
145 );
146 }
147
148 #[test]
149 fn test_tool_creation() {
150 let registry = ToolRegistry::default();
151
152 let tools_to_test = vec!["sequentialthinking", "task_done", "mcp_tool"];
154
155 for tool_name in tools_to_test {
156 let tool = registry.create_tool(tool_name);
157 assert!(tool.is_some(), "Failed to create tool '{}'", tool_name);
158
159 let tool = tool.unwrap();
160 assert_eq!(
161 tool.name(),
162 tool_name,
163 "Tool name mismatch for '{}'",
164 tool_name
165 );
166
167 assert!(
169 !tool.description().is_empty(),
170 "Tool '{}' has empty description",
171 tool_name
172 );
173
174 let schema = tool.parameters_schema();
176 assert!(
177 schema.is_object(),
178 "Tool '{}' parameters schema is not an object",
179 tool_name
180 );
181 }
182 }
183
184 #[test]
185 fn test_tool_info() {
186 let registry = ToolRegistry::default();
187
188 for tool_name in registry.list_tools() {
189 let info = registry.get_tool_info(tool_name);
190 assert!(
191 info.is_some(),
192 "Failed to get info for tool '{}'",
193 tool_name
194 );
195
196 let (name, description) = info.unwrap();
197 assert_eq!(name, tool_name);
198 assert!(!description.is_empty());
199 }
200 }
201
202 #[test]
203 fn test_executor_creation() {
204 let registry = ToolRegistry::default();
205
206 let tool_names = vec![
208 "bash".to_string(),
209 "str_replace_based_edit_tool".to_string(),
210 ];
211 let _executor = registry.create_executor(&tool_names);
212
213 let _all_executor = registry.create_executor_with_all();
219 }
220
221 #[test]
222 fn test_tool_examples() {
223 let registry = ToolRegistry::default();
224
225 for tool_name in registry.list_tools() {
226 let tool = registry.create_tool(tool_name).unwrap();
227 let examples = tool.examples();
228
229 assert!(!examples.is_empty(), "Tool '{}' has no examples", tool_name);
231
232 for (i, example) in examples.iter().enumerate() {
234 assert!(
235 !example.description.is_empty(),
236 "Tool '{}' example {} has empty description",
237 tool_name,
238 i
239 );
240
241 assert!(
242 example.parameters.is_object(),
243 "Tool '{}' example {} parameters is not an object",
244 tool_name,
245 i
246 );
247
248 assert!(
249 !example.expected_result.is_empty(),
250 "Tool '{}' example {} has empty expected result",
251 tool_name,
252 i
253 );
254 }
255 }
256 }
257
258 #[test]
259 fn test_tool_parameter_schemas() {
260 let registry = ToolRegistry::default();
261
262 for tool_name in registry.list_tools() {
263 let tool = registry.create_tool(tool_name).unwrap();
264 let schema = tool.parameters_schema();
265
266 assert!(
268 schema.is_object(),
269 "Tool '{}' schema is not an object",
270 tool_name
271 );
272
273 let schema_obj = schema.as_object().unwrap();
274
275 if let Some(type_val) = schema_obj.get("type") {
277 assert_eq!(
278 type_val.as_str(),
279 Some("object"),
280 "Tool '{}' schema type is not 'object'",
281 tool_name
282 );
283 }
284
285 if let Some(properties) = schema_obj.get("properties") {
287 assert!(
288 properties.is_object(),
289 "Tool '{}' schema properties is not an object",
290 tool_name
291 );
292
293 let props = properties.as_object().unwrap();
294 assert!(
295 !props.is_empty(),
296 "Tool '{}' has no properties in schema",
297 tool_name
298 );
299 }
300 }
301 }
302}