1use crate::ast::{AstNode, NodeId, NodeType, PdfAstGraph, PdfDocument};
2use crate::validation::{SchemaConstraint, ValidationReport};
3use serde::{Deserialize, Serialize};
4use std::any::Any;
5use std::collections::HashMap;
6
7pub mod api;
8pub mod loader;
9pub mod registry;
10
11pub use api::*;
12pub use loader::*;
13pub use registry::*;
14
15#[derive(Debug, Clone)]
17pub enum PluginResult {
18 Success,
19 Modified(Vec<NodeId>),
20 Error(String),
21 Warning(String),
22}
23
24impl PluginResult {
25 pub fn is_success(&self) -> bool {
26 matches!(self, PluginResult::Success | PluginResult::Modified(_))
27 }
28
29 pub fn is_error(&self) -> bool {
30 matches!(self, PluginResult::Error(_))
31 }
32
33 pub fn get_error(&self) -> Option<&String> {
34 match self {
35 PluginResult::Error(msg) => Some(msg),
36 _ => None,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct PluginMetadata {
44 pub name: String,
45 pub version: String,
46 pub description: String,
47 pub author: String,
48 pub license: Option<String>,
49 pub homepage: Option<String>,
50 pub repository: Option<String>,
51 pub tags: Vec<String>,
52 pub supported_node_types: Vec<String>,
53 pub dependencies: Vec<String>,
54 pub api_version: String,
55}
56
57impl PluginMetadata {
58 pub fn new(name: &str, version: &str, description: &str, author: &str) -> Self {
59 Self {
60 name: name.to_string(),
61 version: version.to_string(),
62 description: description.to_string(),
63 author: author.to_string(),
64 license: None,
65 homepage: None,
66 repository: None,
67 tags: Vec::new(),
68 supported_node_types: Vec::new(),
69 dependencies: Vec::new(),
70 api_version: "0.1.0".to_string(),
71 }
72 }
73
74 pub fn with_license(mut self, license: &str) -> Self {
75 self.license = Some(license.to_string());
76 self
77 }
78
79 pub fn with_tags(mut self, tags: Vec<&str>) -> Self {
80 self.tags = tags.into_iter().map(|s| s.to_string()).collect();
81 self
82 }
83
84 pub fn with_supported_types(mut self, types: Vec<NodeType>) -> Self {
85 self.supported_node_types = types.into_iter().map(|t| format!("{:?}", t)).collect();
86 self
87 }
88}
89
90pub trait AstPlugin: Send + Sync {
92 fn metadata(&self) -> &PluginMetadata;
94
95 fn initialize(&mut self, context: &mut PluginContext) -> PluginResult {
97 let _ = context;
98 PluginResult::Success
99 }
100
101 fn process_node(&self, node: &mut AstNode, context: &mut PluginContext) -> PluginResult {
103 let _ = (node, context);
104 PluginResult::Success
105 }
106
107 fn process_document(
109 &self,
110 document: &mut PdfDocument,
111 context: &mut PluginContext,
112 ) -> PluginResult {
113 let _ = (document, context);
114 PluginResult::Success
115 }
116
117 fn finalize(&self, context: &mut PluginContext) -> PluginResult {
119 let _ = context;
120 PluginResult::Success
121 }
122
123 fn config_schema(&self) -> Option<serde_json::Value> {
125 None
126 }
127
128 fn set_config(&mut self, config: serde_json::Value) -> PluginResult {
130 let _ = config;
131 PluginResult::Success
132 }
133
134 fn can_process_node_type(&self, node_type: &NodeType) -> bool {
136 let node_type_str = format!("{:?}", node_type);
137 self.metadata().supported_node_types.is_empty()
138 || self
139 .metadata()
140 .supported_node_types
141 .contains(&node_type_str)
142 }
143
144 fn capabilities(&self) -> PluginCapabilities {
146 PluginCapabilities::default()
147 }
148
149 fn clone_plugin(&self) -> Box<dyn AstPlugin>;
151}
152
153#[derive(Debug, Clone, Default)]
155pub struct PluginCapabilities {
156 pub can_modify_nodes: bool,
157 pub can_add_nodes: bool,
158 pub can_remove_nodes: bool,
159 pub can_validate: bool,
160 pub can_transform: bool,
161 pub requires_document_context: bool,
162 pub thread_safe: bool,
163}
164
165pub struct PluginContext {
167 pub document: Option<*const PdfDocument>,
168 pub current_node: Option<NodeId>,
169 pub graph: Option<*mut PdfAstGraph>,
170 pub config: HashMap<String, serde_json::Value>,
171 pub shared_data: HashMap<String, Box<dyn Any + Send + Sync>>,
172 pub statistics: PluginStatistics,
173}
174
175impl PluginContext {
176 pub fn new() -> Self {
177 Self {
178 document: None,
179 current_node: None,
180 graph: None,
181 config: HashMap::new(),
182 shared_data: HashMap::new(),
183 statistics: PluginStatistics::default(),
184 }
185 }
186
187 pub fn with_document(mut self, document: &PdfDocument) -> Self {
188 self.document = Some(document as *const PdfDocument);
189 self
190 }
191
192 pub fn with_graph(mut self, graph: &mut PdfAstGraph) -> Self {
193 self.graph = Some(graph as *mut PdfAstGraph);
194 self
195 }
196
197 pub fn set_config(&mut self, key: String, value: serde_json::Value) {
198 self.config.insert(key, value);
199 }
200
201 pub fn get_config(&self, key: &str) -> Option<&serde_json::Value> {
202 self.config.get(key)
203 }
204
205 pub fn set_shared_data<T: Any + Send + Sync>(&mut self, key: String, data: T) {
206 self.shared_data.insert(key, Box::new(data));
207 }
208
209 pub fn get_shared_data<T: Any + Send + Sync>(&self, key: &str) -> Option<&T> {
210 self.shared_data.get(key)?.downcast_ref::<T>()
211 }
212
213 pub fn get_document(&self) -> Option<&PdfDocument> {
214 self.document.map(|ptr| unsafe { &*ptr })
215 }
216
217 pub fn get_graph_mut(&mut self) -> Option<&mut PdfAstGraph> {
218 self.graph.map(|ptr| unsafe { &mut *ptr })
219 }
220
221 pub fn set_data(&mut self, key: &str, value: String) {
222 self.set_config(key.to_string(), serde_json::Value::String(value));
223 }
224
225 pub fn add_warning(&mut self, message: &str) {
226 self.statistics.warnings.push(message.to_string());
227 }
228
229 pub fn add_error(&mut self, message: &str) {
230 self.statistics.errors.push(message.to_string());
231 }
232
233 pub fn add_info(&mut self, message: String) {
234 self.statistics.info_messages.push(message);
235 }
236}
237
238unsafe impl Send for PluginContext {}
239unsafe impl Sync for PluginContext {}
240
241impl Default for PluginContext {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247#[derive(Debug, Clone, Default)]
249pub struct PluginStatistics {
250 pub nodes_processed: usize,
251 pub nodes_modified: usize,
252 pub nodes_added: usize,
253 pub nodes_removed: usize,
254 pub execution_time_ms: u64,
255 pub memory_used_bytes: usize,
256 pub errors: Vec<String>,
257 pub warnings: Vec<String>,
258 pub info_messages: Vec<String>,
259}
260
261pub trait NodeTransformerPlugin: AstPlugin {
264 fn transform_node(&self, node: &mut AstNode, context: &mut PluginContext) -> PluginResult;
265}
266
267pub trait ValidatorPlugin: AstPlugin {
269 fn validate_document(&self, document: &PdfDocument) -> ValidationReport;
270 fn get_constraints(&self) -> Vec<Box<dyn SchemaConstraint>>;
271}
272
273pub trait FilterPlugin: AstPlugin {
275 fn filter_content(
276 &self,
277 content: &[u8],
278 filter_params: &HashMap<String, String>,
279 ) -> Result<Vec<u8>, String>;
280 fn get_supported_filters(&self) -> Vec<String>;
281}
282
283pub trait AnalyzerPlugin: AstPlugin {
285 fn analyze_document(&self, document: &PdfDocument) -> AnalysisResult;
286 fn get_analysis_types(&self) -> Vec<String>;
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct AnalysisResult {
292 pub analyzer_name: String,
293 pub analysis_type: String,
294 pub timestamp: chrono::DateTime<chrono::Utc>,
295 pub data: serde_json::Value,
296 pub metadata: HashMap<String, String>,
297}
298
299impl AnalysisResult {
300 pub fn new(analyzer_name: &str, analysis_type: &str, data: serde_json::Value) -> Self {
301 Self {
302 analyzer_name: analyzer_name.to_string(),
303 analysis_type: analysis_type.to_string(),
304 timestamp: chrono::Utc::now(),
305 data,
306 metadata: HashMap::new(),
307 }
308 }
309}
310
311pub struct PluginPipeline {
313 plugins: Vec<Box<dyn AstPlugin>>,
314 context: PluginContext,
315 parallel_execution: bool,
316}
317
318impl PluginPipeline {
319 pub fn new() -> Self {
320 Self {
321 plugins: Vec::new(),
322 context: PluginContext::new(),
323 parallel_execution: false,
324 }
325 }
326
327 pub fn add_plugin(&mut self, plugin: Box<dyn AstPlugin>) {
328 self.plugins.push(plugin);
329 }
330
331 pub fn with_parallel_execution(mut self, parallel: bool) -> Self {
332 self.parallel_execution = parallel;
333 self
334 }
335
336 pub fn execute(&mut self, document: &mut PdfDocument) -> Vec<PluginResult> {
337 let mut results = Vec::new();
338
339 self.context = PluginContext::new()
341 .with_document(document)
342 .with_graph(&mut document.ast);
343
344 for plugin in &mut self.plugins {
346 let result = plugin.initialize(&mut self.context);
347 results.push(result);
348 }
349
350 if self.parallel_execution {
352 self.execute_sequential(document, &mut results);
355 } else {
356 self.execute_sequential(document, &mut results);
357 }
358
359 for plugin in &mut self.plugins {
361 let result = plugin.finalize(&mut self.context);
362 results.push(result);
363 }
364
365 results
366 }
367
368 fn execute_sequential(&mut self, document: &mut PdfDocument, results: &mut Vec<PluginResult>) {
369 for plugin in &self.plugins {
370 let doc_result = plugin.process_document(document, &mut self.context);
372 results.push(doc_result);
373
374 if plugin.capabilities().can_modify_nodes {
376 let node_ids: Vec<NodeId> =
377 document.ast.get_all_nodes().iter().map(|n| n.id).collect();
378
379 for node_id in node_ids {
380 if let Some(mut node) = document.ast.get_node(node_id).cloned() {
381 if plugin.can_process_node_type(&node.node_type) {
382 let node_result = plugin.process_node(&mut node, &mut self.context);
383 results.push(node_result);
384
385 if let Some(graph_node) = document.ast.get_node_mut(node_id) {
387 *graph_node = node;
388 }
389 }
390 }
391 }
392 }
393 }
394 }
395
396 pub fn get_statistics(&self) -> &PluginStatistics {
397 &self.context.statistics
398 }
399}
400
401impl Default for PluginPipeline {
402 fn default() -> Self {
403 Self::new()
404 }
405}