dataforge/customization/
plugins.rs1use std::collections::HashMap;
4use std::path::Path;
5use serde_json::Value;
6use crate::error::{DataForgeError, Result};
7use super::{CustomGenerator, ParamType};
8
9pub struct PluginManager {
11 plugins: HashMap<String, Box<dyn Plugin>>,
12 loaded_paths: Vec<String>,
13}
14
15impl PluginManager {
16 pub fn new() -> Self {
18 Self {
19 plugins: HashMap::new(),
20 loaded_paths: Vec::new(),
21 }
22 }
23
24 pub fn load_plugin<P>(&mut self, plugin: P) -> Result<()>
26 where
27 P: Plugin + 'static,
28 {
29 let name = plugin.name().to_string();
30
31 if self.plugins.contains_key(&name) {
32 return Err(DataForgeError::validation(&format!(
33 "Plugin '{}' is already loaded", name
34 )));
35 }
36
37 plugin.initialize()?;
39
40 self.plugins.insert(name, Box::new(plugin));
41 Ok(())
42 }
43
44 pub fn unload_plugin(&mut self, name: &str) -> Result<()> {
46 if let Some(mut plugin) = self.plugins.remove(name) {
47 plugin.cleanup()?;
48 Ok(())
49 } else {
50 Err(DataForgeError::validation(&format!("Plugin '{}' not found", name)))
51 }
52 }
53
54 pub fn get_plugin(&self, name: &str) -> Option<&dyn Plugin> {
56 self.plugins.get(name).map(|p| p.as_ref())
57 }
58
59 pub fn list_plugins(&self) -> Vec<PluginInfo> {
61 self.plugins.iter().map(|(name, plugin)| {
62 PluginInfo {
63 name: name.clone(),
64 version: plugin.version().to_string(),
65 description: plugin.description().to_string(),
66 author: plugin.author().to_string(),
67 generators: plugin.generators().iter().map(|g| g.name().to_string()).collect(),
68 }
69 }).collect()
70 }
71
72 pub fn load_plugins_from_directory<P: AsRef<Path>>(&mut self, dir: P) -> Result<Vec<String>> {
74 let dir_path = dir.as_ref();
75 if !dir_path.exists() || !dir_path.is_dir() {
76 return Err(DataForgeError::validation("Plugin directory does not exist or is not a directory"));
77 }
78
79 let mut loaded_plugins = Vec::new();
80
81 for entry in std::fs::read_dir(dir_path)? {
87 let entry = entry?;
88 let path = entry.path();
89
90 if path.extension().and_then(|s| s.to_str()) == Some("plugin") {
91 if let Some(path_str) = path.to_str() {
94 self.loaded_paths.push(path_str.to_string());
95 loaded_plugins.push(path_str.to_string());
96 }
97 }
98 }
99
100 Ok(loaded_plugins)
101 }
102
103 pub fn reload_all(&mut self) -> Result<()> {
105 let plugin_names: Vec<String> = self.plugins.keys().cloned().collect();
107 for name in plugin_names {
108 self.unload_plugin(&name)?;
109 }
110
111 let paths = self.loaded_paths.clone();
113 for path in paths {
114 println!("Reloading plugin from: {}", path);
117 }
118
119 Ok(())
120 }
121
122 pub fn get_all_generators(&self) -> Vec<&dyn CustomGenerator> {
124 let mut generators = Vec::new();
125
126 for plugin in self.plugins.values() {
127 generators.extend(plugin.generators());
128 }
129
130 generators
131 }
132}
133
134impl Default for PluginManager {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140pub trait Plugin: Send + Sync {
142 fn name(&self) -> &str;
144
145 fn version(&self) -> &str;
147
148 fn description(&self) -> &str;
150
151 fn author(&self) -> &str;
153
154 fn initialize(&self) -> Result<()> {
156 Ok(())
157 }
158
159 fn cleanup(&mut self) -> Result<()> {
161 Ok(())
162 }
163
164 fn generators(&self) -> Vec<&dyn CustomGenerator>;
166
167 fn config(&self) -> HashMap<String, Value> {
169 HashMap::new()
170 }
171
172 fn set_config(&mut self, config: HashMap<String, Value>) -> Result<()> {
174 let _ = config;
175 Ok(())
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct PluginInfo {
182 pub name: String,
183 pub version: String,
184 pub description: String,
185 pub author: String,
186 pub generators: Vec<String>,
187}
188
189pub struct MathGeneratorPlugin {
191 name: String,
192 version: String,
193 generators: Vec<Box<dyn CustomGenerator>>,
194}
195
196impl MathGeneratorPlugin {
197 pub fn new() -> Self {
198 let mut plugin = Self {
199 name: "math_generators".to_string(),
200 version: "1.0.0".to_string(),
201 generators: Vec::new(),
202 };
203
204 plugin.generators.push(Box::new(RandomNumberGenerator::new()));
206 plugin.generators.push(Box::new(MathExpressionGenerator::new()));
207
208 plugin
209 }
210}
211
212impl Plugin for MathGeneratorPlugin {
213 fn name(&self) -> &str {
214 &self.name
215 }
216
217 fn version(&self) -> &str {
218 &self.version
219 }
220
221 fn description(&self) -> &str {
222 "Provides mathematical data generators"
223 }
224
225 fn author(&self) -> &str {
226 "DataForge Team"
227 }
228
229 fn generators(&self) -> Vec<&dyn CustomGenerator> {
230 self.generators.iter().map(|g| g.as_ref()).collect()
231 }
232}
233
234impl Default for MathGeneratorPlugin {
235 fn default() -> Self {
236 Self::new()
237 }
238}
239
240struct RandomNumberGenerator {
242 name: String,
243}
244
245impl RandomNumberGenerator {
246 fn new() -> Self {
247 Self {
248 name: "random_number".to_string(),
249 }
250 }
251}
252
253impl CustomGenerator for RandomNumberGenerator {
254 fn name(&self) -> &str {
255 &self.name
256 }
257
258 fn description(&self) -> &str {
259 "Generates random numbers within specified range"
260 }
261
262 fn generate(&self) -> Result<Value> {
263 use rand::Rng;
264 let mut rng = rand::thread_rng();
265 let number = rng.gen_range(0..=100);
266 Ok(Value::Number(serde_json::Number::from(number)))
267 }
268
269 fn generate_with_params(&self, params: &HashMap<String, Value>) -> Result<Value> {
270 use rand::Rng;
271 let mut rng = rand::thread_rng();
272
273 let min = params.get("min")
274 .and_then(|v| v.as_i64())
275 .unwrap_or(0);
276
277 let max = params.get("max")
278 .and_then(|v| v.as_i64())
279 .unwrap_or(100);
280
281 if min >= max {
282 return Err(DataForgeError::validation("Min must be less than max"));
283 }
284
285 let number = rng.gen_range(min..=max);
286 Ok(Value::Number(serde_json::Number::from(number)))
287 }
288
289 fn validate_params(&self, params: &HashMap<String, Value>) -> Result<()> {
290 if let (Some(min), Some(max)) = (params.get("min"), params.get("max")) {
291 if let (Some(min_val), Some(max_val)) = (min.as_i64(), max.as_i64()) {
292 if min_val >= max_val {
293 return Err(DataForgeError::validation("Min must be less than max"));
294 }
295 }
296 }
297 Ok(())
298 }
299
300 fn param_schema(&self) -> HashMap<String, ParamType> {
301 let mut schema = HashMap::new();
302 schema.insert("min".to_string(), ParamType::Integer { min: None, max: None });
303 schema.insert("max".to_string(), ParamType::Integer { min: None, max: None });
304 schema
305 }
306}
307
308struct MathExpressionGenerator {
310 name: String,
311}
312
313impl MathExpressionGenerator {
314 fn new() -> Self {
315 Self {
316 name: "math_expression".to_string(),
317 }
318 }
319}
320
321impl CustomGenerator for MathExpressionGenerator {
322 fn name(&self) -> &str {
323 &self.name
324 }
325
326 fn description(&self) -> &str {
327 "Generates simple mathematical expressions"
328 }
329
330 fn generate(&self) -> Result<Value> {
331 use rand::Rng;
332 let mut rng = rand::thread_rng();
333
334 let a = rng.gen_range(1..=100);
335 let b = rng.gen_range(1..=100);
336 let operators = ["+", "-", "*", "/"];
337 let op = operators[rng.gen_range(0..operators.len())];
338
339 let expression = format!("{} {} {}", a, op, b);
340 Ok(Value::String(expression))
341 }
342
343 fn generate_with_params(&self, params: &HashMap<String, Value>) -> Result<Value> {
344 use rand::Rng;
345 let mut rng = rand::thread_rng();
346
347 let max_value = params.get("max_value")
348 .and_then(|v| v.as_u64())
349 .unwrap_or(100) as i32;
350
351 let operators = if let Some(Value::Array(ops)) = params.get("operators") {
352 ops.iter()
353 .filter_map(|v| v.as_str())
354 .collect::<Vec<_>>()
355 } else {
356 vec!["+", "-", "*", "/"]
357 };
358
359 if operators.is_empty() {
360 return Err(DataForgeError::validation("At least one operator must be specified"));
361 }
362
363 let a = rng.gen_range(1..=max_value);
364 let b = rng.gen_range(1..=max_value);
365 let op = operators[rng.gen_range(0..operators.len())];
366
367 let expression = format!("{} {} {}", a, op, b);
368 Ok(Value::String(expression))
369 }
370
371 fn param_schema(&self) -> HashMap<String, ParamType> {
372 let mut schema = HashMap::new();
373 schema.insert("max_value".to_string(), ParamType::Integer { min: Some(1), max: Some(10000) });
374 schema.insert("operators".to_string(), ParamType::Array(Box::new(ParamType::String { max_length: Some(1) })));
375 schema
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_plugin_manager() {
385 let mut manager = PluginManager::new();
386 let plugin = MathGeneratorPlugin::new();
387
388 assert!(manager.load_plugin(plugin).is_ok());
389
390 let plugins = manager.list_plugins();
391 assert_eq!(plugins.len(), 1);
392 assert_eq!(plugins[0].name, "math_generators");
393
394 let generators = manager.get_all_generators();
395 assert_eq!(generators.len(), 2);
396 }
397
398 #[test]
399 fn test_math_plugin() {
400 let plugin = MathGeneratorPlugin::new();
401
402 assert_eq!(plugin.name(), "math_generators");
403 assert_eq!(plugin.version(), "1.0.0");
404 assert!(!plugin.description().is_empty());
405
406 let generators = plugin.generators();
407 assert_eq!(generators.len(), 2);
408 }
409
410 #[test]
411 fn test_random_number_generator() {
412 let generator = RandomNumberGenerator::new();
413
414 let result = generator.generate();
415 assert!(result.is_ok());
416
417 if let Ok(Value::Number(n)) = result {
418 let val = n.as_i64().unwrap();
419 assert!(val >= 0 && val <= 100);
420 }
421
422 let mut params = HashMap::new();
423 params.insert("min".to_string(), Value::Number(serde_json::Number::from(10)));
424 params.insert("max".to_string(), Value::Number(serde_json::Number::from(20)));
425
426 let result = generator.generate_with_params(¶ms);
427 assert!(result.is_ok());
428
429 if let Ok(Value::Number(n)) = result {
430 let val = n.as_i64().unwrap();
431 assert!(val >= 10 && val <= 20);
432 }
433 }
434
435 #[test]
436 fn test_math_expression_generator() {
437 let generator = MathExpressionGenerator::new();
438
439 let result = generator.generate();
440 assert!(result.is_ok());
441
442 if let Ok(Value::String(expr)) = result {
443 assert!(expr.contains(' '));
444 assert!(expr.chars().any(|c| "+-*/".contains(c)));
445 }
446 }
447
448 #[test]
449 fn test_plugin_unload() {
450 let mut manager = PluginManager::new();
451 let plugin = MathGeneratorPlugin::new();
452
453 manager.load_plugin(plugin).unwrap();
454 assert_eq!(manager.list_plugins().len(), 1);
455
456 assert!(manager.unload_plugin("math_generators").is_ok());
457 assert_eq!(manager.list_plugins().len(), 0);
458
459 assert!(manager.unload_plugin("nonexistent").is_err());
460 }
461}