1use crate::chart::{ChartSpec, MarkType};
43use crate::data_sources::DataSource;
44use crate::export_system::{ExportFormat, ExportResult};
45use crate::styling::ChartTheme;
46use serde::{Deserialize, Serialize};
47use std::any::TypeId;
48use std::collections::HashMap;
49use std::fmt;
50use std::sync::{Arc, RwLock};
51use tokio::sync::RwLock as AsyncRwLock;
52
53#[derive(Debug, Clone, thiserror::Error)]
55pub enum PluginError {
56 #[error("Plugin not found: {0}")]
57 PluginNotFound(String),
58
59 #[error("Plugin registration failed: {0}")]
60 RegistrationFailed(String),
61
62 #[error("Plugin execution failed: {0}")]
63 ExecutionFailed(String),
64
65 #[error("Plugin validation failed: {0}")]
66 ValidationFailed(String),
67
68 #[error("Plugin compatibility error: {0}")]
69 CompatibilityError(String),
70
71 #[error("Plugin security violation: {0}")]
72 SecurityViolation(String),
73
74 #[error("Plugin resource limit exceeded: {0}")]
75 ResourceLimitExceeded(String),
76
77 #[error("Plugin dependency missing: {0}")]
78 DependencyMissing(String),
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct PluginMetadata {
84 pub name: String,
85 pub version: String,
86 pub description: String,
87 pub author: String,
88 pub license: String,
89 pub homepage: Option<String>,
90 pub repository: Option<String>,
91 pub dependencies: Vec<PluginDependency>,
92 pub capabilities: PluginCapabilities,
93 pub security_level: SecurityLevel,
94 pub performance_impact: PerformanceImpact,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct PluginDependency {
100 pub name: String,
101 pub version: String,
102 pub optional: bool,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
107pub enum PluginCapability {
108 ChartRendering,
110 DataSource,
112 DataTransform,
114 ExportFormat,
116 MLIntelligence,
118 Theming,
120 Interaction,
122 Performance,
124 Security,
126 Accessibility,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct PluginCapabilities {
133 pub capabilities: Vec<PluginCapability>,
134 pub max_data_points: Option<u64>,
135 pub supported_formats: Vec<String>,
136 pub performance_requirements: PerformanceRequirements,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct PerformanceRequirements {
142 pub max_memory_mb: Option<u64>,
143 pub max_execution_time_ms: Option<u64>,
144 pub max_cpu_usage_percent: Option<u8>,
145 pub requires_gpu: bool,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
150pub enum SecurityLevel {
151 Trusted,
153 Sandboxed,
155 Untrusted,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
161pub enum PerformanceImpact {
162 Minimal,
164 Moderate,
166 High,
168 Critical,
170}
171
172#[derive(Debug, Clone)]
174pub enum PluginEvent {
175 Registered { name: String, version: String },
177 Activated { name: String },
179 Deactivated { name: String },
181 Error { name: String, error: PluginError },
183 PerformanceWarning { name: String, message: String },
185}
186
187pub trait PluginEventHandler: Send + Sync {
189 fn handle_event(&self, event: PluginEvent);
190}
191
192pub trait Plugin: Send + Sync + fmt::Debug {
194 fn metadata(&self) -> &PluginMetadata;
196
197 fn initialize(&mut self) -> Result<(), PluginError>;
199
200 fn cleanup(&mut self) -> Result<(), PluginError>;
202
203 fn is_compatible(&self, system_info: &SystemInfo) -> bool;
205
206 fn type_id(&self) -> TypeId;
208
209 fn clone_plugin(&self) -> Box<dyn Plugin>;
211
212 fn as_any(&self) -> &dyn std::any::Any;
214}
215
216#[derive(Debug, Clone)]
218pub struct SystemInfo {
219 pub helios_version: String,
220 pub rust_version: String,
221 pub platform: String,
222 pub features: Vec<String>,
223 pub available_memory_mb: u64,
224 pub gpu_available: bool,
225}
226
227pub trait ChartPlugin: Plugin {
229 fn supported_marks(&self) -> Vec<MarkType>;
231
232 fn render(
234 &self,
235 spec: &ChartSpec,
236 context: &RenderContext,
237 ) -> Result<RenderResult, PluginError>;
238
239 fn validate_spec(&self, spec: &ChartSpec) -> Result<(), PluginError>;
241
242 fn estimate_performance(&self, spec: &ChartSpec) -> PerformanceEstimate;
244}
245
246pub trait DataSourcePlugin: Plugin {
248 fn supported_sources(&self) -> Vec<String>;
250
251 fn create_connection(
253 &self,
254 config: &DataSourceConfig,
255 ) -> Result<Box<dyn DataSource>, PluginError>;
256
257 fn validate_config(&self, config: &DataSourceConfig) -> Result<(), PluginError>;
259}
260
261pub trait TransformPlugin: Plugin {
263 fn supported_transforms(&self) -> Vec<String>;
265
266 fn apply_transform(
268 &self,
269 data: &[u8],
270 transform_type: &str,
271 params: &TransformParams,
272 ) -> Result<Vec<u8>, PluginError>;
273
274 fn validate_params(
276 &self,
277 transform_type: &str,
278 params: &TransformParams,
279 ) -> Result<(), PluginError>;
280}
281
282pub trait ExportPlugin: Plugin {
284 fn supported_formats(&self) -> Vec<ExportFormat>;
286
287 fn export(
289 &self,
290 chart_data: &ChartData,
291 format: &ExportFormat,
292 options: &ExportOptions,
293 ) -> Result<ExportResult, PluginError>;
294
295 fn validate_options(
297 &self,
298 format: &ExportFormat,
299 options: &ExportOptions,
300 ) -> Result<(), PluginError>;
301}
302
303pub trait MLPlugin: Plugin {
305 fn supported_capabilities(&self) -> Vec<String>;
307
308 fn execute_ml(
310 &self,
311 capability: &str,
312 data: &MLData,
313 params: &MLParams,
314 ) -> Result<Vec<u8>, PluginError>;
315
316 fn validate_ml_params(&self, capability: &str, params: &MLParams) -> Result<(), PluginError>;
318}
319
320pub trait ThemePlugin: Plugin {
322 fn supported_themes(&self) -> Vec<String>;
324
325 fn apply_theme(
327 &self,
328 theme_name: &str,
329 config: &HashMap<String, String>,
330 ) -> Result<ChartTheme, PluginError>;
331
332 fn validate_theme_config(
334 &self,
335 theme_name: &str,
336 config: &HashMap<String, String>,
337 ) -> Result<(), PluginError>;
338}
339
340#[derive(Debug, Clone)]
342pub struct RenderContext {
343 pub viewport: Viewport,
344 pub device_info: DeviceInfo,
345 pub performance_budget: PerformanceBudget,
346 pub security_context: SecurityContext,
347}
348
349#[derive(Debug, Clone)]
351pub struct Viewport {
352 pub width: u32,
353 pub height: u32,
354 pub dpi: f32,
355 pub pixel_ratio: f32,
356}
357
358#[derive(Debug, Clone)]
360pub struct DeviceInfo {
361 pub gpu_available: bool,
362 pub gpu_vendor: Option<String>,
363 pub memory_mb: u64,
364 pub cpu_cores: u32,
365}
366
367#[derive(Debug, Clone)]
369pub struct PerformanceBudget {
370 pub max_render_time_ms: u64,
371 pub max_memory_mb: u64,
372 pub max_cpu_usage_percent: u8,
373}
374
375#[derive(Debug, Clone)]
377pub struct SecurityContext {
378 pub security_level: SecurityLevel,
379 pub allowed_operations: Vec<String>,
380 pub sandbox_mode: bool,
381}
382
383#[derive(Debug, Clone)]
385pub struct RenderResult {
386 pub success: bool,
387 pub render_time_ms: u64,
388 pub memory_used_mb: u64,
389 pub warnings: Vec<String>,
390 pub error: Option<String>,
391}
392
393impl RenderResult {
394 pub fn success() -> Self {
395 Self {
396 success: true,
397 render_time_ms: 0,
398 memory_used_mb: 0,
399 warnings: Vec::new(),
400 error: None,
401 }
402 }
403
404 pub fn error(message: String) -> Self {
405 Self {
406 success: false,
407 render_time_ms: 0,
408 memory_used_mb: 0,
409 warnings: Vec::new(),
410 error: Some(message),
411 }
412 }
413}
414
415#[derive(Debug, Clone)]
417pub struct PerformanceEstimate {
418 pub estimated_time_ms: u64,
419 pub estimated_memory_mb: u64,
420 pub complexity_score: f64,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct DataSourceConfig {
426 pub source_type: String,
427 pub connection_string: String,
428 pub credentials: Option<HashMap<String, String>>,
429 pub options: HashMap<String, String>,
430}
431
432#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct TransformParams {
435 pub transform_type: String,
436 pub parameters: HashMap<String, serde_json::Value>,
437}
438
439#[derive(Debug, Clone)]
441pub struct ChartData {
442 pub spec: ChartSpec,
443 pub rendered_data: Vec<u8>,
444 pub metadata: HashMap<String, String>,
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize)]
449pub struct ExportOptions {
450 pub quality: u8,
451 pub format_options: HashMap<String, serde_json::Value>,
452}
453
454#[derive(Debug, Clone)]
456pub struct MLData {
457 pub data_type: String,
458 pub data: Vec<u8>,
459 pub schema: Option<serde_json::Value>,
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize)]
464pub struct MLParams {
465 pub algorithm: String,
466 pub parameters: HashMap<String, serde_json::Value>,
467}
468
469pub struct PluginManager {
471 chart_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn ChartPlugin>>>>,
472 data_source_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn DataSourcePlugin>>>>,
473 transform_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn TransformPlugin>>>>,
474 export_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn ExportPlugin>>>>,
475 ml_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn MLPlugin>>>>,
476 theme_plugins: Arc<AsyncRwLock<HashMap<String, Box<dyn ThemePlugin>>>>,
477 event_handlers: Arc<RwLock<Vec<Box<dyn PluginEventHandler>>>>,
478 system_info: SystemInfo,
479}
480
481impl PluginManager {
482 pub fn new() -> Self {
484 Self {
485 chart_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
486 data_source_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
487 transform_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
488 export_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
489 ml_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
490 theme_plugins: Arc::new(AsyncRwLock::new(HashMap::new())),
491 event_handlers: Arc::new(RwLock::new(Vec::new())),
492 system_info: SystemInfo::current(),
493 }
494 }
495
496 pub async fn register_chart_plugin(
498 &self,
499 plugin: Box<dyn ChartPlugin>,
500 ) -> Result<(), PluginError> {
501 let metadata = plugin.metadata().clone();
502 let name = metadata.name.clone();
503
504 if !plugin.is_compatible(&self.system_info) {
506 return Err(PluginError::CompatibilityError(format!(
507 "Plugin {} is not compatible with current system",
508 name
509 )));
510 }
511
512 let mut plugin = plugin;
514 plugin.initialize()?;
515
516 let mut plugins = self.chart_plugins.write().await;
518 plugins.insert(name.clone(), plugin);
519
520 self.emit_event(PluginEvent::Registered {
522 name: name.clone(),
523 version: metadata.version.clone(),
524 });
525
526 Ok(())
527 }
528
529 pub async fn register_data_source_plugin(
531 &self,
532 plugin: Box<dyn DataSourcePlugin>,
533 ) -> Result<(), PluginError> {
534 let metadata = plugin.metadata().clone();
535 let name = metadata.name.clone();
536
537 if !plugin.is_compatible(&self.system_info) {
538 return Err(PluginError::CompatibilityError(format!(
539 "Plugin {} is not compatible with current system",
540 name
541 )));
542 }
543
544 let mut plugin = plugin;
545 plugin.initialize()?;
546
547 let mut plugins = self.data_source_plugins.write().await;
548 plugins.insert(name.clone(), plugin);
549
550 self.emit_event(PluginEvent::Registered {
551 name: name.clone(),
552 version: metadata.version.clone(),
553 });
554
555 Ok(())
556 }
557
558 pub async fn register_transform_plugin(
560 &self,
561 plugin: Box<dyn TransformPlugin>,
562 ) -> Result<(), PluginError> {
563 let metadata = plugin.metadata().clone();
564 let name = metadata.name.clone();
565
566 if !plugin.is_compatible(&self.system_info) {
567 return Err(PluginError::CompatibilityError(format!(
568 "Plugin {} is not compatible with current system",
569 name
570 )));
571 }
572
573 let mut plugin = plugin;
574 plugin.initialize()?;
575
576 let mut plugins = self.transform_plugins.write().await;
577 plugins.insert(name.clone(), plugin);
578
579 self.emit_event(PluginEvent::Registered {
580 name: name.clone(),
581 version: metadata.version.clone(),
582 });
583
584 Ok(())
585 }
586
587 pub async fn register_export_plugin(
589 &self,
590 plugin: Box<dyn ExportPlugin>,
591 ) -> Result<(), PluginError> {
592 let metadata = plugin.metadata().clone();
593 let name = metadata.name.clone();
594
595 if !plugin.is_compatible(&self.system_info) {
596 return Err(PluginError::CompatibilityError(format!(
597 "Plugin {} is not compatible with current system",
598 name
599 )));
600 }
601
602 let mut plugin = plugin;
603 plugin.initialize()?;
604
605 let mut plugins = self.export_plugins.write().await;
606 plugins.insert(name.clone(), plugin);
607
608 self.emit_event(PluginEvent::Registered {
609 name: name.clone(),
610 version: metadata.version.clone(),
611 });
612
613 Ok(())
614 }
615
616 pub async fn register_ml_plugin(&self, plugin: Box<dyn MLPlugin>) -> Result<(), PluginError> {
618 let metadata = plugin.metadata().clone();
619 let name = metadata.name.clone();
620
621 if !plugin.is_compatible(&self.system_info) {
622 return Err(PluginError::CompatibilityError(format!(
623 "Plugin {} is not compatible with current system",
624 name
625 )));
626 }
627
628 let mut plugin = plugin;
629 plugin.initialize()?;
630
631 let mut plugins = self.ml_plugins.write().await;
632 plugins.insert(name.clone(), plugin);
633
634 self.emit_event(PluginEvent::Registered {
635 name: name.clone(),
636 version: metadata.version.clone(),
637 });
638
639 Ok(())
640 }
641
642 pub async fn register_theme_plugin(
644 &self,
645 plugin: Box<dyn ThemePlugin>,
646 ) -> Result<(), PluginError> {
647 let metadata = plugin.metadata().clone();
648 let name = metadata.name.clone();
649
650 if !plugin.is_compatible(&self.system_info) {
651 return Err(PluginError::CompatibilityError(format!(
652 "Plugin {} is not compatible with current system",
653 name
654 )));
655 }
656
657 let mut plugin = plugin;
658 plugin.initialize()?;
659
660 let mut plugins = self.theme_plugins.write().await;
661 plugins.insert(name.clone(), plugin);
662
663 self.emit_event(PluginEvent::Registered {
664 name: name.clone(),
665 version: metadata.version.clone(),
666 });
667
668 Ok(())
669 }
670
671 pub async fn get_chart_plugin(&self, name: &str) -> Option<Box<dyn ChartPlugin>> {
673 let plugins = self.chart_plugins.read().await;
674 plugins.get(name).map(|_| None).flatten()
677 }
678
679 pub async fn list_chart_plugins(&self) -> Vec<String> {
681 let plugins = self.chart_plugins.read().await;
682 plugins.keys().cloned().collect()
683 }
684
685 pub fn add_event_handler(&self, handler: Box<dyn PluginEventHandler>) {
687 let mut handlers = self.event_handlers.write().unwrap();
688 handlers.push(handler);
689 }
690
691 fn emit_event(&self, event: PluginEvent) {
693 let handlers = self.event_handlers.read().unwrap();
694 for handler in handlers.iter() {
695 handler.handle_event(event.clone());
696 }
697 }
698
699 pub fn system_info(&self) -> &SystemInfo {
701 &self.system_info
702 }
703
704 pub async fn validate_all_plugins(&self) -> Result<(), PluginError> {
706 let chart_plugins = self.chart_plugins.read().await;
708 for (name, plugin) in chart_plugins.iter() {
709 if !plugin.is_compatible(&self.system_info) {
710 return Err(PluginError::CompatibilityError(format!(
711 "Chart plugin {} is not compatible",
712 name
713 )));
714 }
715 }
716
717 Ok(())
721 }
722
723 pub async fn cleanup_all_plugins(&self) -> Result<(), PluginError> {
725 let mut chart_plugins = self.chart_plugins.write().await;
727 for (name, plugin) in chart_plugins.iter_mut() {
728 if let Err(e) = plugin.cleanup() {
729 return Err(PluginError::ExecutionFailed(format!(
730 "Failed to cleanup chart plugin {}: {}",
731 name, e
732 )));
733 }
734 }
735
736 Ok(())
740 }
741}
742
743impl SystemInfo {
744 pub fn current() -> Self {
746 Self {
747 helios_version: env!("CARGO_PKG_VERSION").to_string(),
748 rust_version: "1.70.0".to_string(), platform: std::env::consts::OS.to_string(),
750 features: vec![], available_memory_mb: 1024, gpu_available: true, }
754 }
755}
756
757impl Default for PluginManager {
758 fn default() -> Self {
759 Self::new()
760 }
761}
762
763pub struct PluginRegistry {
765 plugins: HashMap<String, PluginRegistration>,
766}
767
768pub struct PluginRegistration {
770 pub metadata: PluginMetadata,
771 pub factory: Box<dyn Fn() -> Box<dyn Plugin> + Send + Sync>,
772}
773
774impl PluginRegistry {
775 pub fn new() -> Self {
777 Self {
778 plugins: HashMap::new(),
779 }
780 }
781
782 pub fn register<F>(&mut self, metadata: PluginMetadata, factory: F)
784 where
785 F: Fn() -> Box<dyn Plugin> + Send + Sync + 'static,
786 {
787 let name = metadata.name.clone();
788 self.plugins.insert(
789 name,
790 PluginRegistration {
791 metadata,
792 factory: Box::new(factory),
793 },
794 );
795 }
796
797 pub fn get_factory(&self, name: &str) -> Option<&(dyn Fn() -> Box<dyn Plugin> + Send + Sync)> {
799 self.plugins.get(name).map(|reg| reg.factory.as_ref())
800 }
801
802 pub fn list_plugins(&self) -> Vec<&PluginMetadata> {
804 self.plugins.values().map(|reg| ®.metadata).collect()
805 }
806}
807
808impl Default for PluginRegistry {
809 fn default() -> Self {
810 Self::new()
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817
818 #[derive(Debug)]
820 struct MockChartPlugin {
821 metadata: PluginMetadata,
822 }
823
824 impl MockChartPlugin {
825 fn new() -> Self {
826 Self {
827 metadata: PluginMetadata {
828 name: "mock-chart".to_string(),
829 version: "1.0.0".to_string(),
830 description: "Mock chart plugin for testing".to_string(),
831 author: "Test Author".to_string(),
832 license: "MIT".to_string(),
833 homepage: None,
834 repository: None,
835 dependencies: Vec::new(),
836 capabilities: PluginCapabilities {
837 capabilities: vec![PluginCapability::ChartRendering],
838 max_data_points: Some(10000),
839 supported_formats: vec!["svg".to_string()],
840 performance_requirements: PerformanceRequirements {
841 max_memory_mb: Some(100),
842 max_execution_time_ms: Some(1000),
843 max_cpu_usage_percent: Some(50),
844 requires_gpu: false,
845 },
846 },
847 security_level: SecurityLevel::Sandboxed,
848 performance_impact: PerformanceImpact::Minimal,
849 },
850 }
851 }
852 }
853
854 impl Plugin for MockChartPlugin {
855 fn metadata(&self) -> &PluginMetadata {
856 &self.metadata
857 }
858
859 fn initialize(&mut self) -> Result<(), PluginError> {
860 Ok(())
861 }
862
863 fn cleanup(&mut self) -> Result<(), PluginError> {
864 Ok(())
865 }
866
867 fn is_compatible(&self, _system_info: &SystemInfo) -> bool {
868 true
869 }
870
871 fn type_id(&self) -> TypeId {
872 TypeId::of::<MockChartPlugin>()
873 }
874
875 fn clone_plugin(&self) -> Box<dyn Plugin> {
876 Box::new(MockChartPlugin::new())
877 }
878
879 fn as_any(&self) -> &dyn std::any::Any {
880 self
881 }
882 }
883
884 impl ChartPlugin for MockChartPlugin {
885 fn supported_marks(&self) -> Vec<MarkType> {
886 vec![MarkType::Point {
887 size: Some(5.0),
888 opacity: Some(1.0),
889 shape: Some(crate::chart::PointShape::Circle),
890 }]
891 }
892
893 fn render(
894 &self,
895 _spec: &ChartSpec,
896 _context: &RenderContext,
897 ) -> Result<RenderResult, PluginError> {
898 Ok(RenderResult::success())
899 }
900
901 fn validate_spec(&self, _spec: &ChartSpec) -> Result<(), PluginError> {
902 Ok(())
903 }
904
905 fn estimate_performance(&self, _spec: &ChartSpec) -> PerformanceEstimate {
906 PerformanceEstimate {
907 estimated_time_ms: 10,
908 estimated_memory_mb: 5,
909 complexity_score: 1.0,
910 }
911 }
912 }
913
914 #[tokio::test]
915 async fn test_plugin_manager_creation() {
916 let manager = PluginManager::new();
917 assert_eq!(manager.list_chart_plugins().await.len(), 0);
918 }
919
920 #[tokio::test]
921 async fn test_chart_plugin_registration() {
922 let manager = PluginManager::new();
923 let plugin = Box::new(MockChartPlugin::new());
924
925 let result = manager.register_chart_plugin(plugin).await;
926 assert!(result.is_ok());
927
928 let plugins = manager.list_chart_plugins().await;
929 assert_eq!(plugins.len(), 1);
930 assert_eq!(plugins[0], "mock-chart");
931 }
932
933 #[tokio::test]
934 async fn test_chart_plugin_retrieval() {
935 let manager = PluginManager::new();
936 let plugin = Box::new(MockChartPlugin::new());
937
938 manager.register_chart_plugin(plugin).await.unwrap();
939
940 let plugins = manager.list_chart_plugins().await;
942 assert_eq!(plugins.len(), 1);
943 assert_eq!(plugins[0], "mock-chart");
944
945 let retrieved_plugin = manager.get_chart_plugin("mock-chart").await;
948 assert!(retrieved_plugin.is_none()); }
950
951 #[tokio::test]
952 async fn test_plugin_not_found() {
953 let manager = PluginManager::new();
954 let plugin = manager.get_chart_plugin("nonexistent").await;
955 assert!(plugin.is_none());
956 }
957
958 #[test]
959 fn test_plugin_metadata() {
960 let plugin = MockChartPlugin::new();
961 let metadata = plugin.metadata();
962
963 assert_eq!(metadata.name, "mock-chart");
964 assert_eq!(metadata.version, "1.0.0");
965 assert_eq!(metadata.security_level, SecurityLevel::Sandboxed);
966 assert_eq!(metadata.performance_impact, PerformanceImpact::Minimal);
967 }
968
969 #[test]
970 fn test_plugin_capabilities() {
971 let plugin = MockChartPlugin::new();
972 let capabilities = &plugin.metadata().capabilities;
973
974 assert!(capabilities
975 .capabilities
976 .contains(&PluginCapability::ChartRendering));
977 assert_eq!(capabilities.max_data_points, Some(10000));
978 assert!(capabilities.supported_formats.contains(&"svg".to_string()));
979 }
980
981 #[test]
982 fn test_system_info() {
983 let system_info = SystemInfo::current();
984
985 assert!(!system_info.helios_version.is_empty());
986 assert!(!system_info.platform.is_empty());
987 }
988
989 #[test]
990 fn test_plugin_registry() {
991 let mut registry = PluginRegistry::new();
992 let plugin = MockChartPlugin::new();
993 let metadata = plugin.metadata().clone();
994
995 registry.register(metadata.clone(), || Box::new(MockChartPlugin::new()));
996
997 let plugins = registry.list_plugins();
998 assert_eq!(plugins.len(), 1);
999 assert_eq!(plugins[0].name, "mock-chart");
1000
1001 let factory = registry.get_factory("mock-chart");
1002 assert!(factory.is_some());
1003 }
1004
1005 #[test]
1006 fn test_render_result() {
1007 let success_result = RenderResult::success();
1008 assert!(success_result.success);
1009 assert!(success_result.error.is_none());
1010
1011 let error_result = RenderResult::error("Test error".to_string());
1012 assert!(!error_result.success);
1013 assert_eq!(error_result.error, Some("Test error".to_string()));
1014 }
1015
1016 #[test]
1017 fn test_performance_estimate() {
1018 let estimate = PerformanceEstimate {
1019 estimated_time_ms: 100,
1020 estimated_memory_mb: 50,
1021 complexity_score: 2.5,
1022 };
1023
1024 assert_eq!(estimate.estimated_time_ms, 100);
1025 assert_eq!(estimate.estimated_memory_mb, 50);
1026 assert_eq!(estimate.complexity_score, 2.5);
1027 }
1028}