1use crate::{Plugin, PluginError, PluginMetadata};
4use anyhow::Result;
5use rma_common::{Finding, Language};
6use std::collections::HashMap;
7use tracing::{debug, info};
8
9pub 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 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 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 pub fn get(&self, name: &str) -> Option<&Plugin> {
49 self.plugins.get(name)
50 }
51
52 pub fn get_mut(&mut self, name: &str) -> Option<&mut Plugin> {
54 self.plugins.get_mut(name)
55 }
56
57 pub fn list(&self) -> Vec<&PluginMetadata> {
59 self.plugins.values().map(|p| &p.metadata).collect()
60 }
61
62 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 pub fn analyze_all(&mut self, source: &str, language: Language) -> Result<Vec<Finding>> {
75 let mut all_findings = Vec::new();
76
77 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 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 pub fn count(&self) -> usize {
108 self.plugins.len()
109 }
110
111 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}