//! 插件系统模块 //! //! 支持插件加载、卸载、生命周期管理等功能 use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::sync::Arc; use chrono::{DateTime, Utc}; /// 插件元数据 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PluginManifest { /// 插件唯一标识 pub id: String, /// 插件名称 pub name: String, /// 插件描述 pub description: String, /// 插件版本 pub version: String, /// 作者 pub author: String, /// 最低 ReadFlow 版本要求 pub min_readflow_version: Option, /// 插件入口点(WASM 文件路径) pub entry_point: Option, /// 依赖的其他插件 pub dependencies: Vec, /// 插件配置项 pub config_schema: Option, } /// 插件状态 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum PluginStatus { /// 已禁用 Disabled, /// 已启用 Enabled, /// 加载中 Loading, /// 运行中 Running, /// 错误 Error(String), } /// 插件信息 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PluginInfo { pub manifest: PluginManifest, pub path: PathBuf, pub status: PluginStatus, pub loaded_at: Option>, pub config: serde_json::Value, } /// 插件 trait - 定义插件接口 pub trait Plugin: Send + Sync { /// 获取插件 ID fn id(&self) -> &str; /// 插件初始化 fn initialize(&mut self) -> Result<()> { Ok(()) } /// 插件激活 fn on_activate(&mut self) -> Result<()> { Ok(()) } /// 插件停用 fn on_deactivate(&mut self) -> Result<()> { Ok(()) } /// 插件卸载 fn on_uninstall(&mut self) -> Result<()> { Ok(()) } } /// 插件管理器 pub struct PluginManager { /// 插件存储路径 plugins_dir: PathBuf, /// 已加载的插件 plugins: HashMap, /// 插件注册表 registry: HashMap>, } impl PluginManager { /// 创建插件管理器 pub fn new(plugins_dir: &str) -> Result { let plugins_dir = PathBuf::from(plugins_dir); // 创建插件目录(如果不存在) if !plugins_dir.exists() { std::fs::create_dir_all(&plugins_dir)?; } Ok(Self { plugins_dir, plugins: HashMap::new(), registry: HashMap::new(), }) } /// 获取插件目录 pub fn plugins_dir(&self) -> &Path { &self.plugins_dir } /// 扫描插件目录 pub fn scan_plugins(&mut self) -> Result> { let mut manifests = Vec::new(); if !self.plugins_dir.exists() { return Ok(manifests); } for entry in std::fs::read_dir(&self.plugins_dir)? { let entry = entry?; let path = entry.path(); if !path.is_dir() { continue; } let manifest_path = path.join("manifest.json"); if !manifest_path.exists() { continue; } let manifest_content = std::fs::read_to_string(&manifest_path)?; let manifest: PluginManifest = serde_json::from_str(&manifest_content)?; manifests.push(manifest); } Ok(manifests) } /// 加载插件 pub fn load_plugin(&mut self, plugin_id: &str) -> Result<()> { let plugin_path = self.plugins_dir.join(plugin_id); if !plugin_path.exists() { anyhow::bail!("插件目录不存在:{}", plugin_id); } let manifest_path = plugin_path.join("manifest.json"); let manifest_content = std::fs::read_to_string(&manifest_path)?; let manifest: PluginManifest = serde_json::from_str(&manifest_content)?; // 检查依赖 for dep in &manifest.dependencies { if !self.registry.contains_key(dep) { anyhow::bail!("插件 {} 依赖未满足:{}", plugin_id, dep); } } // 创建插件信息 let plugin_info = PluginInfo { manifest: manifest.clone(), path: plugin_path, status: PluginStatus::Loading, loaded_at: None, config: serde_json::Value::Object(serde_json::Map::new()), }; self.plugins.insert(plugin_id.to_string(), plugin_info); // TODO: 加载 WASM 插件 // let wasm_path = plugin_path.join(&manifest.entry_point.unwrap_or_else(|| "plugin.wasm".to_string())); // 模拟加载成功 if let Some(info) = self.plugins.get_mut(plugin_id) { info.status = PluginStatus::Enabled; info.loaded_at = Some(Utc::now()); } Ok(()) } /// 启用插件 pub fn enable_plugin(&mut self, plugin_id: &str) -> Result<()> { if let Some(info) = self.plugins.get_mut(plugin_id) { info.status = PluginStatus::Enabled; // TODO: 调用插件 on_activate } Ok(()) } /// 禁用插件 pub fn disable_plugin(&mut self, plugin_id: &str) -> Result<()> { if let Some(info) = self.plugins.get_mut(plugin_id) { info.status = PluginStatus::Disabled; // TODO: 调用插件 on_deactivate } Ok(()) } /// 卸载插件 pub fn uninstall_plugin(&mut self, plugin_id: &str) -> Result<()> { // 检查是否有其他插件依赖此插件 for (id, info) in &self.plugins { if id != plugin_id && info.manifest.dependencies.contains(&plugin_id.to_string()) { anyhow::bail!("无法卸载插件 {}:插件 {} 依赖它", plugin_id, id); } } // 调用插件卸载回调 if let Some(info) = self.plugins.get_mut(plugin_id) { info.status = PluginStatus::Disabled; // TODO: 调用插件 on_uninstall } // 从注册表移除 self.registry.remove(plugin_id); // 从文件系统删除 let plugin_path = self.plugins_dir.join(plugin_id); if plugin_path.exists() { std::fs::remove_dir_all(&plugin_path)?; } // 从内存移除 self.plugins.remove(plugin_id); Ok(()) } /// 获取所有插件 pub fn get_all_plugins(&self) -> Vec<&PluginInfo> { self.plugins.values().collect() } /// 获取启用的插件 pub fn get_enabled_plugins(&self) -> Vec<&PluginInfo> { self.plugins .values() .filter(|info| matches!(info.status, PluginStatus::Enabled | PluginStatus::Running)) .collect() } /// 获取插件状态 pub fn get_plugin_status(&self, plugin_id: &str) -> Option<&PluginStatus> { self.plugins.get(plugin_id).map(|info| &info.status) } /// 安装插件(从文件) pub fn install_plugin(&mut self, plugin_path: &Path) -> Result { // 读取 manifest let manifest_path = plugin_path.join("manifest.json"); let manifest_content = std::fs::read_to_string(&manifest_path)?; let manifest: PluginManifest = serde_json::from_str(&manifest_content)?; // 复制到插件目录 let target_path = self.plugins_dir.join(&manifest.id); if target_path.exists() { anyhow::bail!("插件已安装:{}", manifest.id); } // 复制整个插件目录 self.copy_dir_recursive(plugin_path, &target_path)?; // 加载插件 self.load_plugin(&manifest.id)?; Ok(manifest.id.clone()) } /// 递归复制目录 fn copy_dir_recursive(&self, src: &Path, dst: &Path) -> Result<()> { std::fs::create_dir_all(dst)?; for entry in std::fs::read_dir(src)? { let entry = entry?; let src_path = entry.path(); let dst_path = dst.join(entry.file_name()); if src_path.is_dir() { self.copy_dir_recursive(&src_path, &dst_path)?; } else { std::fs::copy(&src_path, &dst_path)?; } } Ok(()) } /// 导出插件列表为 JSON pub fn export_plugins(&self) -> Result { let plugins: Vec<&PluginInfo> = self.get_all_plugins(); let json = serde_json::to_string_pretty(&plugins)?; Ok(json) } } /// 插件配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PluginConfig { pub plugin_id: String, pub enabled: bool, pub settings: serde_json::Value, } /// 内置插件:主题切换 pub struct ThemePlugin { current_theme: String, } impl ThemePlugin { pub fn new() -> Self { Self { current_theme: "dark".to_string(), } } pub fn set_theme(&mut self, theme: &str) { self.current_theme = theme.to_string(); } pub fn get_theme(&self) -> &str { &self.current_theme } } impl Plugin for ThemePlugin { fn id(&self) -> &str { "com.readflow.theme" } } /// 内置插件:快捷键 pub struct HotkeyPlugin { shortcuts: HashMap, } impl HotkeyPlugin { pub fn new() -> Self { let mut shortcuts = HashMap::new(); shortcuts.insert("open_file".to_string(), "Ctrl+O".to_string()); shortcuts.insert("search".to_string(), "Ctrl+F".to_string()); shortcuts.insert("bookmark".to_string(), "Ctrl+B".to_string()); Self { shortcuts } } pub fn get_shortcut(&self, action: &str) -> Option<&String> { self.shortcuts.get(action) } } impl Plugin for HotkeyPlugin { fn id(&self) -> &str { "com.readflow.hotkey" } } #[cfg(test)] mod tests { use super::*; #[test] fn test_plugin_manager_creation() { let temp_dir = std::env::temp_dir().join("readflow_test_plugins"); let manager = PluginManager::new(temp_dir.to_str().unwrap()); assert!(manager.is_ok()); // 清理 let _ = std::fs::remove_dir_all(&temp_dir); } #[test] fn test_theme_plugin() { let mut plugin = ThemePlugin::new(); assert_eq!(plugin.get_theme(), "dark"); plugin.set_theme("light"); assert_eq!(plugin.get_theme(), "light"); } }