1use crate::{Result, VxError, VxPackageManager, VxPlugin, VxTool};
4use std::collections::HashMap;
5
6pub struct PluginRegistry {
8 plugins: Vec<Box<dyn VxPlugin>>,
9 tool_cache: HashMap<String, usize>, pm_cache: HashMap<String, usize>, }
12
13impl PluginRegistry {
14 pub fn new() -> Self {
16 Self {
17 plugins: Vec::new(),
18 tool_cache: HashMap::new(),
19 pm_cache: HashMap::new(),
20 }
21 }
22
23 pub fn register(&mut self, plugin: Box<dyn VxPlugin>) -> Result<()> {
25 let plugin_index = self.plugins.len();
26
27 for tool in plugin.tools() {
29 let tool_name = tool.name().to_string();
30 if self.tool_cache.contains_key(&tool_name) {
31 return Err(VxError::Other {
32 message: format!("Tool '{}' is already registered", tool_name),
33 });
34 }
35 self.tool_cache.insert(tool_name.clone(), plugin_index);
36
37 for alias in tool.aliases() {
39 self.tool_cache.insert(alias.to_string(), plugin_index);
40 }
41 }
42
43 for pm in plugin.package_managers() {
45 let pm_name = pm.name().to_string();
46 if self.pm_cache.contains_key(&pm_name) {
47 return Err(VxError::Other {
48 message: format!("Package manager '{}' is already registered", pm_name),
49 });
50 }
51 self.pm_cache.insert(pm_name, plugin_index);
52 }
53
54 self.plugins.push(plugin);
55 Ok(())
56 }
57
58 pub fn find_plugin_for_tool(&self, tool_name: &str) -> Option<&dyn VxPlugin> {
60 self.tool_cache
61 .get(tool_name)
62 .and_then(|&index| self.plugins.get(index))
63 .map(|plugin| plugin.as_ref())
64 }
65
66 pub fn find_plugin_for_package_manager(&self, pm_name: &str) -> Option<&dyn VxPlugin> {
68 self.pm_cache
69 .get(pm_name)
70 .and_then(|&index| self.plugins.get(index))
71 .map(|plugin| plugin.as_ref())
72 }
73
74 pub fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
76 if let Some(plugin) = self.find_plugin_for_tool(tool_name) {
77 plugin
78 .tools()
79 .into_iter()
80 .find(|tool| tool.name() == tool_name || tool.aliases().contains(&tool_name))
81 } else {
82 None
83 }
84 }
85
86 pub fn get_package_manager(&self, pm_name: &str) -> Option<Box<dyn VxPackageManager>> {
88 if let Some(plugin) = self.find_plugin_for_package_manager(pm_name) {
89 plugin
90 .package_managers()
91 .into_iter()
92 .find(|pm| pm.name() == pm_name)
93 } else {
94 None
95 }
96 }
97
98 pub fn get_all_tools(&self) -> Vec<Box<dyn VxTool>> {
100 self.plugins
101 .iter()
102 .flat_map(|plugin| plugin.tools())
103 .collect()
104 }
105
106 pub fn get_all_package_managers(&self) -> Vec<Box<dyn VxPackageManager>> {
108 self.plugins
109 .iter()
110 .flat_map(|plugin| plugin.package_managers())
111 .collect()
112 }
113
114 pub fn get_tool_names(&self) -> Vec<String> {
116 self.tool_cache.keys().cloned().collect()
117 }
118
119 pub fn get_package_manager_names(&self) -> Vec<String> {
121 self.pm_cache.keys().cloned().collect()
122 }
123
124 pub fn supports_tool(&self, tool_name: &str) -> bool {
126 self.tool_cache.contains_key(tool_name)
127 }
128
129 pub fn supports_package_manager(&self, pm_name: &str) -> bool {
131 self.pm_cache.contains_key(pm_name)
132 }
133
134 pub fn get_plugins(&self) -> &[Box<dyn VxPlugin>] {
136 &self.plugins
137 }
138
139 pub async fn initialize_all(&mut self) -> Result<()> {
141 for plugin in &mut self.plugins {
142 plugin.initialize().await?;
143 }
144 Ok(())
145 }
146}
147
148impl Default for PluginRegistry {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154pub struct ToolRegistry {
156 plugin_registry: PluginRegistry,
157}
158
159impl ToolRegistry {
160 pub fn new() -> Self {
162 Self {
163 plugin_registry: PluginRegistry::new(),
164 }
165 }
166
167 pub fn register_plugin(&mut self, plugin: Box<dyn VxPlugin>) -> Result<()> {
169 self.plugin_registry.register(plugin)
170 }
171
172 pub fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
174 self.plugin_registry.get_tool(tool_name)
175 }
176
177 pub fn supports_tool(&self, tool_name: &str) -> bool {
179 self.plugin_registry.supports_tool(tool_name)
180 }
181
182 pub fn get_tool_names(&self) -> Vec<String> {
184 self.plugin_registry.get_tool_names()
185 }
186
187 pub fn get_all_tools(&self) -> Vec<Box<dyn VxTool>> {
189 self.plugin_registry.get_all_tools()
190 }
191}
192
193impl Default for ToolRegistry {
194 fn default() -> Self {
195 Self::new()
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate::{Ecosystem, PackageSpec, VersionInfo};
203 use std::path::Path;
204
205 struct MockTool {
207 name: String,
208 }
209
210 #[async_trait::async_trait]
211 impl VxTool for MockTool {
212 fn name(&self) -> &str {
213 &self.name
214 }
215
216 async fn fetch_versions(&self, _include_prerelease: bool) -> Result<Vec<VersionInfo>> {
217 Ok(vec![VersionInfo::new("1.0.0".to_string())])
218 }
219 }
220
221 struct MockPackageManager {
222 name: String,
223 }
224
225 #[async_trait::async_trait]
226 impl VxPackageManager for MockPackageManager {
227 fn name(&self) -> &str {
228 &self.name
229 }
230
231 fn ecosystem(&self) -> Ecosystem {
232 Ecosystem::Other("test".to_string())
233 }
234
235 async fn install_packages(
236 &self,
237 _packages: &[PackageSpec],
238 _project_path: &Path,
239 ) -> Result<()> {
240 Ok(())
241 }
242 }
243
244 struct MockPlugin {
245 name: String,
246 }
247
248 #[async_trait::async_trait]
249 impl VxPlugin for MockPlugin {
250 fn name(&self) -> &str {
251 &self.name
252 }
253
254 fn tools(&self) -> Vec<Box<dyn VxTool>> {
255 vec![Box::new(MockTool {
256 name: "test-tool".to_string(),
257 })]
258 }
259
260 fn package_managers(&self) -> Vec<Box<dyn VxPackageManager>> {
261 vec![Box::new(MockPackageManager {
262 name: "test-pm".to_string(),
263 })]
264 }
265 }
266
267 #[tokio::test]
268 async fn test_plugin_registry() {
269 let mut registry = PluginRegistry::new();
270
271 let plugin = Box::new(MockPlugin {
272 name: "test-plugin".to_string(),
273 });
274
275 assert!(registry.register(plugin).is_ok());
276 assert!(registry.supports_tool("test-tool"));
277 assert!(registry.supports_package_manager("test-pm"));
278 }
279}