Skip to main content

sh_layer4/plugin_loader/
mod.rs

1//! # Plugin Loader
2//!
3//! 插件动态加载和管理系统。
4//!
5//! 支持两种加载方式:
6//! - **dylib**: 动态库加载 (.so/.dylib/.dll)
7//! - **wasm**: WebAssembly 模块加载 (沙箱隔离)
8//!
9//! ## 示例
10//!
11//! ```rust,ignore
12//! use sh_layer4::plugin_loader::{PluginLoader, DylibLoader};
13//!
14//! // 加载动态库插件
15//! let loader = PluginLoader::new("./plugins");
16//! let name = loader.load_dylib(Path::new("./plugins/my_plugin.so")).await?;
17//!
18//! // 初始化并执行
19//! loader.initialize(&name, &context).await?;
20//! let result = loader.execute(&name, &input).await?;
21//! ```
22
23pub mod abi;
24pub mod capabilities;
25pub mod dylib;
26pub mod sandbox;
27pub mod wasm;
28
29use async_trait::async_trait;
30use parking_lot::RwLock;
31use serde::{Deserialize, Serialize};
32use std::collections::HashMap;
33use std::path::Path;
34use std::sync::Arc;
35
36use crate::types::Layer4Result;
37pub use abi::StablePluginMeta;
38pub use capabilities::{Capability, CapabilitySet};
39pub use dylib::{DylibLoader, PluginCreateFn, PluginDestroyFn, PluginMetaFn};
40pub use sandbox::PluginSandbox;
41pub use wasm::WasmLoader;
42
43/// 插件接口
44#[async_trait]
45pub trait Plugin: Send + Sync {
46    /// 插件名称
47    fn name(&self) -> &str;
48
49    /// 插件版本
50    fn version(&self) -> &str;
51
52    /// 插件描述
53    fn description(&self) -> &str {
54        ""
55    }
56
57    /// 依赖列表
58    fn dependencies(&self) -> Vec<&str> {
59        Vec::new()
60    }
61
62    /// 初始化插件
63    async fn initialize(&self, context: &PluginContext) -> Layer4Result<()>;
64
65    /// 执行插件
66    async fn execute(&self, input: &serde_json::Value) -> Layer4Result<serde_json::Value>;
67
68    /// 关闭插件
69    async fn shutdown(&self) -> Layer4Result<()> {
70        Ok(())
71    }
72}
73
74/// 插件元数据
75///
76/// 注意: 此结构体用于 Rust 插件元数据序列化,不直接用于 FFI 边界。
77/// FFI 插件应使用 C-compatible 的 PluginMetadata 结构体。
78#[derive(Debug, Clone, Serialize, Deserialize)]
79#[repr(C)]
80#[allow(improper_ctypes_definitions)] // 用于 Rust 内部序列化,非直接 FFI
81pub struct PluginMeta {
82    pub name: String,
83    pub version: String,
84    pub author: String,
85    pub description: String,
86    pub dependencies: Vec<String>,
87    pub entry_point: String,
88}
89
90impl Default for PluginMeta {
91    fn default() -> Self {
92        Self {
93            name: "unknown".to_string(),
94            version: "0.1.0".to_string(),
95            author: "unknown".to_string(),
96            description: String::new(),
97            dependencies: Vec::new(),
98            entry_point: "main".to_string(),
99        }
100    }
101}
102
103/// 插件状态
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum PluginState {
106    Unloaded,
107    Loaded,
108    Initialized,
109    Running,
110    Error,
111    Shutdown,
112}
113
114/// 插件信息
115#[derive(Debug, Clone)]
116pub struct PluginInfo {
117    pub meta: PluginMeta,
118    pub state: PluginState,
119    pub path: std::path::PathBuf,
120    pub loaded_at: Option<chrono::DateTime<chrono::Utc>>,
121}
122
123/// 插件上下文
124#[derive(Debug, Clone)]
125pub struct PluginContext {
126    pub plugin_name: String,
127    pub config: serde_json::Value,
128    pub data_dir: std::path::PathBuf,
129}
130
131impl PluginContext {
132    pub fn new(plugin_name: impl Into<String>, data_dir: impl Into<std::path::PathBuf>) -> Self {
133        Self {
134            plugin_name: plugin_name.into(),
135            config: serde_json::Value::Null,
136            data_dir: data_dir.into(),
137        }
138    }
139
140    pub fn with_config(mut self, config: serde_json::Value) -> Self {
141        self.config = config;
142        self
143    }
144}
145
146/// 插件注册表
147pub struct PluginRegistry {
148    plugins: RwLock<HashMap<String, PluginInfo>>,
149    instances: RwLock<HashMap<String, Box<dyn Plugin>>>,
150}
151
152impl PluginRegistry {
153    pub fn new() -> Self {
154        Self {
155            plugins: RwLock::new(HashMap::new()),
156            instances: RwLock::new(HashMap::new()),
157        }
158    }
159
160    /// 注册插件
161    pub fn register(&self, plugin: Box<dyn Plugin>, path: &Path) -> Layer4Result<()> {
162        let name = plugin.name().to_string();
163        let meta = PluginMeta {
164            name: name.clone(),
165            version: plugin.version().to_string(),
166            description: plugin.description().to_string(),
167            dependencies: plugin
168                .dependencies()
169                .iter()
170                .map(|s| s.to_string())
171                .collect(),
172            ..Default::default()
173        };
174
175        let info = PluginInfo {
176            meta,
177            state: PluginState::Loaded,
178            path: path.to_path_buf(),
179            loaded_at: Some(chrono::Utc::now()),
180        };
181
182        self.plugins.write().insert(name.clone(), info);
183        self.instances.write().insert(name, plugin);
184
185        Ok(())
186    }
187
188    /// 注销插件
189    pub fn unregister(&self, name: &str) -> Layer4Result<bool> {
190        self.plugins.write().remove(name);
191        Ok(self.instances.write().remove(name).is_some())
192    }
193
194    /// 获取插件信息
195    pub fn get_info(&self, name: &str) -> Option<PluginInfo> {
196        self.plugins.read().get(name).cloned()
197    }
198
199    /// 获取插件实例
200    pub fn get(&self, _name: &str) -> Option<Arc<dyn Plugin>> {
201        // 由于 Box 不能直接共享,这里返回 Option
202        // 实际实现需要使用 Arc
203        None
204    }
205
206    /// 列出所有插件
207    pub fn list(&self) -> Vec<PluginInfo> {
208        self.plugins.read().values().cloned().collect()
209    }
210
211    /// 更新插件状态
212    pub fn update_state(&self, name: &str, state: PluginState) {
213        if let Some(info) = self.plugins.write().get_mut(name) {
214            info.state = state;
215        }
216    }
217
218    /// 插件数量
219    pub fn count(&self) -> usize {
220        self.plugins.read().len()
221    }
222}
223
224impl Default for PluginRegistry {
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230/// 插件加载器
231pub struct PluginLoader {
232    registry: PluginRegistry,
233    dylib_loader: DylibLoader,
234    wasm_loader: WasmLoader,
235    plugin_dir: std::path::PathBuf,
236}
237
238impl PluginLoader {
239    /// 创建新的插件加载器
240    pub fn new(plugin_dir: impl Into<std::path::PathBuf>) -> Self {
241        Self {
242            registry: PluginRegistry::new(),
243            dylib_loader: DylibLoader::new(),
244            wasm_loader: WasmLoader::new().expect("Failed to create WasmLoader"),
245            plugin_dir: plugin_dir.into(),
246        }
247    }
248
249    /// 使用默认目录创建
250    pub fn with_default_dir() -> Self {
251        Self::new("~/.continuum/plugins")
252    }
253
254    /// 加载动态库插件
255    ///
256    /// # Safety
257    ///
258    /// 动态库加载涉及不安全操作,请确保库来源可信
259    pub async fn load_dylib(&self, path: &Path) -> Layer4Result<String> {
260        let (name, meta) = self.dylib_loader.load_safe(path)?;
261
262        let info = PluginInfo {
263            meta,
264            state: PluginState::Loaded,
265            path: path.to_path_buf(),
266            loaded_at: Some(chrono::Utc::now()),
267        };
268
269        self.registry.plugins.write().insert(name.clone(), info);
270        self.registry.update_state(&name, PluginState::Loaded);
271
272        Ok(name)
273    }
274
275    /// 加载单个插件(自动检测类型)
276    pub async fn load(&self, path: &Path) -> Layer4Result<String> {
277        // 检测插件类型
278        let ext = path.extension().and_then(|e| e.to_str());
279
280        match ext {
281            Some("so") | Some("dylib") | Some("dll") => self.load_dylib(path).await,
282            Some("wasm") => self.load_wasm(path).await,
283            _ => {
284                let ext_display = ext.unwrap_or("(no extension)");
285                Err(anyhow::anyhow!(
286                    "Unsupported plugin extension '{}'. Supported formats: .so, .dylib, .dll (native), .wasm (WebAssembly)",
287                    ext_display
288                ))
289            }
290        }
291    }
292
293    /// 加载 WASM 插件
294    pub async fn load_wasm(&self, path: &Path) -> Layer4Result<String> {
295        let capabilities = CapabilitySet::sandboxed();
296        let name = self.wasm_loader.load(path, capabilities)?;
297        self.registry.update_state(&name, PluginState::Loaded);
298        Ok(name)
299    }
300
301    /// 加载目录中的所有插件
302    pub async fn load_dir(&self) -> Layer4Result<Vec<String>> {
303        let mut loaded = Vec::new();
304
305        if let Ok(entries) = std::fs::read_dir(&self.plugin_dir) {
306            for entry in entries.flatten() {
307                let path = entry.path();
308                let ext = path.extension().and_then(|e| e.to_str());
309
310                // 支持的插件类型
311                if matches!(ext, Some("so") | Some("dylib") | Some("dll") | Some("wasm")) {
312                    if let Ok(name) = self.load(&path).await {
313                        loaded.push(name);
314                    }
315                }
316            }
317        }
318
319        Ok(loaded)
320    }
321
322    /// 获取插件
323    pub fn get(&self, name: &str) -> Option<PluginInfo> {
324        self.registry.get_info(name)
325    }
326
327    /// 获取插件元数据
328    pub fn get_meta(&self, name: &str) -> Option<PluginMeta> {
329        self.dylib_loader.get_meta(name)
330    }
331
332    /// 初始化插件
333    pub async fn initialize(&self, name: &str, context: &PluginContext) -> Layer4Result<()> {
334        // 调用插件的 FFI 初始化函数(如果存在)
335        let config_json = serde_json::to_string(&context.config).unwrap_or_default();
336        match self.dylib_loader.call_initialize(name, &config_json) {
337            Some(true) => {
338                tracing::debug!("Plugin {} initialized via FFI", name);
339            }
340            Some(false) => {
341                tracing::warn!("Plugin {} FFI initialize returned failure", name);
342            }
343            None => {
344                // 插件未导出 plugin_initialize 函数,跳过(可选)
345                tracing::debug!("Plugin {} has no FFI initialize function", name);
346            }
347        }
348
349        self.registry.update_state(name, PluginState::Initialized);
350        Ok(())
351    }
352
353    /// 重新加载插件
354    pub async fn reload(&self, name: &str) -> Layer4Result<()> {
355        // 先卸载
356        self.dylib_loader.unload(name)?;
357
358        // 重新加载
359        let info = self.registry.get_info(name);
360        if let Some(info) = info {
361            self.load_dylib(&info.path).await?;
362        }
363
364        Ok(())
365    }
366
367    /// 卸载插件
368    pub async fn unload(&self, name: &str) -> Layer4Result<()> {
369        self.registry.update_state(name, PluginState::Shutdown);
370        self.dylib_loader.unload(name)?;
371        self.registry.unregister(name)?;
372        Ok(())
373    }
374
375    /// 列出所有插件
376    pub fn list(&self) -> Vec<PluginInfo> {
377        self.registry.list()
378    }
379
380    /// 插件数量
381    pub fn count(&self) -> usize {
382        self.registry.count()
383    }
384
385    /// 渲染插件状态
386    pub fn render_status(&self) -> String {
387        let plugins = self.registry.list();
388        let mut output = String::new();
389
390        output.push_str("Plugins:\n");
391
392        if plugins.is_empty() {
393            output.push_str("  No plugins loaded\n");
394        } else {
395            for info in plugins {
396                let status = match info.state {
397                    PluginState::Unloaded => "⚪",
398                    PluginState::Loaded => "🔵",
399                    PluginState::Initialized => "🟢",
400                    PluginState::Running => "🟡",
401                    PluginState::Error => "🔴",
402                    PluginState::Shutdown => "⚫",
403                };
404                output.push_str(&format!(
405                    "  {} {} v{}\n",
406                    status, info.meta.name, info.meta.version
407                ));
408            }
409        }
410
411        output
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418
419    #[test]
420    fn test_plugin_registry_creation() {
421        let registry = PluginRegistry::new();
422        assert_eq!(registry.count(), 0);
423    }
424
425    #[test]
426    fn test_plugin_context_creation() {
427        let ctx = PluginContext::new("test-plugin", "/tmp/plugins");
428        assert_eq!(ctx.plugin_name, "test-plugin");
429    }
430
431    #[test]
432    fn test_plugin_loader_creation() {
433        let loader = PluginLoader::with_default_dir();
434        assert_eq!(loader.count(), 0);
435    }
436
437    #[test]
438    fn test_plugin_meta_default() {
439        let meta = PluginMeta::default();
440        assert_eq!(meta.name, "unknown");
441        assert_eq!(meta.version, "0.1.0");
442    }
443
444    #[tokio::test]
445    async fn test_unknown_plugin_extension_returns_error() {
446        let dir = tempfile::tempdir().unwrap();
447        let plugin_path = dir.path().join("plugin.txt");
448        std::fs::write(&plugin_path, b"not a plugin").unwrap();
449
450        let loader = PluginLoader::new(dir.path());
451        let err = loader.load(&plugin_path).await.unwrap_err();
452        let message = err.to_string();
453
454        assert!(message.contains("Unsupported plugin extension"));
455        assert!(message.contains(".wasm"));
456    }
457}