vx_plugin/
registry.rs

1//! Plugin registry for managing and discovering plugins
2//!
3//! This module provides functionality for registering, discovering, and managing
4//! plugins in the vx ecosystem.
5
6use crate::{Result, VxPackageManager, VxPlugin, VxTool};
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9
10/// Plugin registry that manages all loaded plugins
11///
12/// The registry is responsible for:
13/// - Loading and registering plugins
14/// - Discovering tools and package managers
15/// - Managing plugin lifecycle
16/// - Resolving plugin dependencies
17#[derive(Default)]
18pub struct PluginRegistry {
19    /// Map of plugin name to plugin instance
20    plugins: Arc<RwLock<HashMap<String, Box<dyn VxPlugin>>>>,
21    /// Map of tool name to plugin name (for quick lookup)
22    tool_index: Arc<RwLock<HashMap<String, String>>>,
23    /// Map of package manager name to plugin name (for quick lookup)
24    pm_index: Arc<RwLock<HashMap<String, String>>>,
25}
26
27impl PluginRegistry {
28    /// Create a new empty plugin registry
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Register a plugin with the registry
34    ///
35    /// This method adds a plugin to the registry and updates the internal
36    /// indexes for quick tool and package manager lookup.
37    pub async fn register_plugin(&self, mut plugin: Box<dyn VxPlugin>) -> Result<()> {
38        let plugin_name = plugin.name().to_string();
39
40        // Initialize the plugin
41        plugin.initialize().await?;
42
43        // Update tool index
44        {
45            let mut tool_index = self.tool_index.write().unwrap();
46            for tool in plugin.tools() {
47                tool_index.insert(tool.name().to_string(), plugin_name.clone());
48                // Also register aliases
49                for alias in tool.aliases() {
50                    tool_index.insert(alias.to_string(), plugin_name.clone());
51                }
52            }
53        }
54
55        // Update package manager index
56        {
57            let mut pm_index = self.pm_index.write().unwrap();
58            for pm in plugin.package_managers() {
59                pm_index.insert(pm.name().to_string(), plugin_name.clone());
60            }
61        }
62
63        // Register the plugin
64        {
65            let mut plugins = self.plugins.write().unwrap();
66            plugins.insert(plugin_name, plugin);
67        }
68
69        Ok(())
70    }
71    /// Unregister a plugin from the registry
72    ///
73    /// This method removes a plugin and cleans up all associated indexes.
74    pub async fn unregister_plugin(&self, plugin_name: &str) -> Result<()> {
75        // Get the plugin first to call shutdown
76        let mut plugin = {
77            let mut plugins = self.plugins.write().unwrap();
78            plugins.remove(plugin_name)
79        };
80
81        if let Some(ref mut plugin) = plugin {
82            // Shutdown the plugin
83            plugin.shutdown().await?;
84
85            // Clean up tool index
86            {
87                let mut tool_index = self.tool_index.write().unwrap();
88                tool_index.retain(|_, p_name| p_name != plugin_name);
89            }
90
91            // Clean up package manager index
92            {
93                let mut pm_index = self.pm_index.write().unwrap();
94                pm_index.retain(|_, p_name| p_name != plugin_name);
95            }
96        }
97
98        Ok(())
99    }
100
101    /// Get a tool by name
102    ///
103    /// Returns the tool implementation if found in any registered plugin.
104    pub fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
105        let tool_index = self.tool_index.read().unwrap();
106        let plugins = self.plugins.read().unwrap();
107
108        if let Some(plugin_name) = tool_index.get(tool_name) {
109            if let Some(plugin) = plugins.get(plugin_name) {
110                return plugin
111                    .tools()
112                    .into_iter()
113                    .find(|tool| tool.name() == tool_name || tool.aliases().contains(&tool_name));
114            }
115        }
116
117        None
118    }
119
120    /// Get a package manager by name
121    ///
122    /// Returns the package manager implementation if found in any registered plugin.
123    pub fn get_package_manager(&self, pm_name: &str) -> Option<Box<dyn VxPackageManager>> {
124        let pm_index = self.pm_index.read().unwrap();
125        let plugins = self.plugins.read().unwrap();
126
127        if let Some(plugin_name) = pm_index.get(pm_name) {
128            if let Some(plugin) = plugins.get(plugin_name) {
129                return plugin
130                    .package_managers()
131                    .into_iter()
132                    .find(|pm| pm.name() == pm_name);
133            }
134        }
135
136        None
137    }
138    /// List all registered plugins
139    ///
140    /// Returns a vector of plugin names currently registered.
141    pub fn list_plugins(&self) -> Vec<String> {
142        let plugins = self.plugins.read().unwrap();
143        plugins.keys().cloned().collect()
144    }
145
146    /// List all available tools
147    ///
148    /// Returns a vector of tool names from all registered plugins.
149    pub fn list_tools(&self) -> Vec<String> {
150        let tool_index = self.tool_index.read().unwrap();
151        tool_index.keys().cloned().collect()
152    }
153
154    /// List all available package managers
155    ///
156    /// Returns a vector of package manager names from all registered plugins.
157    pub fn list_package_managers(&self) -> Vec<String> {
158        let pm_index = self.pm_index.read().unwrap();
159        pm_index.keys().cloned().collect()
160    }
161
162    /// Check if a tool is available
163    ///
164    /// Returns true if any registered plugin provides the specified tool.
165    pub fn has_tool(&self, tool_name: &str) -> bool {
166        let tool_index = self.tool_index.read().unwrap();
167        tool_index.contains_key(tool_name)
168    }
169
170    /// Check if a package manager is available
171    ///
172    /// Returns true if any registered plugin provides the specified package manager.
173    pub fn has_package_manager(&self, pm_name: &str) -> bool {
174        let pm_index = self.pm_index.read().unwrap();
175        pm_index.contains_key(pm_name)
176    }
177
178    /// Get plugin information
179    ///
180    /// Returns metadata about a specific plugin if it's registered.
181    pub fn get_plugin_info(&self, plugin_name: &str) -> Option<HashMap<String, String>> {
182        let plugins = self.plugins.read().unwrap();
183        plugins.get(plugin_name).map(|plugin| plugin.metadata())
184    }
185
186    /// Get all plugin information
187    ///
188    /// Returns metadata for all registered plugins.
189    pub fn get_all_plugin_info(&self) -> HashMap<String, HashMap<String, String>> {
190        let plugins = self.plugins.read().unwrap();
191        plugins
192            .iter()
193            .map(|(name, plugin)| (name.clone(), plugin.metadata()))
194            .collect()
195    }
196    /// Shutdown all plugins
197    ///
198    /// This method shuts down all registered plugins and clears the registry.
199    /// It should be called when the application is shutting down.
200    pub async fn shutdown_all(&self) -> Result<()> {
201        let plugins = {
202            let mut plugins_guard = self.plugins.write().unwrap();
203            std::mem::take(&mut *plugins_guard)
204        };
205
206        // Shutdown all plugins
207        for (_, mut plugin) in plugins {
208            if let Err(e) = plugin.shutdown().await {
209                eprintln!(
210                    "Warning: Failed to shutdown plugin {}: {}",
211                    plugin.name(),
212                    e
213                );
214            }
215        }
216
217        // Clear indexes
218        {
219            let mut tool_index = self.tool_index.write().unwrap();
220            tool_index.clear();
221        }
222        {
223            let mut pm_index = self.pm_index.write().unwrap();
224            pm_index.clear();
225        }
226
227        Ok(())
228    }
229}
230
231/// Builder for creating and configuring a plugin registry
232///
233/// This builder provides a fluent interface for setting up a plugin registry
234/// with various configuration options.
235#[derive(Default)]
236pub struct PluginRegistryBuilder {
237    plugins: Vec<Box<dyn VxPlugin>>,
238}
239
240impl PluginRegistryBuilder {
241    /// Create a new plugin registry builder
242    pub fn new() -> Self {
243        Self::default()
244    }
245
246    /// Add a plugin to be registered
247    ///
248    /// The plugin will be registered when `build()` is called.
249    pub fn with_plugin(mut self, plugin: Box<dyn VxPlugin>) -> Self {
250        self.plugins.push(plugin);
251        self
252    }
253
254    /// Build the plugin registry
255    ///
256    /// This method creates the registry and registers all added plugins.
257    pub async fn build(self) -> Result<PluginRegistry> {
258        let registry = PluginRegistry::new();
259
260        for plugin in self.plugins {
261            registry.register_plugin(plugin).await?;
262        }
263
264        Ok(registry)
265    }
266}
267
268/// Simplified tool registry for backward compatibility
269///
270/// This registry provides a simplified interface focused on tools only,
271/// maintaining compatibility with existing code that expects a ToolRegistry.
272pub struct ToolRegistry {
273    plugin_registry: PluginRegistry,
274}
275
276impl ToolRegistry {
277    /// Create a new tool registry
278    pub fn new() -> Self {
279        Self {
280            plugin_registry: PluginRegistry::new(),
281        }
282    }
283
284    /// Register a plugin that provides tools
285    pub async fn register_plugin(&self, plugin: Box<dyn VxPlugin>) -> Result<()> {
286        self.plugin_registry.register_plugin(plugin).await
287    }
288
289    /// Get a tool by name
290    pub fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
291        self.plugin_registry.get_tool(tool_name)
292    }
293
294    /// Check if a tool is supported
295    pub fn supports_tool(&self, tool_name: &str) -> bool {
296        self.plugin_registry.has_tool(tool_name)
297    }
298
299    /// Get all tool names
300    pub fn get_tool_names(&self) -> Vec<String> {
301        self.plugin_registry.list_tools()
302    }
303
304    /// Get all tools from all plugins
305    pub fn get_all_tools(&self) -> Vec<Box<dyn VxTool>> {
306        let plugins = self.plugin_registry.plugins.read().unwrap();
307        plugins.values().flat_map(|plugin| plugin.tools()).collect()
308    }
309
310    /// Initialize all plugins
311    pub async fn initialize_all(&self) -> Result<()> {
312        // Get all plugins and initialize them
313        let plugins = {
314            let plugins_guard = self.plugin_registry.plugins.read().unwrap();
315            plugins_guard.keys().cloned().collect::<Vec<_>>()
316        };
317
318        for _plugin_name in plugins {
319            // Note: This is a simplified implementation
320            // In a real scenario, we'd need mutable access to plugins
321            // For now, we assume plugins are already initialized during registration
322        }
323
324        Ok(())
325    }
326}
327
328impl Default for ToolRegistry {
329    fn default() -> Self {
330        Self::new()
331    }
332}