Skip to main content

pdf_ast/plugins/
api.rs

1use super::*;
2use crate::ast::PdfDocument;
3use crate::plugins::{loader::PluginLoader, registry::PluginRegistry};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7/// High-level plugin API for easy integration
8pub struct PluginManager {
9    registry: Arc<PluginRegistry>,
10    loader: PluginLoader,
11    execution_config: ExecutionConfig,
12}
13
14/// Plugin execution configuration
15#[derive(Debug, Clone)]
16pub struct ExecutionConfig {
17    pub parallel_execution: bool,
18    pub max_execution_time_ms: Option<u64>,
19    pub max_memory_usage_mb: Option<usize>,
20    pub abort_on_error: bool,
21    pub collect_statistics: bool,
22}
23
24impl Default for ExecutionConfig {
25    fn default() -> Self {
26        Self {
27            parallel_execution: false,
28            max_execution_time_ms: Some(30000), // 30 seconds
29            max_memory_usage_mb: Some(512),     // 512 MB
30            abort_on_error: true,
31            collect_statistics: true,
32        }
33    }
34}
35
36/// Plugin execution summary
37#[derive(Debug, Clone)]
38pub struct ExecutionSummary {
39    pub total_plugins: usize,
40    pub successful_plugins: usize,
41    pub failed_plugins: usize,
42    pub total_execution_time_ms: u64,
43    pub plugin_results: HashMap<String, PluginResult>,
44    pub statistics: HashMap<String, PluginStatistics>,
45}
46
47impl PluginManager {
48    /// Create a new plugin manager
49    pub fn new() -> Self {
50        let registry = Arc::new(PluginRegistry::new());
51        let loader = PluginLoader::new(Arc::clone(&registry));
52
53        Self {
54            registry,
55            loader,
56            execution_config: ExecutionConfig::default(),
57        }
58    }
59
60    /// Create with custom configuration
61    pub fn with_config(config: ExecutionConfig) -> Self {
62        let mut manager = Self::new();
63        manager.execution_config = config;
64        manager
65    }
66
67    /// Register a plugin
68    pub fn register_plugin(&mut self, plugin: Arc<dyn AstPlugin>) -> PluginResult {
69        self.loader.load_plugin(plugin)
70    }
71
72    /// Load plugins from configuration file
73    pub fn load_plugins_from_file<P: AsRef<std::path::Path>>(
74        &mut self,
75        path: P,
76    ) -> Result<Vec<PluginResult>, Box<dyn std::error::Error>> {
77        let content = std::fs::read_to_string(path)?;
78        let config: serde_json::Value = serde_json::from_str(&content)?;
79
80        let plugins_config = config
81            .get("plugins")
82            .and_then(|v| v.as_array())
83            .ok_or("Invalid plugin configuration")?;
84
85        let load_config = crate::plugins::loader::LoadConfig::default();
86        let configs: Vec<serde_json::Value> = plugins_config.to_vec();
87
88        Ok(self.loader.load_plugins(&configs, &load_config))
89    }
90
91    /// Add search path for plugin discovery
92    pub fn add_plugin_path<P: AsRef<std::path::Path>>(&mut self, path: P) {
93        self.loader.add_search_path(path);
94    }
95
96    /// Discover and load plugins from search paths
97    pub fn discover_and_load_plugins(&mut self) -> Vec<PluginResult> {
98        let discovered = self.loader.discover_plugins();
99        let mut results = Vec::new();
100        let load_config = crate::plugins::loader::LoadConfig::default();
101
102        for path in discovered {
103            let result = self.loader.load_from_path(&path, &load_config);
104            results.push(result);
105        }
106
107        results
108    }
109
110    /// Execute all applicable plugins on a document
111    pub fn execute_plugins(&self, document: &mut PdfDocument) -> ExecutionSummary {
112        let start_time = std::time::Instant::now();
113        let plugin_names = self.registry.list_plugins();
114        let mut summary = ExecutionSummary {
115            total_plugins: plugin_names.len(),
116            successful_plugins: 0,
117            failed_plugins: 0,
118            total_execution_time_ms: 0,
119            plugin_results: HashMap::new(),
120            statistics: HashMap::new(),
121        };
122
123        let mut pipeline =
124            PluginPipeline::new().with_parallel_execution(self.execution_config.parallel_execution);
125
126        // Add all plugins to pipeline
127        for name in &plugin_names {
128            if let Some(plugin) = self.registry.get_plugin(name) {
129                pipeline.add_plugin(plugin.clone_plugin());
130            }
131        }
132
133        // Execute pipeline
134        let results = pipeline.execute(document);
135
136        // Process results
137        for (i, result) in results.into_iter().enumerate() {
138            if let Some(plugin_name) = plugin_names.get(i) {
139                match result {
140                    PluginResult::Success | PluginResult::Modified(_) => {
141                        summary.successful_plugins += 1;
142                    }
143                    _ => {
144                        summary.failed_plugins += 1;
145                    }
146                }
147                summary.plugin_results.insert(plugin_name.clone(), result);
148            }
149        }
150
151        // Collect statistics
152        if self.execution_config.collect_statistics {
153            summary
154                .statistics
155                .insert("pipeline".to_string(), pipeline.get_statistics().clone());
156        }
157
158        summary.total_execution_time_ms = start_time.elapsed().as_millis() as u64;
159        summary
160    }
161
162    /// Execute specific plugins by name
163    pub fn execute_plugins_by_name(
164        &self,
165        document: &mut PdfDocument,
166        plugin_names: &[String],
167    ) -> ExecutionSummary {
168        let start_time = std::time::Instant::now();
169        let mut summary = ExecutionSummary {
170            total_plugins: plugin_names.len(),
171            successful_plugins: 0,
172            failed_plugins: 0,
173            total_execution_time_ms: 0,
174            plugin_results: HashMap::new(),
175            statistics: HashMap::new(),
176        };
177
178        let mut pipeline =
179            PluginPipeline::new().with_parallel_execution(self.execution_config.parallel_execution);
180
181        // Add specified plugins to pipeline
182        for name in plugin_names {
183            if let Some(plugin) = self.registry.get_plugin(name) {
184                pipeline.add_plugin(plugin.clone_plugin());
185            }
186        }
187
188        // Execute pipeline
189        let results = pipeline.execute(document);
190
191        // Process results
192        for (i, result) in results.into_iter().enumerate() {
193            if let Some(plugin_name) = plugin_names.get(i) {
194                match result {
195                    PluginResult::Success | PluginResult::Modified(_) => {
196                        summary.successful_plugins += 1;
197                    }
198                    _ => {
199                        summary.failed_plugins += 1;
200                    }
201                }
202                summary.plugin_results.insert(plugin_name.clone(), result);
203            }
204        }
205
206        summary.total_execution_time_ms = start_time.elapsed().as_millis() as u64;
207        summary
208    }
209
210    /// Execute plugins for specific node type
211    pub fn execute_plugins_for_type(
212        &self,
213        document: &mut PdfDocument,
214        node_type: &crate::ast::NodeType,
215    ) -> ExecutionSummary {
216        let start_time = std::time::Instant::now();
217        let plugins = self.registry.get_plugins_for_type(node_type);
218        let mut summary = ExecutionSummary {
219            total_plugins: plugins.len(),
220            successful_plugins: 0,
221            failed_plugins: 0,
222            total_execution_time_ms: 0,
223            plugin_results: HashMap::new(),
224            statistics: HashMap::new(),
225        };
226
227        let mut pipeline =
228            PluginPipeline::new().with_parallel_execution(self.execution_config.parallel_execution);
229
230        // Add type-specific plugins to pipeline
231        for plugin in &plugins {
232            pipeline.add_plugin(plugin.clone_plugin());
233        }
234
235        // Execute pipeline
236        let results = pipeline.execute(document);
237
238        // Process results
239        for (i, result) in results.into_iter().enumerate() {
240            if let Some(plugin) = plugins.get(i) {
241                let plugin_name = plugin.metadata().name.clone();
242                match result {
243                    PluginResult::Success | PluginResult::Modified(_) => {
244                        summary.successful_plugins += 1;
245                    }
246                    _ => {
247                        summary.failed_plugins += 1;
248                    }
249                }
250                summary.plugin_results.insert(plugin_name, result);
251            }
252        }
253
254        summary.total_execution_time_ms = start_time.elapsed().as_millis() as u64;
255        summary
256    }
257
258    /// Get plugin information
259    pub fn get_plugin_info(&self, name: &str) -> Option<PluginMetadata> {
260        self.registry.get_metadata(name)
261    }
262
263    /// List all registered plugins
264    pub fn list_plugins(&self) -> Vec<PluginMetadata> {
265        self.registry.list_metadata()
266    }
267
268    /// List plugins by tag
269    pub fn list_plugins_by_tag(&self, tag: &str) -> Vec<PluginMetadata> {
270        let plugin_names = self.registry.find_by_tag(tag);
271        plugin_names
272            .into_iter()
273            .filter_map(|name| self.registry.get_metadata(&name))
274            .collect()
275    }
276
277    /// Check plugin dependencies
278    pub fn validate_dependencies(&self) -> HashMap<String, PluginResult> {
279        let mut results = HashMap::new();
280        let plugin_names = self.registry.list_plugins();
281
282        for name in plugin_names {
283            let result = self.registry.check_dependencies(&name);
284            results.insert(name, result);
285        }
286
287        results
288    }
289
290    /// Unload a plugin
291    pub fn unload_plugin(&mut self, name: &str) -> PluginResult {
292        self.loader.unload_plugin(name)
293    }
294
295    /// Reload a plugin
296    pub fn reload_plugin(&mut self, name: &str) -> PluginResult {
297        let load_config = crate::plugins::loader::LoadConfig::default();
298        self.loader.reload_plugin(name, &load_config)
299    }
300
301    /// Set execution configuration
302    pub fn set_execution_config(&mut self, config: ExecutionConfig) {
303        self.execution_config = config;
304    }
305
306    /// Get execution configuration
307    pub fn get_execution_config(&self) -> &ExecutionConfig {
308        &self.execution_config
309    }
310
311    /// Get plugin registry
312    pub fn registry(&self) -> &PluginRegistry {
313        &self.registry
314    }
315
316    /// Get plugin loader
317    pub fn loader(&self) -> &PluginLoader {
318        &self.loader
319    }
320
321    /// Get mutable plugin loader
322    pub fn loader_mut(&mut self) -> &mut PluginLoader {
323        &mut self.loader
324    }
325}
326
327impl Default for PluginManager {
328    fn default() -> Self {
329        Self::new()
330    }
331}
332
333/// Convenience functions for plugin execution
334pub mod convenience {
335    use super::*;
336
337    /// Execute a single plugin on a document
338    pub fn execute_plugin(plugin: Arc<dyn AstPlugin>, document: &mut PdfDocument) -> PluginResult {
339        let mut context = PluginContext::new()
340            .with_document(document)
341            .with_graph(&mut document.ast);
342
343        plugin.process_document(document, &mut context)
344    }
345
346    /// Execute multiple plugins in sequence
347    pub fn execute_plugins_sequence(
348        plugins: Vec<Arc<dyn AstPlugin>>,
349        document: &mut PdfDocument,
350    ) -> Vec<PluginResult> {
351        let mut results = Vec::new();
352        let mut context = PluginContext::new()
353            .with_document(document)
354            .with_graph(&mut document.ast);
355
356        for plugin in plugins {
357            let result = plugin.process_document(document, &mut context);
358            results.push(result);
359        }
360
361        results
362    }
363
364    /// Create a simple plugin manager with built-in plugins
365    pub fn create_default_manager() -> PluginManager {
366        let mut manager = PluginManager::new();
367
368        // Load built-in plugins
369        let basic_validator = Arc::new(super::loader::BasicValidatorPlugin::new());
370        let basic_transformer = Arc::new(super::loader::BasicTransformerPlugin::new());
371
372        let _ = manager.register_plugin(basic_validator);
373        let _ = manager.register_plugin(basic_transformer);
374
375        manager
376    }
377}
378
379/// Plugin execution errors
380#[derive(Debug, Clone)]
381pub enum PluginExecutionError {
382    PluginNotFound(String),
383    DependencyError(String),
384    ExecutionTimeout,
385    MemoryLimitExceeded,
386    PluginError(String),
387}
388
389impl std::fmt::Display for PluginExecutionError {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        match self {
392            PluginExecutionError::PluginNotFound(name) => {
393                write!(f, "Plugin not found: {}", name)
394            }
395            PluginExecutionError::DependencyError(msg) => {
396                write!(f, "Dependency error: {}", msg)
397            }
398            PluginExecutionError::ExecutionTimeout => {
399                write!(f, "Plugin execution timeout")
400            }
401            PluginExecutionError::MemoryLimitExceeded => {
402                write!(f, "Plugin memory limit exceeded")
403            }
404            PluginExecutionError::PluginError(msg) => {
405                write!(f, "Plugin error: {}", msg)
406            }
407        }
408    }
409}
410
411impl std::error::Error for PluginExecutionError {}