Skip to main content

rma_plugins/
registry.rs

1//! Plugin registry for managing loaded plugins
2
3use crate::{Plugin, PluginError, PluginMetadata};
4use anyhow::Result;
5use rma_common::{Finding, Language};
6use std::collections::HashMap;
7use tracing::{debug, info};
8
9/// Registry of loaded plugins
10pub struct PluginRegistry {
11    plugins: HashMap<String, Plugin>,
12}
13
14impl PluginRegistry {
15    pub fn new() -> Self {
16        Self {
17            plugins: HashMap::new(),
18        }
19    }
20
21    /// Register a new plugin
22    pub fn register(&mut self, plugin: Plugin) -> Result<(), PluginError> {
23        let name = plugin.metadata.name.clone();
24
25        if self.plugins.contains_key(&name) {
26            return Err(PluginError::LoadError(format!(
27                "Plugin '{}' is already registered",
28                name
29            )));
30        }
31
32        info!("Registered plugin: {} v{}", name, plugin.metadata.version);
33        self.plugins.insert(name, plugin);
34        Ok(())
35    }
36
37    /// Unregister a plugin by name
38    pub fn unregister(&mut self, name: &str) -> Result<(), PluginError> {
39        if self.plugins.remove(name).is_some() {
40            info!("Unregistered plugin: {}", name);
41            Ok(())
42        } else {
43            Err(PluginError::NotFound(name.to_string()))
44        }
45    }
46
47    /// Get a plugin by name
48    pub fn get(&self, name: &str) -> Option<&Plugin> {
49        self.plugins.get(name)
50    }
51
52    /// Get a mutable reference to a plugin
53    pub fn get_mut(&mut self, name: &str) -> Option<&mut Plugin> {
54        self.plugins.get_mut(name)
55    }
56
57    /// List all registered plugins
58    pub fn list(&self) -> Vec<&PluginMetadata> {
59        self.plugins.values().map(|p| &p.metadata).collect()
60    }
61
62    /// Get plugins that support a given language
63    pub fn plugins_for_language(&self, language: Language) -> Vec<&str> {
64        self.plugins
65            .iter()
66            .filter(|(_, p)| {
67                p.metadata.languages.contains(&language) || p.metadata.languages.is_empty()
68            })
69            .map(|(name, _)| name.as_str())
70            .collect()
71    }
72
73    /// Run all applicable plugins on source code
74    pub fn analyze_all(&mut self, source: &str, language: Language) -> Result<Vec<Finding>> {
75        let mut all_findings = Vec::new();
76
77        // Get names of applicable plugins
78        let plugin_names: Vec<String> = self
79            .plugins
80            .iter()
81            .filter(|(_, p)| {
82                p.metadata.languages.contains(&language) || p.metadata.languages.is_empty()
83            })
84            .map(|(name, _)| name.clone())
85            .collect();
86
87        // Run each plugin
88        for name in plugin_names {
89            if let Some(plugin) = self.plugins.get_mut(&name) {
90                debug!("Running plugin: {}", name);
91                match plugin.analyze(source, language) {
92                    Ok(findings) => {
93                        debug!("Plugin {} returned {} findings", name, findings.len());
94                        all_findings.extend(findings);
95                    }
96                    Err(e) => {
97                        tracing::warn!("Plugin {} failed: {}", name, e);
98                    }
99                }
100            }
101        }
102
103        Ok(all_findings)
104    }
105
106    /// Get count of registered plugins
107    pub fn count(&self) -> usize {
108        self.plugins.len()
109    }
110
111    /// Check if a plugin is registered
112    pub fn contains(&self, name: &str) -> bool {
113        self.plugins.contains_key(name)
114    }
115}
116
117impl Default for PluginRegistry {
118    fn default() -> Self {
119        Self::new()
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_registry_creation() {
129        let registry = PluginRegistry::new();
130        assert_eq!(registry.count(), 0);
131    }
132}