vx_core/
registry.rs

1//! Plugin and tool registry for managing loaded plugins
2
3use crate::{Result, VxError, VxPackageManager, VxPlugin, VxTool};
4use std::collections::HashMap;
5
6/// Registry for managing all loaded plugins
7pub struct PluginRegistry {
8    plugins: Vec<Box<dyn VxPlugin>>,
9    tool_cache: HashMap<String, usize>, // tool_name -> plugin_index
10    pm_cache: HashMap<String, usize>,   // pm_name -> plugin_index
11}
12
13impl PluginRegistry {
14    /// Create a new plugin registry
15    pub fn new() -> Self {
16        Self {
17            plugins: Vec::new(),
18            tool_cache: HashMap::new(),
19            pm_cache: HashMap::new(),
20        }
21    }
22
23    /// Register a new plugin
24    pub fn register(&mut self, plugin: Box<dyn VxPlugin>) -> Result<()> {
25        let plugin_index = self.plugins.len();
26
27        // Cache tool mappings
28        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            // Also register aliases
38            for alias in tool.aliases() {
39                self.tool_cache.insert(alias.to_string(), plugin_index);
40            }
41        }
42
43        // Cache package manager mappings
44        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    /// Find plugin that supports the given tool
59    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    /// Find plugin that supports the given package manager
67    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    /// Get tool by name
75    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    /// Get package manager by name
87    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    /// Get all available tools from all plugins
99    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    /// Get all available package managers from all plugins
107    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    /// Get all registered tool names
115    pub fn get_tool_names(&self) -> Vec<String> {
116        self.tool_cache.keys().cloned().collect()
117    }
118
119    /// Get all registered package manager names
120    pub fn get_package_manager_names(&self) -> Vec<String> {
121        self.pm_cache.keys().cloned().collect()
122    }
123
124    /// Check if a tool is supported
125    pub fn supports_tool(&self, tool_name: &str) -> bool {
126        self.tool_cache.contains_key(tool_name)
127    }
128
129    /// Check if a package manager is supported
130    pub fn supports_package_manager(&self, pm_name: &str) -> bool {
131        self.pm_cache.contains_key(pm_name)
132    }
133
134    /// Get all plugins
135    pub fn get_plugins(&self) -> &[Box<dyn VxPlugin>] {
136        &self.plugins
137    }
138
139    /// Initialize all plugins
140    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
154/// Simplified tool registry for backward compatibility
155pub struct ToolRegistry {
156    plugin_registry: PluginRegistry,
157}
158
159impl ToolRegistry {
160    /// Create a new tool registry
161    pub fn new() -> Self {
162        Self {
163            plugin_registry: PluginRegistry::new(),
164        }
165    }
166
167    /// Register a tool plugin
168    pub fn register_plugin(&mut self, plugin: Box<dyn VxPlugin>) -> Result<()> {
169        self.plugin_registry.register(plugin)
170    }
171
172    /// Get a tool by name
173    pub fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
174        self.plugin_registry.get_tool(tool_name)
175    }
176
177    /// Check if a tool is supported
178    pub fn supports_tool(&self, tool_name: &str) -> bool {
179        self.plugin_registry.supports_tool(tool_name)
180    }
181
182    /// Get all tool names
183    pub fn get_tool_names(&self) -> Vec<String> {
184        self.plugin_registry.get_tool_names()
185    }
186
187    /// Get all tools
188    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    // Mock implementations for testing
206    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}