1use crate::engine::RustRuleEngine;
2use crate::errors::Result;
3use std::collections::HashMap;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, PartialEq)]
8pub enum PluginState {
9 Loading,
10 Loaded,
11 Unloaded,
12 Error,
13}
14
15#[derive(Debug, Clone, PartialEq)]
17pub enum PluginHealth {
18 Healthy,
19 Warning(String),
20 Error(String),
21}
22
23#[derive(Debug, Clone)]
25pub struct PluginMetadata {
26 pub name: String,
27 pub version: String,
28 pub description: String,
29 pub author: String,
30 pub state: PluginState,
31 pub health: PluginHealth,
32 pub actions: Vec<String>,
33 pub functions: Vec<String>,
34 pub dependencies: Vec<String>,
35}
36
37#[derive(Debug, Clone)]
39pub struct PluginInfo {
40 pub name: String,
41 pub version: String,
42 pub description: String,
43 pub state: PluginState,
44 pub health: PluginHealth,
45}
46
47pub trait RulePlugin: Send + Sync {
49 fn get_metadata(&self) -> &PluginMetadata;
51
52 fn register_actions(&self, engine: &mut RustRuleEngine) -> Result<()>;
54
55 fn register_functions(&self, engine: &mut RustRuleEngine) -> Result<()> {
57 Ok(())
59 }
60
61 fn unload(&mut self) -> Result<()> {
63 Ok(())
64 }
65
66 fn health_check(&mut self) -> PluginHealth {
68 PluginHealth::Healthy
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct PluginStats {
75 pub total_plugins: usize,
76 pub loaded_plugins: usize,
77 pub failed_plugins: usize,
78 pub warnings: usize,
79}
80
81#[derive(Debug, Clone)]
83pub struct PluginConfig {
84 pub max_plugins: usize,
85 pub enable_hot_reload: bool,
86 pub plugin_timeout_ms: u64,
87 pub safety_checks: bool,
88}
89
90impl Default for PluginConfig {
91 fn default() -> Self {
92 Self {
93 max_plugins: 50,
94 enable_hot_reload: true,
95 plugin_timeout_ms: 5000,
96 safety_checks: true,
97 }
98 }
99}
100
101pub struct PluginManager {
103 plugins: HashMap<String, Arc<dyn RulePlugin>>,
104 config: PluginConfig,
105 load_order: Vec<String>,
106}
107
108impl PluginManager {
109 pub fn new(config: PluginConfig) -> Self {
110 Self {
111 plugins: HashMap::new(),
112 config,
113 load_order: Vec::new(),
114 }
115 }
116
117 pub fn with_default_config() -> Self {
118 Self::new(PluginConfig::default())
119 }
120
121 pub fn load_plugin(&mut self, plugin: Arc<dyn RulePlugin>) -> Result<()> {
123 let metadata = plugin.get_metadata();
124 let name = metadata.name.clone();
125
126 if self.plugins.contains_key(&name) {
128 return Err(crate::errors::RuleEngineError::PluginError {
129 message: format!("Plugin '{}' is already loaded", name),
130 });
131 }
132
133 if self.plugins.len() >= self.config.max_plugins {
135 return Err(crate::errors::RuleEngineError::PluginError {
136 message: format!("Maximum plugin limit ({}) reached", self.config.max_plugins),
137 });
138 }
139
140 if self.config.safety_checks {
142 self.validate_dependencies(&metadata.dependencies)?;
143 }
144
145 self.plugins.insert(name.clone(), plugin);
147 self.load_order.push(name.clone());
148
149 Ok(())
150 }
151
152 pub fn unload_plugin(&mut self, name: &str) -> Result<()> {
154 let plugin = self.plugins.get_mut(name).ok_or_else(|| {
155 crate::errors::RuleEngineError::PluginError {
156 message: format!("Plugin '{}' not found", name),
157 }
158 })?;
159
160 self.plugins.remove(name);
163 self.load_order.retain(|n| n != name);
164
165 Ok(())
166 }
167
168 pub fn hot_reload_plugin(&mut self, name: &str, new_plugin: Arc<dyn RulePlugin>) -> Result<()> {
170 self.unload_plugin(name)?;
172
173 self.load_plugin(new_plugin)?;
175
176 Ok(())
177 }
178
179 pub fn get_plugin_info(&self, name: &str) -> Option<&PluginMetadata> {
181 self.plugins.get(name).map(|p| p.get_metadata())
182 }
183
184 pub fn list_plugins(&self) -> Vec<PluginInfo> {
186 self.plugins
187 .values()
188 .map(|plugin| {
189 let metadata = plugin.get_metadata();
190 PluginInfo {
191 name: metadata.name.clone(),
192 version: metadata.version.clone(),
193 description: metadata.description.clone(),
194 state: metadata.state.clone(),
195 health: metadata.health.clone(),
196 }
197 })
198 .collect()
199 }
200
201 pub fn plugin_health_check(&mut self) -> HashMap<String, PluginHealth> {
203 let mut results = HashMap::new();
204
205 for plugin in self.plugins.values() {
207 let metadata = plugin.get_metadata();
208 results.insert(metadata.name.clone(), metadata.health.clone());
209 }
210
211 results
212 }
213
214 fn validate_dependencies(&self, dependencies: &[String]) -> Result<()> {
216 for dep in dependencies {
217 if !self.plugins.contains_key(dep) {
218 return Err(crate::errors::RuleEngineError::PluginError {
219 message: format!("Dependency '{}' is not loaded", dep),
220 });
221 }
222 }
223 Ok(())
224 }
225
226 pub fn get_stats(&self) -> PluginStats {
228 let mut loaded_count = 0;
229 let mut failed_count = 0;
230 let mut warning_count = 0;
231
232 for plugin in self.plugins.values() {
233 let metadata = plugin.get_metadata();
234 match metadata.health {
235 PluginHealth::Healthy => loaded_count += 1,
236 PluginHealth::Warning(_) => warning_count += 1,
237 PluginHealth::Error(_) => failed_count += 1,
238 }
239 }
240
241 PluginStats {
242 total_plugins: self.plugins.len(),
243 loaded_plugins: loaded_count,
244 failed_plugins: failed_count,
245 warnings: warning_count,
246 }
247 }
248}
249
250impl std::fmt::Display for PluginStats {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 write!(
253 f,
254 "Plugins: {} total (✅ {} loaded, ⚠️ {} warnings, ❌ {} failed)",
255 self.total_plugins, self.loaded_plugins, self.warnings, self.failed_plugins
256 )
257 }
258}