qorzen_oxide/plugin/
mod.rs

1// src/plugin/mod.rs - Plugin system with hot-reloading and sandboxing
2
3mod loader;
4mod manager;
5mod manifest;
6mod sdk;
7mod search;
8
9use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::auth::{Permission, User};
13use crate::config::SettingsSchema;
14use crate::error::{Error, Result};
15use crate::event::{Event, EventBusManager};
16use crate::manager::{ManagedState, Manager, ManagerStatus, PlatformRequirements};
17use crate::platform::database::DatabaseArc;
18use crate::platform::filesystem::FileSystemArc;
19use async_trait::async_trait;
20use dioxus::prelude::*;
21use serde::{Deserialize, Serialize};
22use uuid::Uuid;
23
24/// Plugin information structure
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct PluginInfo {
27    pub id: String,
28    pub name: String,
29    pub version: String,
30    pub description: String,
31    pub author: String,
32    pub license: String,
33    pub homepage: Option<String>,
34    pub repository: Option<String>,
35    pub minimum_core_version: String,
36    pub supported_platforms: Vec<Platform>,
37}
38
39/// Supported platforms for plugins
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub enum Platform {
42    Windows,
43    MacOS,
44    Linux,
45    IOS,
46    Android,
47    Web,
48    All,
49}
50
51/// Plugin dependency specification
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct PluginDependency {
54    pub plugin_id: String,
55    pub version_requirement: String, // SemVer
56    pub optional: bool,
57}
58
59/// Plugin configuration schema
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct PluginConfig {
62    pub plugin_id: String,
63    pub version: String,
64    pub config_schema: serde_json::Value, // JSON Schema
65    pub default_values: serde_json::Value,
66    pub user_overrides: serde_json::Value,
67    pub validation_rules: Vec<ValidationRule>,
68}
69
70/// Configuration validation rule
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ValidationRule {
73    pub field: String,
74    pub rule_type: ValidationType,
75    pub message: String,
76}
77
78/// Validation rule types
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub enum ValidationType {
81    Required,
82    MinLength(usize),
83    MaxLength(usize),
84    Pattern(String),
85    Range { min: f64, max: f64 },
86    Custom(String),
87}
88
89/// UI component provided by a plugin
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct UIComponent {
92    pub id: String,
93    pub name: String,
94    pub component_type: ComponentType,
95    pub props: serde_json::Value,
96    pub required_permissions: Vec<Permission>,
97}
98
99/// Types of UI components
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub enum ComponentType {
102    Page,
103    Widget,
104    Modal,
105    Sidebar,
106    Header,
107    Footer,
108    Menu,
109    Form,
110}
111
112/// Menu item for navigation
113#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
114pub struct MenuItem {
115    pub id: String,
116    pub label: String,
117    pub icon: Option<String>,
118    pub route: Option<String>,
119    pub action: Option<String>,
120    pub required_permissions: Vec<Permission>,
121    pub order: i32,
122    pub children: Vec<MenuItem>,
123}
124
125/// API route definition
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ApiRoute {
128    pub path: String,
129    pub method: HttpMethod,
130    pub handler_id: String,
131    pub required_permissions: Vec<Permission>,
132    pub rate_limit: Option<RateLimit>,
133    pub documentation: ApiDocumentation,
134}
135
136/// HTTP methods
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub enum HttpMethod {
139    GET,
140    POST,
141    PUT,
142    DELETE,
143    PATCH,
144    HEAD,
145    OPTIONS,
146}
147
148/// Rate limiting configuration
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct RateLimit {
151    pub requests_per_minute: u32,
152    pub burst_limit: u32,
153}
154
155/// API documentation
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ApiDocumentation {
158    pub summary: String,
159    pub description: String,
160    pub parameters: Vec<ApiParameter>,
161    pub responses: Vec<ApiResponse>,
162    pub examples: Vec<ApiExample>,
163}
164
165/// API parameter definition
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct ApiParameter {
168    pub name: String,
169    pub parameter_type: ParameterType,
170    pub required: bool,
171    pub description: String,
172    pub example: Option<serde_json::Value>,
173}
174
175/// Parameter types
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub enum ParameterType {
178    Query,
179    Path,
180    Header,
181    Body,
182}
183
184/// API example
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct ApiExample {
187    pub name: String,
188    pub description: String,
189    pub request: serde_json::Value,
190    pub response: serde_json::Value,
191}
192
193/// API response definition
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct ApiResponse {
196    pub status_code: u16,
197    pub description: String,
198    pub schema: Option<serde_json::Value>,
199}
200
201/// Event handler registration
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct EventHandler {
204    pub event_type: String,
205    pub handler_id: String,
206    pub priority: i32,
207}
208
209/// Plugin execution context
210#[derive(Clone, Debug)]
211pub struct PluginContext {
212    pub plugin_id: String,
213    pub config: PluginConfig,
214    pub api_client: PluginApiClient,
215    pub event_bus: Arc<EventBusManager>,
216    pub database: Option<PluginDatabase>,
217    pub file_system: PluginFileSystem,
218}
219
220/// API client for plugin to core communication
221#[derive(Debug, Clone)]
222pub struct PluginApiClient {
223    #[allow(dead_code)]
224    plugin_id: String,
225}
226
227impl PluginApiClient {
228    /// Create a new API client for a plugin
229    pub fn new(plugin_id: String) -> Self {
230        Self { plugin_id }
231    }
232
233    /// Get a configuration value
234    pub async fn get_config(&self, _key: &str) -> Result<Option<serde_json::Value>> {
235        // Implementation would call core config system
236        Ok(None)
237    }
238
239    /// Set a configuration value
240    pub async fn set_config(&self, _key: &str, _value: serde_json::Value) -> Result<()> {
241        // Implementation would call core config system
242        Ok(())
243    }
244
245    /// Get the current user
246    pub async fn get_current_user(&self) -> Result<Option<User>> {
247        // Implementation would call account manager
248        Ok(None)
249    }
250
251    /// Check if current user has permission
252    pub async fn check_permission(&self, _resource: &str, _action: &str) -> Result<bool> {
253        // Implementation would call account manager
254        Ok(false)
255    }
256}
257
258/// Database access for plugins with sandboxing
259#[derive(Clone, Debug)]
260pub struct PluginDatabase {
261    plugin_id: String,
262    provider: DatabaseArc,
263    permissions: DatabasePermissions,
264}
265
266/// Database permissions for plugins
267#[derive(Debug, Clone)]
268pub struct DatabasePermissions {
269    pub can_create_tables: bool,
270    pub can_drop_tables: bool,
271    pub can_modify_schema: bool,
272    pub max_table_count: Option<u32>,
273    pub max_storage_size: Option<u64>,
274}
275
276impl PluginDatabase {
277    /// Create a new database access wrapper for a plugin
278    pub fn new(plugin_id: String, provider: DatabaseArc, permissions: DatabasePermissions) -> Self {
279        Self {
280            plugin_id,
281            provider,
282            permissions,
283        }
284    }
285
286    /// Execute a database query with permission checks
287    pub async fn execute(
288        &self,
289        query: &str,
290        params: &[serde_json::Value],
291    ) -> Result<crate::platform::database::QueryResult> {
292        // Check permissions before executing
293        if query.to_uppercase().contains("CREATE TABLE") && !self.permissions.can_create_tables {
294            return Err(Error::permission(
295                "database.create_table",
296                "Plugin not allowed to create tables",
297            ));
298        }
299
300        if query.to_uppercase().contains("DROP TABLE") && !self.permissions.can_drop_tables {
301            return Err(Error::permission(
302                "database.drop_table",
303                "Plugin not allowed to drop tables",
304            ));
305        }
306
307        // Add plugin prefix to table names to isolate data
308        let prefixed_query = self.add_table_prefix(query);
309
310        self.provider.execute(&prefixed_query, params).await
311    }
312
313    /// Query the database with permission checks
314    pub async fn query(
315        &self,
316        query: &str,
317        params: &[serde_json::Value],
318    ) -> Result<Vec<crate::platform::database::Row>> {
319        let prefixed_query = self.add_table_prefix(query);
320        self.provider.query(&prefixed_query, params).await
321    }
322
323    fn add_table_prefix(&self, query: &str) -> String {
324        // Simple implementation - in practice would need proper SQL parsing
325        query.replace("TABLE ", &format!("TABLE plugin_{}_ ", self.plugin_id))
326    }
327}
328
329/// File system access for plugins with sandboxing
330#[derive(Clone, Debug)]
331pub struct PluginFileSystem {
332    #[allow(dead_code)]
333    plugin_id: String,
334    provider: FileSystemArc,
335    base_path: String,
336}
337
338impl PluginFileSystem {
339    /// Create a new file system access wrapper for a plugin
340    pub fn new(plugin_id: String, provider: FileSystemArc) -> Self {
341        Self {
342            plugin_id: plugin_id.clone(),
343            provider,
344            base_path: format!("plugins/{}/", plugin_id),
345        }
346    }
347
348    /// Read a file with sandboxing
349    pub async fn read_file(&self, path: &str) -> Result<Vec<u8>> {
350        let safe_path = self.make_safe_path(path)?;
351        self.provider.read_file(&safe_path).await
352    }
353
354    /// Write a file with sandboxing
355    pub async fn write_file(&self, path: &str, data: &[u8]) -> Result<()> {
356        let safe_path = self.make_safe_path(path)?;
357        self.provider.write_file(&safe_path, data).await
358    }
359
360    fn make_safe_path(&self, path: &str) -> Result<String> {
361        // Prevent directory traversal
362        if path.contains("..") || path.starts_with('/') {
363            return Err(Error::permission("file.access", "Invalid file path"));
364        }
365
366        Ok(format!("{}{}", self.base_path, path))
367    }
368}
369
370/// Resource limits for plugin sandboxing
371#[derive(Debug, Clone)]
372pub struct ResourceLimits {
373    pub max_memory_mb: u64,
374    pub max_cpu_time_ms: u64,
375    pub max_file_size_mb: u64,
376    pub max_network_requests_per_minute: u32,
377    pub max_database_queries_per_minute: u32,
378}
379
380impl Default for ResourceLimits {
381    fn default() -> Self {
382        Self {
383            max_memory_mb: 100,
384            max_cpu_time_ms: 5000,
385            max_file_size_mb: 10,
386            max_network_requests_per_minute: 60,
387            max_database_queries_per_minute: 100,
388        }
389    }
390}
391
392/// Plugin sandbox for security
393pub struct PluginSandbox {
394    #[allow(dead_code)]
395    resource_limits: ResourceLimits,
396    allowed_permissions: Vec<Permission>,
397}
398
399impl PluginSandbox {
400    /// Create a new plugin sandbox
401    pub fn new(resource_limits: ResourceLimits, allowed_permissions: Vec<Permission>) -> Self {
402        Self {
403            resource_limits,
404            allowed_permissions,
405        }
406    }
407
408    /// Check if an operation is allowed
409    pub fn check_operation(&self, operation: &str, resource: &str) -> bool {
410        self.allowed_permissions.iter().any(|p| {
411            (p.resource == resource || p.resource == "*")
412                && (p.action == operation || p.action == "*")
413        })
414    }
415}
416
417/// Main plugin trait that all plugins must implement
418#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
419#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
420pub trait Plugin: Send + Sync + std::fmt::Debug {
421    /// Get plugin information
422    fn info(&self) -> PluginInfo;
423
424    /// Get required dependencies
425    fn required_dependencies(&self) -> Vec<PluginDependency>;
426
427    /// Get required permissions
428    fn required_permissions(&self) -> Vec<Permission>;
429
430    /// Initialize the plugin
431    async fn initialize(&mut self, context: PluginContext) -> Result<()>;
432
433    /// Shutdown the plugin
434    async fn shutdown(&mut self) -> Result<()>;
435
436    /// Get UI components provided by this plugin
437    fn ui_components(&self) -> Vec<UIComponent>;
438
439    /// Get menu items provided by this plugin
440    fn menu_items(&self) -> Vec<MenuItem>;
441
442    /// Get settings schema for configuration
443    fn settings_schema(&self) -> Option<SettingsSchema>;
444
445    /// Get API routes provided by this plugin
446    fn api_routes(&self) -> Vec<ApiRoute>;
447
448    /// Get event handlers provided by this plugin
449    fn event_handlers(&self) -> Vec<EventHandler>;
450
451    /// Render a UI component
452    fn render_component(&self, component_id: &str, props: serde_json::Value) -> Result<VNode>;
453
454    /// Handle an API request
455    async fn handle_api_request(&self, route_id: &str, request: ApiRequest) -> Result<ApiResponse>;
456
457    /// Handle an event
458    async fn handle_event(&self, handler_id: &str, event: &dyn Event) -> Result<()>;
459}
460
461/// Plugin loader trait for different loading mechanisms
462#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
463#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
464pub trait PluginLoader: Send + Sync {
465    /// Load a plugin from the given path
466    async fn load_plugin(&self, path: &str) -> Result<Box<dyn Plugin>>;
467
468    /// Validate a plugin before loading
469    async fn validate_plugin(&self, plugin: &dyn Plugin) -> Result<ValidationResult>;
470
471    /// Unload a plugin
472    async fn unload_plugin(&self, plugin_id: &str) -> Result<()>;
473}
474
475/// Plugin validation result
476#[derive(Debug, Clone)]
477pub struct ValidationResult {
478    pub is_valid: bool,
479    pub errors: Vec<String>,
480    pub warnings: Vec<String>,
481}
482
483/// API request structure
484#[derive(Debug, Clone)]
485pub struct ApiRequest {
486    pub method: String,
487    pub path: String,
488    pub headers: HashMap<String, String>,
489    pub query_params: HashMap<String, String>,
490    pub body: Option<serde_json::Value>,
491    pub user: Option<User>,
492}
493
494/// Plugin registry for managing loaded plugins
495#[derive(Debug)]
496pub struct PluginRegistry {
497    plugins: HashMap<String, Box<dyn Plugin>>,
498    dependencies: HashMap<String, Vec<String>>,
499    load_order: Vec<String>,
500}
501
502impl Default for PluginRegistry {
503    fn default() -> Self {
504        Self::new()
505    }
506}
507
508impl PluginRegistry {
509    /// Create a new plugin registry
510    pub fn new() -> Self {
511        Self {
512            plugins: HashMap::new(),
513            dependencies: HashMap::new(),
514            load_order: Vec::new(),
515        }
516    }
517
518    /// Register a plugin
519    pub fn register(&mut self, plugin: Box<dyn Plugin>) -> Result<()> {
520        let info = plugin.info();
521        let deps = plugin
522            .required_dependencies()
523            .into_iter()
524            .map(|d| d.plugin_id)
525            .collect();
526
527        if self.plugins.contains_key(&info.id) {
528            return Err(Error::plugin(&info.id, "Plugin already registered"));
529        }
530
531        self.plugins.insert(info.id.clone(), plugin);
532        self.dependencies.insert(info.id.clone(), deps);
533
534        self.calculate_load_order()?;
535
536        Ok(())
537    }
538
539    /// Get a plugin by ID
540    pub fn get(&self, plugin_id: &str) -> Option<&dyn Plugin> {
541        self.plugins.get(plugin_id).map(|p| p.as_ref())
542    }
543
544    /// List all plugin IDs
545    pub fn list(&self) -> Vec<&str> {
546        self.plugins.keys().map(|s| s.as_str()).collect()
547    }
548
549    /// Get the load order for plugins
550    pub fn load_order(&self) -> &[String] {
551        &self.load_order
552    }
553
554    fn calculate_load_order(&mut self) -> Result<()> {
555        let mut order = Vec::new();
556        let mut visited = std::collections::HashSet::new();
557        let mut visiting = std::collections::HashSet::new();
558
559        for plugin_id in self.plugins.keys() {
560            if !visited.contains(plugin_id) {
561                self.visit_plugin(plugin_id, &mut order, &mut visited, &mut visiting)?;
562            }
563        }
564
565        self.load_order = order;
566        Ok(())
567    }
568
569    fn visit_plugin(
570        &self,
571        plugin_id: &str,
572        order: &mut Vec<String>,
573        visited: &mut std::collections::HashSet<String>,
574        visiting: &mut std::collections::HashSet<String>,
575    ) -> Result<()> {
576        if visiting.contains(plugin_id) {
577            return Err(Error::plugin(plugin_id, "Circular dependency detected"));
578        }
579
580        if visited.contains(plugin_id) {
581            return Ok(());
582        }
583
584        visiting.insert(plugin_id.to_string());
585
586        if let Some(deps) = self.dependencies.get(plugin_id) {
587            for dep in deps {
588                if !self.plugins.contains_key(dep) {
589                    return Err(Error::plugin(
590                        plugin_id,
591                        format!("Missing dependency: {}", dep),
592                    ));
593                }
594                self.visit_plugin(dep, order, visited, visiting)?;
595            }
596        }
597
598        visiting.remove(plugin_id);
599        visited.insert(plugin_id.to_string());
600        order.push(plugin_id.to_string());
601
602        Ok(())
603    }
604}
605
606/// Dependency resolver for plugins
607pub struct DependencyResolver;
608
609impl Default for DependencyResolver {
610    fn default() -> Self {
611        Self::new()
612    }
613}
614
615impl DependencyResolver {
616    /// Create a new dependency resolver
617    pub fn new() -> Self {
618        Self
619    }
620
621    /// Resolve dependencies for a plugin
622    pub fn resolve(&self, plugin: &dyn Plugin, registry: &PluginRegistry) -> Result<Vec<String>> {
623        let deps = plugin.required_dependencies();
624        let mut resolved = Vec::new();
625
626        for dep in deps {
627            if registry.get(&dep.plugin_id).is_some() {
628                resolved.push(dep.plugin_id);
629            } else if !dep.optional {
630                return Err(Error::plugin(
631                    &plugin.info().id,
632                    format!("Required dependency not found: {}", dep.plugin_id),
633                ));
634            }
635        }
636
637        Ok(resolved)
638    }
639
640    /// Check version compatibility
641    pub fn check_version_compatibility(&self, required: &str, available: &str) -> bool {
642        // Simple version check - in practice would use semver
643        required == available || required == "*"
644    }
645}
646
647/// Main plugin manager
648pub struct PluginManager {
649    state: ManagedState,
650    registry: PluginRegistry,
651    loader: Box<dyn PluginLoader>,
652    #[allow(dead_code)]
653    sandbox: PluginSandbox,
654    api_provider: PluginApiProvider,
655    dependency_resolver: DependencyResolver,
656    plugin_contexts: HashMap<String, PluginContext>,
657}
658
659impl std::fmt::Debug for PluginManager {
660    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661        f.debug_struct("PluginManager")
662            .field("registry", &self.registry)
663            .finish()
664    }
665}
666
667/// API provider for plugins
668pub struct PluginApiProvider;
669
670impl PluginApiProvider {
671    /// Create a new API provider
672    pub fn new() -> Self {
673        Self
674    }
675
676    /// Create an API client for a plugin
677    pub fn create_client(&self, plugin_id: String) -> PluginApiClient {
678        PluginApiClient::new(plugin_id)
679    }
680}
681
682impl Default for PluginApiProvider {
683    fn default() -> Self {
684        Self::new()
685    }
686}
687
688impl PluginManager {
689    /// Create a new plugin manager
690    pub fn new(loader: Box<dyn PluginLoader>) -> Self {
691        Self {
692            state: ManagedState::new(Uuid::new_v4(), "plugin_manager"),
693            registry: PluginRegistry::new(),
694            loader,
695            sandbox: PluginSandbox::new(ResourceLimits::default(), Vec::new()),
696            api_provider: PluginApiProvider::new(),
697            dependency_resolver: DependencyResolver::new(),
698            plugin_contexts: HashMap::new(),
699        }
700    }
701
702    /// Load a plugin from a path
703    pub async fn load_plugin(&mut self, path: &str) -> Result<()> {
704        let plugin = self.loader.load_plugin(path).await?;
705
706        // Validate plugin
707        let validation = self.loader.validate_plugin(plugin.as_ref()).await?;
708        if !validation.is_valid {
709            return Err(Error::plugin(
710                &plugin.info().id,
711                format!("Plugin validation failed: {:?}", validation.errors),
712            ));
713        }
714
715        // Check dependencies
716        let _resolved_deps = self
717            .dependency_resolver
718            .resolve(plugin.as_ref(), &self.registry)?;
719
720        // Register plugin
721        let plugin_id = plugin.info().id.clone();
722        self.registry.register(plugin)?;
723
724        // Create plugin context
725        let context = self.create_plugin_context(&plugin_id).await?;
726        self.plugin_contexts.insert(plugin_id, context);
727
728        Ok(())
729    }
730
731    /// Unload a plugin
732    pub async fn unload_plugin(&mut self, plugin_id: &str) -> Result<()> {
733        if let Some(plugin) = self.registry.plugins.get_mut(plugin_id) {
734            plugin.shutdown().await?;
735        }
736
737        self.registry.plugins.remove(plugin_id);
738        self.plugin_contexts.remove(plugin_id);
739        self.loader.unload_plugin(plugin_id).await?;
740
741        Ok(())
742    }
743
744    /// Initialize all plugins
745    pub async fn initialize_plugins(&mut self) -> Result<()> {
746        let load_order = self.registry.load_order().to_vec();
747
748        for plugin_id in load_order {
749            if let (Some(plugin), Some(context)) = (
750                self.registry.plugins.get_mut(&plugin_id),
751                self.plugin_contexts.get(&plugin_id).cloned(),
752            ) {
753                plugin.initialize(context).await.map_err(|e| {
754                    Error::plugin(&plugin_id, format!("Plugin initialization failed: {}", e))
755                })?;
756            }
757        }
758
759        Ok(())
760    }
761
762    /// Get UI components from all plugins
763    pub fn get_ui_components(&self) -> Vec<(String, UIComponent)> {
764        let mut components = Vec::new();
765
766        for (plugin_id, plugin) in &self.registry.plugins {
767            for component in plugin.ui_components() {
768                components.push((plugin_id.clone(), component));
769            }
770        }
771
772        components
773    }
774
775    /// Get menu items from all plugins
776    pub fn get_menu_items(&self) -> Vec<(String, MenuItem)> {
777        let mut items = Vec::new();
778
779        for (plugin_id, plugin) in &self.registry.plugins {
780            for item in plugin.menu_items() {
781                items.push((plugin_id.clone(), item));
782            }
783        }
784
785        items
786    }
787
788    /// Render a component from a plugin
789    pub fn render_component(
790        &self,
791        plugin_id: &str,
792        component_id: &str,
793        props: serde_json::Value,
794    ) -> Result<VNode> {
795        let plugin = self
796            .registry
797            .get(plugin_id)
798            .ok_or_else(|| Error::plugin(plugin_id, "Plugin not found"))?;
799
800        plugin.render_component(component_id, props)
801    }
802
803    async fn create_plugin_context(&self, plugin_id: &str) -> Result<PluginContext> {
804        // This is a simplified implementation
805        // In a real system, this would create proper filesystem and database access
806        Ok(PluginContext {
807            plugin_id: plugin_id.to_string(),
808            config: PluginConfig {
809                plugin_id: plugin_id.to_string(),
810                version: "1.0.0".to_string(),
811                config_schema: serde_json::json!({}),
812                default_values: serde_json::json!({}),
813                user_overrides: serde_json::json!({}),
814                validation_rules: Vec::new(),
815            },
816            api_client: self.api_provider.create_client(plugin_id.to_string()),
817            event_bus: Arc::new(EventBusManager::new(crate::event::EventBusConfig::default())),
818            database: None,
819            file_system: PluginFileSystem {
820                plugin_id: plugin_id.to_string(),
821                provider: Arc::new(crate::platform::MockFileSystem::new()),
822                base_path: format!("plugins/{}/", plugin_id),
823            },
824        })
825    }
826}
827
828#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
829#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
830impl Manager for PluginManager {
831    fn name(&self) -> &str {
832        "plugin_manager"
833    }
834
835    fn id(&self) -> Uuid {
836        self.state.id()
837    }
838
839    async fn initialize(&mut self) -> Result<()> {
840        self.state
841            .set_state(crate::manager::ManagerState::Initializing)
842            .await;
843
844        // Initialize all loaded plugins
845        self.initialize_plugins().await?;
846
847        self.state
848            .set_state(crate::manager::ManagerState::Running)
849            .await;
850        Ok(())
851    }
852
853    async fn shutdown(&mut self) -> Result<()> {
854        self.state
855            .set_state(crate::manager::ManagerState::ShuttingDown)
856            .await;
857
858        // Shutdown all plugins in reverse order
859        let mut load_order = self.registry.load_order().to_vec();
860        load_order.reverse();
861
862        for plugin_id in load_order {
863            if let Err(e) = self.unload_plugin(&plugin_id).await {
864                tracing::error!("Failed to unload plugin {}: {}", plugin_id, e);
865            }
866        }
867
868        self.state
869            .set_state(crate::manager::ManagerState::Shutdown)
870            .await;
871        Ok(())
872    }
873
874    async fn status(&self) -> ManagerStatus {
875        let mut status = self.state.status().await;
876
877        status.add_metadata(
878            "loaded_plugins",
879            serde_json::Value::from(self.registry.plugins.len()),
880        );
881        status.add_metadata(
882            "plugin_list",
883            serde_json::Value::Array(
884                self.registry
885                    .list()
886                    .into_iter()
887                    .map(|s| serde_json::Value::String(s.to_string()))
888                    .collect(),
889            ),
890        );
891
892        status
893    }
894
895    fn platform_requirements(&self) -> PlatformRequirements {
896        PlatformRequirements {
897            requires_filesystem: true,
898            requires_network: false,
899            requires_database: false,
900            requires_native_apis: false,
901            minimum_permissions: vec!["plugin.load".to_string(), "plugin.manage".to_string()],
902        }
903    }
904}
905
906#[cfg(test)]
907mod tests {
908    use super::*;
909
910    #[derive(Debug)]
911    struct TestPlugin {
912        info: PluginInfo,
913    }
914
915    impl TestPlugin {
916        fn new(id: String) -> Self {
917            Self {
918                info: PluginInfo {
919                    id,
920                    name: "Test Plugin".to_string(),
921                    version: "1.0.0".to_string(),
922                    description: "A test plugin".to_string(),
923                    author: "Test Author".to_string(),
924                    license: "MIT".to_string(),
925                    homepage: None,
926                    repository: None,
927                    minimum_core_version: "1.0.0".to_string(),
928                    supported_platforms: vec![Platform::All],
929                },
930            }
931        }
932    }
933
934    #[async_trait]
935    impl Plugin for TestPlugin {
936        fn info(&self) -> PluginInfo {
937            self.info.clone()
938        }
939
940        fn required_dependencies(&self) -> Vec<PluginDependency> {
941            Vec::new()
942        }
943
944        fn required_permissions(&self) -> Vec<Permission> {
945            Vec::new()
946        }
947
948        async fn initialize(&mut self, _context: PluginContext) -> Result<()> {
949            Ok(())
950        }
951
952        async fn shutdown(&mut self) -> Result<()> {
953            Ok(())
954        }
955
956        fn ui_components(&self) -> Vec<UIComponent> {
957            Vec::new()
958        }
959
960        fn menu_items(&self) -> Vec<MenuItem> {
961            Vec::new()
962        }
963
964        fn settings_schema(&self) -> Option<SettingsSchema> {
965            None
966        }
967
968        fn api_routes(&self) -> Vec<ApiRoute> {
969            Vec::new()
970        }
971
972        fn event_handlers(&self) -> Vec<EventHandler> {
973            Vec::new()
974        }
975
976        fn render_component(
977            &self,
978            _component_id: &str,
979            _props: serde_json::Value,
980        ) -> Result<VNode> {
981            Err(Error::plugin(
982                &self.info.id,
983                "Component rendering not implemented",
984            ))
985        }
986
987        async fn handle_api_request(
988            &self,
989            _route_id: &str,
990            _request: ApiRequest,
991        ) -> Result<ApiResponse> {
992            Err(Error::plugin(&self.info.id, "API handling not implemented"))
993        }
994
995        async fn handle_event(&self, _handler_id: &str, _event: &dyn Event) -> Result<()> {
996            Ok(())
997        }
998    }
999
1000    #[test]
1001    fn test_plugin_registry() {
1002        let mut registry = PluginRegistry::new();
1003        let plugin = Box::new(TestPlugin::new("test_plugin".to_string()));
1004
1005        registry.register(plugin).unwrap();
1006
1007        assert!(registry.get("test_plugin").is_some());
1008        assert_eq!(registry.list().len(), 1);
1009    }
1010
1011    #[test]
1012    fn test_dependency_resolution() {
1013        let resolver = DependencyResolver::new();
1014        let registry = PluginRegistry::new();
1015        let plugin = TestPlugin::new("test_plugin".to_string());
1016
1017        let resolved = resolver.resolve(&plugin, &registry).unwrap();
1018        assert!(resolved.is_empty()); // No dependencies
1019    }
1020}