1use std::collections::HashMap;
7use std::time::{Duration, Instant};
8use serde_json::Value;
9use thiserror::Error;
10
11pub struct EnhancedPluginLoader {
13 npm_loader: NPMPluginLoader,
14 native_loader: NativePluginLoader,
15 plugin_registry: PluginRegistry,
16 config_manager: PluginConfigManager,
17 performance_monitor: PluginPerformanceMonitor,
18 cache: PluginCache,
19}
20
21impl EnhancedPluginLoader {
22 pub fn new() -> Self {
24 Self {
25 npm_loader: NPMPluginLoader::new(),
26 native_loader: NativePluginLoader::new(),
27 plugin_registry: PluginRegistry::new(),
28 config_manager: PluginConfigManager::new(),
29 performance_monitor: PluginPerformanceMonitor::new(),
30 cache: PluginCache::new(),
31 }
32 }
33
34 pub fn load_npm_plugin(&mut self, plugin_name: &str, config: &PluginConfig) -> Result<PluginInstance, PluginError> {
36 let start_time = Instant::now();
37
38 if let Some(cached) = self.cache.get_plugin(plugin_name, config.version.as_deref()) {
40 return Ok(cached.clone());
41 }
42
43 let plugin_info = self.npm_loader.install_plugin(plugin_name, config.version.as_deref())?;
45
46 let instance = PluginInstance {
48 name: plugin_info.name.clone(),
49 version: plugin_info.version.clone(),
50 info: plugin_info,
51 config: config.clone(),
52 execution_mode: config.execution_mode.clone(),
53 security_level: config.security_level.clone(),
54 };
55
56 self.cache.cache_plugin(plugin_name, config.version.as_deref(), &instance);
58
59 let loading_time = start_time.elapsed();
61 self.performance_monitor.record_loading(&instance, loading_time);
62
63 Ok(instance)
64 }
65
66 pub fn load_native_plugin(&mut self, plugin_path: &str, config: &PluginConfig) -> Result<PluginInstance, PluginError> {
68 let start_time = Instant::now();
69
70 let plugin_info = self.native_loader.load_plugin(plugin_path)?;
72
73 let instance = PluginInstance {
75 name: plugin_info.name.clone(),
76 version: plugin_info.version.clone(),
77 info: plugin_info,
78 config: config.clone(),
79 execution_mode: ExecutionMode::Native,
80 security_level: config.security_level.clone(),
81 };
82
83 let loading_time = start_time.elapsed();
85 self.performance_monitor.record_loading(&instance, loading_time);
86
87 Ok(instance)
88 }
89
90 pub fn execute_plugin(&mut self, plugin: &PluginInstance, css: &str) -> Result<PluginResult, PluginError> {
92 let start_time = Instant::now();
93
94 let result = match plugin.execution_mode {
96 ExecutionMode::Native => self.execute_native_plugin(plugin, css)?,
97 ExecutionMode::NPM => self.execute_npm_plugin(plugin, css)?,
98 ExecutionMode::WebAssembly => self.execute_wasm_plugin(plugin, css)?,
99 ExecutionMode::Sandboxed => self.execute_sandboxed_plugin(plugin, css)?,
100 };
101
102 let execution_time = start_time.elapsed();
104 let metrics = PluginMetrics {
105 execution_time,
106 memory_usage: result.memory_usage,
107 output_size: result.css.len(),
108 success: result.success,
109 total_executions: 1,
110 total_time: execution_time,
111 avg_time: execution_time,
112 max_time: execution_time,
113 min_time: execution_time,
114 loading_time: Duration::from_secs(0),
115 };
116
117 self.performance_monitor.record_execution(plugin, execution_time, metrics);
118
119 Ok(result)
120 }
121
122 pub fn execute_plugin_pipeline(&mut self, plugins: &[PluginInstance], css: &str) -> Result<String, PluginError> {
124 let mut processed_css = css.to_string();
125
126 for plugin in plugins {
127 let result = self.execute_plugin(plugin, &processed_css)?;
128 processed_css = result.css;
129 }
130
131 Ok(processed_css)
132 }
133
134 pub fn execute_plugins_parallel(&self, plugins: &[PluginInstance], css: &str) -> Result<String, PluginError> {
136 use rayon::prelude::*;
137
138 let chunks: Vec<&[PluginInstance]> = plugins.chunks(4).collect();
140
141 let results: Result<Vec<String>, PluginError> = chunks
142 .par_iter()
143 .map(|chunk| self.execute_plugin_chunk_parallel(chunk, css))
144 .collect();
145
146 let results = results?;
147 Ok(results.join("\n"))
148 }
149
150 fn execute_plugin_chunk(&mut self, plugins: &[PluginInstance], css: &str) -> Result<String, PluginError> {
152 let mut processed_css = css.to_string();
153
154 for plugin in plugins {
155 let result = self.execute_plugin(plugin, &processed_css)?;
156 processed_css = result.css;
157 }
158
159 Ok(processed_css)
160 }
161
162 fn execute_plugin_chunk_parallel(&self, plugins: &[PluginInstance], css: &str) -> Result<String, PluginError> {
164 let mut processed_css = css.to_string();
165
166 for plugin in plugins {
167 processed_css = format!("/* Processed by {} */\n{}", plugin.name, processed_css);
169 }
170
171 Ok(processed_css)
172 }
173
174 fn execute_native_plugin(&self, _plugin: &PluginInstance, css: &str) -> Result<PluginResult, PluginError> {
176 Ok(PluginResult {
178 css: css.to_string(),
179 success: true,
180 memory_usage: 0,
181 warnings: Vec::new(),
182 errors: Vec::new(),
183 })
184 }
185
186 fn execute_npm_plugin(&self, plugin: &PluginInstance, css: &str) -> Result<PluginResult, PluginError> {
188 self.npm_loader.execute_plugin(&plugin.info, css, &plugin.config.options)
189 }
190
191 fn execute_wasm_plugin(&self, _plugin: &PluginInstance, css: &str) -> Result<PluginResult, PluginError> {
193 Ok(PluginResult {
195 css: css.to_string(),
196 success: true,
197 memory_usage: 0,
198 warnings: Vec::new(),
199 errors: Vec::new(),
200 })
201 }
202
203 fn execute_sandboxed_plugin(&self, plugin: &PluginInstance, css: &str) -> Result<PluginResult, PluginError> {
205 let sandbox = PluginSandbox::new(plugin.security_level.clone());
207
208 sandbox.execute_safely(&plugin.info.name, css, &plugin.config.options)
210 }
211
212 pub fn get_statistics(&self) -> PluginStatistics {
214 self.performance_monitor.get_statistics()
215 }
216
217 pub fn discover_plugins(&self, search_paths: &[String]) -> Result<Vec<PluginInfo>, PluginError> {
219 let mut plugins = Vec::new();
220
221 for path in search_paths {
222 let discovered = self.npm_loader.discover_plugins(path)?;
223 plugins.extend(discovered);
224 }
225
226 Ok(plugins)
227 }
228}
229
230pub struct NPMPluginLoader {
232 npm_client: NPMClient,
233 cache: PluginCache,
234 sandbox: PluginSandbox,
235}
236
237impl NPMPluginLoader {
238 pub fn new() -> Self {
240 Self {
241 npm_client: NPMClient::new(),
242 cache: PluginCache::new(),
243 sandbox: PluginSandbox::new(SecurityLevel::Sandboxed),
244 }
245 }
246
247 pub fn install_plugin(&self, plugin_name: &str, version: Option<&str>) -> Result<PluginInfo, PluginError> {
249 if let Some(cached) = self.cache.get_plugin(plugin_name, version) {
251 return Ok(cached.info.clone());
252 }
253
254 let plugin_info = self.npm_client.install(plugin_name, version)?;
256
257 for dependency in &plugin_info.dependencies {
259 self.install_plugin(&dependency.name, Some(&dependency.version))?;
260 }
261
262 self.validate_plugin(&plugin_info)?;
264
265 Ok(plugin_info)
266 }
267
268 pub fn resolve_dependencies(&self, plugin_name: &str) -> Result<Vec<PluginDependency>, PluginError> {
270 self.npm_client.get_dependencies(plugin_name)
271 }
272
273 pub fn execute_plugin(&self, plugin_info: &PluginInfo, css: &str, config: &HashMap<String, Value>) -> Result<PluginResult, PluginError> {
275 self.sandbox.execute_safely(&plugin_info.name, css, config)
276 }
277
278 pub fn discover_plugins(&self, _path: &str) -> Result<Vec<PluginInfo>, PluginError> {
280 let mut plugins = Vec::new();
281
282 let package_files = self.find_package_files(_path)?;
284
285 for package_file in package_files {
286 let plugin_info = self.parse_package_json(&package_file)?;
287 if self.is_postcss_plugin(&plugin_info) {
288 plugins.push(plugin_info);
289 }
290 }
291
292 Ok(plugins)
293 }
294
295 fn find_package_files(&self, _path: &str) -> Result<Vec<String>, PluginError> {
297 Ok(Vec::new())
299 }
300
301 fn parse_package_json(&self, _file_path: &str) -> Result<PluginInfo, PluginError> {
303 Ok(PluginInfo {
305 name: "example-plugin".to_string(),
306 version: "1.0.0".to_string(),
307 description: "Example plugin".to_string(),
308 author: "Example Author".to_string(),
309 license: "MIT".to_string(),
310 repository: None,
311 dependencies: Vec::new(),
312 postcss_version: "8.0.0".to_string(),
313 node_version: Some("16.0.0".to_string()),
314 rust_version: None,
315 capabilities: vec![PluginCapability::Transform],
316 })
317 }
318
319 fn is_postcss_plugin(&self, plugin_info: &PluginInfo) -> bool {
321 plugin_info.capabilities.contains(&PluginCapability::Transform) ||
322 plugin_info.name.contains("postcss") ||
323 plugin_info.dependencies.iter().any(|dep| dep.name == "postcss")
324 }
325
326 fn validate_plugin(&self, plugin_info: &PluginInfo) -> Result<(), PluginError> {
328 if plugin_info.name.is_empty() {
330 return Err(PluginError::PluginNotFound { name: "".to_string() });
331 }
332
333 if plugin_info.postcss_version != "8.0.0" {
335 return Err(PluginError::CompatibilityError {
336 error: "Incompatible PostCSS version".to_string()
337 });
338 }
339
340 Ok(())
341 }
342}
343
344pub struct NativePluginLoader;
346
347impl NativePluginLoader {
348 pub fn new() -> Self {
350 Self
351 }
352
353 pub fn load_plugin(&self, plugin_path: &str) -> Result<PluginInfo, PluginError> {
355 Ok(PluginInfo {
357 name: plugin_path.to_string(),
358 version: "1.0.0".to_string(),
359 description: "Native plugin".to_string(),
360 author: "Native Author".to_string(),
361 license: "MIT".to_string(),
362 repository: None,
363 dependencies: Vec::new(),
364 postcss_version: "8.0.0".to_string(),
365 node_version: None,
366 rust_version: Some("1.70.0".to_string()),
367 capabilities: vec![PluginCapability::Transform],
368 })
369 }
370}
371
372pub struct PluginRegistry {
374 plugins: HashMap<String, PluginInfo>,
375 categories: HashMap<String, Vec<String>>,
376 compatibility_matrix: HashMap<String, Vec<String>>,
377}
378
379impl PluginRegistry {
380 pub fn new() -> Self {
382 Self {
383 plugins: HashMap::new(),
384 categories: HashMap::new(),
385 compatibility_matrix: HashMap::new(),
386 }
387 }
388
389 pub fn register_plugin(&mut self, plugin: PluginInfo) -> Result<(), PluginError> {
391 if plugin.name.is_empty() {
393 return Err(PluginError::PluginNotFound { name: "".to_string() });
394 }
395
396 if self.plugins.contains_key(&plugin.name) {
398 return Err(PluginError::CompatibilityError {
399 error: "Plugin already registered".to_string()
400 });
401 }
402
403 self.plugins.insert(plugin.name.clone(), plugin);
405
406 Ok(())
407 }
408
409 pub fn find_compatible_plugins(&self, requirements: &PluginRequirements) -> Vec<PluginInfo> {
411 let mut compatible = Vec::new();
412
413 for (_name, plugin) in &self.plugins {
414 if self.is_compatible(plugin, requirements) {
415 compatible.push(plugin.clone());
416 }
417 }
418
419 compatible
420 }
421
422 fn is_compatible(&self, plugin: &PluginInfo, requirements: &PluginRequirements) -> bool {
424 for required_capability in &requirements.capabilities {
426 if !plugin.capabilities.contains(required_capability) {
427 return false;
428 }
429 }
430
431 if plugin.postcss_version != requirements.postcss_version {
433 return false;
434 }
435
436 true
437 }
438
439 pub fn get_plugin_dependencies(&self, plugin_name: &str) -> Result<Vec<String>, PluginError> {
441 if let Some(plugin) = self.plugins.get(plugin_name) {
442 Ok(plugin.dependencies.iter().map(|dep| dep.name.clone()).collect())
443 } else {
444 Err(PluginError::PluginNotFound { name: plugin_name.to_string() })
445 }
446 }
447}
448
449pub struct PluginConfigManager {
451 configs: HashMap<String, PluginConfig>,
452 templates: HashMap<String, ConfigTemplate>,
453}
454
455impl PluginConfigManager {
456 pub fn new() -> Self {
458 Self {
459 configs: HashMap::new(),
460 templates: HashMap::new(),
461 }
462 }
463
464 pub fn load_config(&self, plugin_name: &str, _config_path: &str) -> Result<PluginConfig, PluginError> {
466 Ok(PluginConfig {
468 name: plugin_name.to_string(),
469 version: None,
470 options: HashMap::new(),
471 dependencies: Vec::new(),
472 execution_mode: ExecutionMode::NPM,
473 security_level: SecurityLevel::Sandboxed,
474 })
475 }
476
477 pub fn generate_template(&self, plugin_name: &str) -> Result<ConfigTemplate, PluginError> {
479 Ok(ConfigTemplate {
481 name: plugin_name.to_string(),
482 template: "{}".to_string(),
483 documentation: "Example configuration".to_string(),
484 })
485 }
486
487 pub fn validate_config(&self, config: &PluginConfig) -> Result<Vec<ConfigValidationError>, PluginError> {
489 let mut errors = Vec::new();
490
491 if config.name.is_empty() {
493 errors.push(ConfigValidationError::MissingField("name".to_string()));
494 }
495
496 for dependency in &config.dependencies {
498 if !self.is_dependency_available(&dependency.name) {
499 errors.push(ConfigValidationError::MissingDependency(dependency.name.clone()));
500 }
501 }
502
503 Ok(errors)
504 }
505
506 fn is_dependency_available(&self, _dependency_name: &str) -> bool {
508 true
510 }
511}
512
513pub struct PluginPerformanceMonitor {
515 metrics: HashMap<String, PluginMetrics>,
516 alerts: Vec<PerformanceAlert>,
517}
518
519impl PluginPerformanceMonitor {
520 pub fn new() -> Self {
522 Self {
523 metrics: HashMap::new(),
524 alerts: Vec::new(),
525 }
526 }
527
528 pub fn record_execution(&mut self, plugin: &PluginInstance, duration: Duration, metrics: PluginMetrics) {
530 let plugin_name = &plugin.name;
531
532 if let Some(existing_metrics) = self.metrics.get_mut(plugin_name) {
533 existing_metrics.total_executions += 1;
534 existing_metrics.total_time += duration;
535 existing_metrics.avg_time = existing_metrics.total_time / existing_metrics.total_executions;
536 existing_metrics.max_time = existing_metrics.max_time.max(duration);
537 existing_metrics.min_time = existing_metrics.min_time.min(duration);
538 } else {
539 self.metrics.insert(plugin_name.clone(), metrics);
540 }
541
542 self.check_performance_alerts(plugin_name, duration);
544 }
545
546 pub fn record_loading(&mut self, plugin: &PluginInstance, duration: Duration) {
548 let plugin_name = &plugin.name;
550
551 if let Some(existing_metrics) = self.metrics.get_mut(plugin_name) {
552 existing_metrics.loading_time = duration;
553 }
554 }
555
556 fn check_performance_alerts(&mut self, plugin_name: &str, duration: Duration) {
558 if duration > Duration::from_millis(1000) {
559 self.alerts.push(PerformanceAlert {
560 plugin: plugin_name.to_string(),
561 issue: "Slow execution".to_string(),
562 duration,
563 timestamp: std::time::SystemTime::now(),
564 });
565 }
566 }
567
568 pub fn get_statistics(&self) -> PluginStatistics {
570 PluginStatistics {
571 total_plugins: self.metrics.len(),
572 total_executions: self.metrics.values().map(|m| m.total_executions).sum(),
573 average_execution_time: self.metrics.values().map(|m| m.avg_time).sum::<Duration>() / self.metrics.len().max(1) as u32,
574 performance_alerts: self.alerts.len(),
575 }
576 }
577}
578
579pub struct PluginSandbox {
581 security_policy: SecurityPolicy,
582 resource_limits: ResourceLimits,
583 allowed_apis: std::collections::HashSet<String>,
584}
585
586impl PluginSandbox {
587 pub fn new(security_level: SecurityLevel) -> Self {
589 Self {
590 security_policy: SecurityPolicy::new(security_level),
591 resource_limits: ResourceLimits::new(),
592 allowed_apis: std::collections::HashSet::new(),
593 }
594 }
595
596 pub fn execute_safely(&self, plugin_name: &str, css: &str, config: &HashMap<String, Value>) -> Result<PluginResult, PluginError> {
598 let sandbox_env = self.create_sandbox_environment();
600
601 self.apply_resource_limits(&sandbox_env);
603
604 let result = self.execute_with_constraints(plugin_name, css, config)?;
606
607 self.validate_output(&result.css)?;
609
610 Ok(result)
611 }
612
613 fn create_sandbox_environment(&self) -> SandboxEnvironment {
615 SandboxEnvironment {
616 memory_limit: self.resource_limits.max_memory,
617 execution_time_limit: self.resource_limits.max_execution_time,
618 allowed_apis: self.allowed_apis.clone(),
619 }
620 }
621
622 fn apply_resource_limits(&self, _env: &SandboxEnvironment) {
624 }
626
627 fn execute_with_constraints(&self, _plugin_name: &str, css: &str, _config: &HashMap<String, Value>) -> Result<PluginResult, PluginError> {
629 Ok(PluginResult {
631 css: css.to_string(),
632 success: true,
633 memory_usage: 0,
634 warnings: Vec::new(),
635 errors: Vec::new(),
636 })
637 }
638
639 fn validate_output(&self, output: &str) -> Result<(), PluginError> {
641 if output.contains("<script>") || output.contains("javascript:") {
643 return Err(PluginError::SecurityViolation);
644 }
645
646 if output.len() > self.resource_limits.max_output_size {
648 return Err(PluginError::ResourceLimitExceeded);
649 }
650
651 Ok(())
652 }
653}
654
655pub struct PluginCache {
657 plugin_cache: HashMap<String, PluginInstance>,
658 execution_cache: HashMap<String, String>,
659 dependency_cache: HashMap<String, Vec<String>>,
660}
661
662impl PluginCache {
663 pub fn new() -> Self {
665 Self {
666 plugin_cache: HashMap::new(),
667 execution_cache: HashMap::new(),
668 dependency_cache: HashMap::new(),
669 }
670 }
671
672 pub fn cache_plugin(&mut self, name: &str, version: Option<&str>, plugin: &PluginInstance) {
674 let key = format!("{}:{}", name, version.unwrap_or("latest"));
675 self.plugin_cache.insert(key, plugin.clone());
676 }
677
678 pub fn get_plugin(&self, name: &str, version: Option<&str>) -> Option<&PluginInstance> {
680 let key = format!("{}:{}", name, version.unwrap_or("latest"));
681 self.plugin_cache.get(&key)
682 }
683
684 pub fn cache_execution(&mut self, key: &str, result: &str) {
686 self.execution_cache.insert(key.to_string(), result.to_string());
687 }
688
689 pub fn get_cached_execution(&self, key: &str) -> Option<&String> {
691 self.execution_cache.get(key)
692 }
693}
694
695pub struct NPMClient;
697
698impl NPMClient {
699 pub fn new() -> Self {
701 Self
702 }
703
704 pub fn install(&self, package_name: &str, version: Option<&str>) -> Result<PluginInfo, PluginError> {
706 Ok(PluginInfo {
708 name: package_name.to_string(),
709 version: version.unwrap_or("latest").to_string(),
710 description: "NPM plugin".to_string(),
711 author: "NPM Author".to_string(),
712 license: "MIT".to_string(),
713 repository: None,
714 dependencies: Vec::new(),
715 postcss_version: "8.0.0".to_string(),
716 node_version: Some("16.0.0".to_string()),
717 rust_version: None,
718 capabilities: vec![PluginCapability::Transform],
719 })
720 }
721
722 pub fn get_dependencies(&self, _package_name: &str) -> Result<Vec<PluginDependency>, PluginError> {
724 Ok(Vec::new())
726 }
727}
728
729#[derive(Debug, Clone)]
733pub struct PluginConfig {
734 pub name: String,
735 pub version: Option<String>,
736 pub options: HashMap<String, Value>,
737 pub dependencies: Vec<PluginDependency>,
738 pub execution_mode: ExecutionMode,
739 pub security_level: SecurityLevel,
740}
741
742#[derive(Debug, Clone)]
744pub enum ExecutionMode {
745 Native,
746 NPM,
747 WebAssembly,
748 Sandboxed,
749}
750
751#[derive(Debug, Clone)]
753pub enum SecurityLevel {
754 Trusted,
755 Sandboxed,
756 Restricted,
757}
758
759#[derive(Debug, Clone)]
761pub struct PluginDependency {
762 pub name: String,
763 pub version: String,
764 pub optional: bool,
765}
766
767#[derive(Debug, Clone)]
769pub struct PluginInfo {
770 pub name: String,
771 pub version: String,
772 pub description: String,
773 pub author: String,
774 pub license: String,
775 pub repository: Option<String>,
776 pub dependencies: Vec<PluginDependency>,
777 pub postcss_version: String,
778 pub node_version: Option<String>,
779 pub rust_version: Option<String>,
780 pub capabilities: Vec<PluginCapability>,
781}
782
783#[derive(Debug, Clone, PartialEq)]
785pub enum PluginCapability {
786 Transform,
787 Parse,
788 Stringify,
789 SourceMap,
790 Optimization,
791 Linting,
792 Validation,
793}
794
795#[derive(Debug, Clone)]
797pub struct PluginInstance {
798 pub name: String,
799 pub version: String,
800 pub info: PluginInfo,
801 pub config: PluginConfig,
802 pub execution_mode: ExecutionMode,
803 pub security_level: SecurityLevel,
804}
805
806#[derive(Debug, Clone)]
808pub struct PluginResult {
809 pub css: String,
810 pub success: bool,
811 pub memory_usage: usize,
812 pub warnings: Vec<String>,
813 pub errors: Vec<String>,
814}
815
816#[derive(Debug, Clone)]
818pub struct PluginMetrics {
819 pub execution_time: Duration,
820 pub memory_usage: usize,
821 pub output_size: usize,
822 pub success: bool,
823 pub total_executions: u32,
824 pub total_time: Duration,
825 pub avg_time: Duration,
826 pub max_time: Duration,
827 pub min_time: Duration,
828 pub loading_time: Duration,
829}
830
831impl PluginMetrics {
832 pub fn new() -> Self {
834 Self {
835 execution_time: Duration::from_secs(0),
836 memory_usage: 0,
837 output_size: 0,
838 success: true,
839 total_executions: 0,
840 total_time: Duration::from_secs(0),
841 avg_time: Duration::from_secs(0),
842 max_time: Duration::from_secs(0),
843 min_time: Duration::from_secs(0),
844 loading_time: Duration::from_secs(0),
845 }
846 }
847}
848
849#[derive(Debug, Clone)]
851pub struct PluginRequirements {
852 pub capabilities: Vec<PluginCapability>,
853 pub postcss_version: String,
854 pub node_version: Option<String>,
855 pub rust_version: Option<String>,
856}
857
858#[derive(Debug, Clone)]
860pub struct ConfigTemplate {
861 pub name: String,
862 pub template: String,
863 pub documentation: String,
864}
865
866#[derive(Debug, Clone)]
868pub enum ConfigValidationError {
869 MissingField(String),
870 MissingDependency(String),
871 InvalidValue(String),
872}
873
874#[derive(Debug, Clone)]
876pub struct PerformanceAlert {
877 pub plugin: String,
878 pub issue: String,
879 pub duration: Duration,
880 pub timestamp: std::time::SystemTime,
881}
882
883#[derive(Debug, Clone)]
885pub struct PluginStatistics {
886 pub total_plugins: usize,
887 pub total_executions: u32,
888 pub average_execution_time: Duration,
889 pub performance_alerts: usize,
890}
891
892#[derive(Debug, Clone)]
894pub struct SecurityPolicy {
895 pub level: SecurityLevel,
896 pub allowed_apis: std::collections::HashSet<String>,
897 pub resource_limits: ResourceLimits,
898}
899
900impl SecurityPolicy {
901 pub fn new(level: SecurityLevel) -> Self {
903 Self {
904 level,
905 allowed_apis: std::collections::HashSet::new(),
906 resource_limits: ResourceLimits::new(),
907 }
908 }
909}
910
911#[derive(Debug, Clone)]
913pub struct ResourceLimits {
914 pub max_memory: usize,
915 pub max_execution_time: Duration,
916 pub max_output_size: usize,
917}
918
919impl ResourceLimits {
920 pub fn new() -> Self {
922 Self {
923 max_memory: 100 * 1024 * 1024, max_execution_time: Duration::from_secs(30),
925 max_output_size: 10 * 1024 * 1024, }
927 }
928}
929
930#[derive(Debug, Clone)]
932pub struct SandboxEnvironment {
933 pub memory_limit: usize,
934 pub execution_time_limit: Duration,
935 pub allowed_apis: std::collections::HashSet<String>,
936}
937
938#[derive(Debug, Error)]
940pub enum PluginError {
941 #[error("Plugin not found: {name}")]
942 PluginNotFound { name: String },
943
944 #[error("Plugin installation failed: {error}")]
945 InstallationFailed { error: String },
946
947 #[error("Plugin execution failed: {error}")]
948 ExecutionFailed { error: String },
949
950 #[error("Security violation detected")]
951 SecurityViolation,
952
953 #[error("Resource limit exceeded")]
954 ResourceLimitExceeded,
955
956 #[error("Plugin compatibility error: {error}")]
957 CompatibilityError { error: String },
958
959 #[error("Configuration validation failed: {errors:?}")]
960 ConfigValidationFailed { errors: Vec<ConfigValidationError> },
961}
962
963#[cfg(test)]
964mod tests {
965 use super::*;
966
967 #[test]
968 fn test_enhanced_plugin_loader_creation() {
969 let loader = EnhancedPluginLoader::new();
970 assert!(loader.get_statistics().total_plugins == 0);
971 }
972
973 #[test]
974 fn test_plugin_config_creation() {
975 let config = PluginConfig {
976 name: "test-plugin".to_string(),
977 version: Some("1.0.0".to_string()),
978 options: HashMap::new(),
979 dependencies: Vec::new(),
980 execution_mode: ExecutionMode::NPM,
981 security_level: SecurityLevel::Sandboxed,
982 };
983
984 assert_eq!(config.name, "test-plugin");
985 assert_eq!(config.version, Some("1.0.0".to_string()));
986 }
987
988 #[test]
989 fn test_plugin_metrics_creation() {
990 let metrics = PluginMetrics::new();
991 assert_eq!(metrics.total_executions, 0);
992 assert_eq!(metrics.memory_usage, 0);
993 }
994
995 #[test]
996 fn test_plugin_registry_operations() {
997 let mut registry = PluginRegistry::new();
998
999 let plugin = PluginInfo {
1000 name: "test-plugin".to_string(),
1001 version: "1.0.0".to_string(),
1002 description: "Test plugin".to_string(),
1003 author: "Test Author".to_string(),
1004 license: "MIT".to_string(),
1005 repository: None,
1006 dependencies: Vec::new(),
1007 postcss_version: "8.0.0".to_string(),
1008 node_version: None,
1009 rust_version: None,
1010 capabilities: vec![PluginCapability::Transform],
1011 };
1012
1013 let result = registry.register_plugin(plugin);
1014 assert!(result.is_ok());
1015 }
1016
1017 #[test]
1018 fn test_plugin_cache_operations() {
1019 let mut cache = PluginCache::new();
1020
1021 let plugin = PluginInstance {
1022 name: "test-plugin".to_string(),
1023 version: "1.0.0".to_string(),
1024 info: PluginInfo {
1025 name: "test-plugin".to_string(),
1026 version: "1.0.0".to_string(),
1027 description: "Test plugin".to_string(),
1028 author: "Test Author".to_string(),
1029 license: "MIT".to_string(),
1030 repository: None,
1031 dependencies: Vec::new(),
1032 postcss_version: "8.0.0".to_string(),
1033 node_version: None,
1034 rust_version: None,
1035 capabilities: vec![PluginCapability::Transform],
1036 },
1037 config: PluginConfig {
1038 name: "test-plugin".to_string(),
1039 version: Some("1.0.0".to_string()),
1040 options: HashMap::new(),
1041 dependencies: Vec::new(),
1042 execution_mode: ExecutionMode::NPM,
1043 security_level: SecurityLevel::Sandboxed,
1044 },
1045 execution_mode: ExecutionMode::NPM,
1046 security_level: SecurityLevel::Sandboxed,
1047 };
1048
1049 cache.cache_plugin("test-plugin", Some("1.0.0"), &plugin);
1050 let cached = cache.get_plugin("test-plugin", Some("1.0.0"));
1051 assert!(cached.is_some());
1052 }
1053}