kaish_kernel/tools/
registry.rs1use std::collections::HashMap;
4use std::sync::Arc;
5
6use super::traits::{Tool, ToolSchema};
7
8#[derive(Default)]
10pub struct ToolRegistry {
11 tools: HashMap<String, Arc<dyn Tool>>,
12}
13
14impl ToolRegistry {
15 pub fn new() -> Self {
17 Self::default()
18 }
19
20 pub fn register(&mut self, tool: impl Tool + 'static) {
22 let name = tool.name().to_string();
23 self.tools.insert(name, Arc::new(tool));
24 }
25
26 pub fn register_arc(&mut self, tool: Arc<dyn Tool>) {
28 let name = tool.name().to_string();
29 self.tools.insert(name, tool);
30 }
31
32 pub fn get(&self, name: &str) -> Option<Arc<dyn Tool>> {
34 self.tools.get(name).cloned()
35 }
36
37 pub fn contains(&self, name: &str) -> bool {
39 self.tools.contains_key(name)
40 }
41
42 pub fn names(&self) -> Vec<&str> {
44 let mut names: Vec<_> = self.tools.keys().map(|s| s.as_str()).collect();
45 names.sort();
46 names
47 }
48
49 pub fn schemas(&self) -> Vec<ToolSchema> {
51 let mut schemas: Vec<_> = self.tools.values().map(|t| t.schema()).collect();
52 schemas.sort_by(|a, b| a.name.cmp(&b.name));
53 schemas
54 }
55
56 pub fn len(&self) -> usize {
58 self.tools.len()
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.tools.is_empty()
64 }
65}
66
67impl std::fmt::Debug for ToolRegistry {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 f.debug_struct("ToolRegistry")
70 .field("tools", &self.names())
71 .finish()
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::interpreter::ExecResult;
79 use crate::tools::{ExecContext, ToolArgs};
80 use async_trait::async_trait;
81
82 struct DummyTool;
83
84 #[async_trait]
85 impl Tool for DummyTool {
86 fn name(&self) -> &str {
87 "dummy"
88 }
89
90 fn schema(&self) -> ToolSchema {
91 ToolSchema::new("dummy", "A test tool")
92 }
93
94 async fn execute(&self, _args: ToolArgs, _ctx: &mut ExecContext) -> ExecResult {
95 ExecResult::success("dummy output")
96 }
97 }
98
99 #[test]
100 fn test_register_and_get() {
101 let mut registry = ToolRegistry::new();
102 registry.register(DummyTool);
103
104 assert!(registry.contains("dummy"));
105 assert!(registry.get("dummy").is_some());
106 assert!(!registry.contains("nonexistent"));
107 }
108
109 #[test]
110 fn test_names_sorted() {
111 let mut registry = ToolRegistry::new();
112
113 struct ToolA;
114 struct ToolZ;
115
116 #[async_trait]
117 impl Tool for ToolA {
118 fn name(&self) -> &str { "aaa" }
119 fn schema(&self) -> ToolSchema { ToolSchema::new("aaa", "") }
120 async fn execute(&self, _: ToolArgs, _: &mut ExecContext) -> ExecResult {
121 ExecResult::success("")
122 }
123 }
124
125 #[async_trait]
126 impl Tool for ToolZ {
127 fn name(&self) -> &str { "zzz" }
128 fn schema(&self) -> ToolSchema { ToolSchema::new("zzz", "") }
129 async fn execute(&self, _: ToolArgs, _: &mut ExecContext) -> ExecResult {
130 ExecResult::success("")
131 }
132 }
133
134 registry.register(ToolZ);
135 registry.register(ToolA);
136
137 let names = registry.names();
138 assert_eq!(names, vec!["aaa", "zzz"]);
139 }
140}