1use serde::{Deserialize, Serialize};
44use std::collections::HashMap;
45use std::path::{Path, PathBuf};
46use std::sync::Arc;
47use thiserror::Error;
48use tokio::sync::RwLock;
49
50pub mod api;
51pub mod effects;
52pub mod loader;
53pub mod registry;
54pub mod voices;
55
56type PluginMap = RwLock<HashMap<String, Arc<RwLock<Box<dyn Plugin>>>>>;
58
59#[derive(Debug, Error)]
60pub enum PluginError {
61 #[error("Plugin not found: {0}")]
62 NotFound(String),
63
64 #[error("Plugin loading failed: {0}")]
65 LoadingFailed(String),
66
67 #[error("Invalid plugin manifest: {0}")]
68 InvalidManifest(String),
69
70 #[error("Plugin API version mismatch: expected {expected}, got {actual}")]
71 ApiVersionMismatch { expected: String, actual: String },
72
73 #[error("Plugin permission denied: {0}")]
74 PermissionDenied(String),
75
76 #[error("Plugin execution failed: {0}")]
77 ExecutionFailed(String),
78
79 #[error("Plugin dependency missing: {0}")]
80 DependencyMissing(String),
81
82 #[error("Plugin security violation: {0}")]
83 SecurityViolation(String),
84
85 #[error("IO error: {0}")]
86 IoError(#[from] std::io::Error),
87
88 #[error("Serialization error: {0}")]
89 SerializationError(#[from] serde_json::Error),
90}
91
92pub type PluginResult<T> = Result<T, PluginError>;
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct PluginManifest {
96 pub name: String,
97 pub version: String,
98 pub description: String,
99 pub author: String,
100 pub api_version: String,
101 pub plugin_type: PluginType,
102 pub entry_point: String,
103 pub dependencies: Vec<String>,
104 pub permissions: Vec<Permission>,
105 pub configuration: Option<serde_json::Value>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
109pub enum PluginType {
110 Effect,
111 Voice,
112 Processor,
113 Extension,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub enum Permission {
118 FileRead,
119 FileWrite,
120 NetworkAccess,
121 SystemInfo,
122 AudioCapture,
123 AudioPlayback,
124 ConfigAccess,
125 ModelAccess,
126}
127
128#[derive(Debug, Clone)]
129pub struct PluginInfo {
130 pub manifest: PluginManifest,
131 pub path: PathBuf,
132 pub loaded: bool,
133 pub enabled: bool,
134 pub load_count: u32,
135 pub last_error: Option<String>,
136}
137
138pub trait Plugin: Send + Sync {
139 fn name(&self) -> &str;
140 fn version(&self) -> &str;
141 fn description(&self) -> &str;
142 fn plugin_type(&self) -> PluginType;
143
144 fn initialize(&mut self, config: &serde_json::Value) -> PluginResult<()>;
145 fn cleanup(&mut self) -> PluginResult<()>;
146
147 fn get_capabilities(&self) -> Vec<String>;
148 fn execute(&self, command: &str, args: &serde_json::Value) -> PluginResult<serde_json::Value>;
149}
150
151pub struct PluginManager {
152 plugins: PluginMap,
153 plugin_info: RwLock<HashMap<String, PluginInfo>>,
154 plugin_directories: Vec<PathBuf>,
155 api_version: String,
156 security_enabled: bool,
157}
158
159impl PluginManager {
160 pub fn new() -> Self {
161 Self {
162 plugins: RwLock::new(HashMap::new()),
163 plugin_info: RwLock::new(HashMap::new()),
164 plugin_directories: vec![
165 dirs::config_dir()
166 .unwrap_or_default()
167 .join("voirs")
168 .join("plugins"),
169 dirs::data_local_dir()
170 .unwrap_or_default()
171 .join("voirs")
172 .join("plugins"),
173 PathBuf::from("/usr/local/share/voirs/plugins"),
174 PathBuf::from("./plugins"),
175 ],
176 api_version: "1.0.0".to_string(),
177 security_enabled: true,
178 }
179 }
180
181 pub fn add_plugin_directory<P: AsRef<Path>>(&mut self, path: P) {
182 self.plugin_directories.push(path.as_ref().to_path_buf());
183 }
184
185 pub async fn discover_plugins(&self) -> PluginResult<Vec<PluginInfo>> {
186 let mut discovered = Vec::new();
187
188 for directory in &self.plugin_directories {
189 if !directory.exists() {
190 continue;
191 }
192
193 let mut entries = tokio::fs::read_dir(directory).await?;
194
195 while let Some(entry) = entries.next_entry().await? {
196 let path = entry.path();
197
198 if path.is_dir() {
199 let manifest_path = path.join("plugin.json");
200 if manifest_path.exists() {
201 match self.load_manifest(&manifest_path).await {
202 Ok(manifest) => {
203 let plugin_info = PluginInfo {
204 manifest,
205 path: path.clone(),
206 loaded: false,
207 enabled: true,
208 load_count: 0,
209 last_error: None,
210 };
211 discovered.push(plugin_info);
212 }
213 Err(e) => {
214 eprintln!(
215 "Failed to load plugin manifest {}: {}",
216 manifest_path.display(),
217 e
218 );
219 }
220 }
221 }
222 }
223 }
224 }
225
226 Ok(discovered)
227 }
228
229 pub async fn load_plugin(&self, name: &str) -> PluginResult<()> {
230 let plugin_info = {
231 let info_guard = self.plugin_info.read().await;
232 info_guard
233 .get(name)
234 .cloned()
235 .ok_or_else(|| PluginError::NotFound(name.to_string()))?
236 };
237
238 if plugin_info.manifest.api_version != self.api_version {
239 return Err(PluginError::ApiVersionMismatch {
240 expected: self.api_version.clone(),
241 actual: plugin_info.manifest.api_version.clone(),
242 });
243 }
244
245 if self.security_enabled {
246 self.validate_permissions(&plugin_info.manifest.permissions)?;
247 }
248
249 let plugin = self
250 .load_plugin_from_path(&plugin_info.path, &plugin_info.manifest)
251 .await?;
252
253 {
254 let mut plugins_guard = self.plugins.write().await;
255 plugins_guard.insert(name.to_string(), Arc::new(RwLock::new(plugin)));
256 }
257
258 {
259 let mut info_guard = self.plugin_info.write().await;
260 if let Some(info) = info_guard.get_mut(name) {
261 info.loaded = true;
262 info.load_count += 1;
263 info.last_error = None;
264 }
265 }
266
267 Ok(())
268 }
269
270 pub async fn unload_plugin(&self, name: &str) -> PluginResult<()> {
271 let plugin = {
272 let mut plugins_guard = self.plugins.write().await;
273 plugins_guard
274 .remove(name)
275 .ok_or_else(|| PluginError::NotFound(name.to_string()))?
276 };
277
278 {
279 let mut plugin_guard = plugin.write().await;
280 plugin_guard.cleanup()?;
281 }
282
283 {
284 let mut info_guard = self.plugin_info.write().await;
285 if let Some(info) = info_guard.get_mut(name) {
286 info.loaded = false;
287 info.last_error = None;
288 }
289 }
290
291 Ok(())
292 }
293
294 pub async fn execute_plugin(
295 &self,
296 name: &str,
297 command: &str,
298 args: &serde_json::Value,
299 ) -> PluginResult<serde_json::Value> {
300 let plugin = {
301 let plugins_guard = self.plugins.read().await;
302 plugins_guard
303 .get(name)
304 .cloned()
305 .ok_or_else(|| PluginError::NotFound(name.to_string()))?
306 };
307
308 let plugin_guard = plugin.read().await;
309 plugin_guard.execute(command, args)
310 }
311
312 pub async fn list_plugins(&self) -> Vec<PluginInfo> {
313 let info_guard = self.plugin_info.read().await;
314 info_guard.values().cloned().collect()
315 }
316
317 pub async fn get_plugin_info(&self, name: &str) -> Option<PluginInfo> {
318 let info_guard = self.plugin_info.read().await;
319 info_guard.get(name).cloned()
320 }
321
322 pub async fn enable_plugin(&self, name: &str) -> PluginResult<()> {
323 let mut info_guard = self.plugin_info.write().await;
324 if let Some(info) = info_guard.get_mut(name) {
325 info.enabled = true;
326 Ok(())
327 } else {
328 Err(PluginError::NotFound(name.to_string()))
329 }
330 }
331
332 pub async fn disable_plugin(&self, name: &str) -> PluginResult<()> {
333 let mut info_guard = self.plugin_info.write().await;
334 if let Some(info) = info_guard.get_mut(name) {
335 info.enabled = false;
336 Ok(())
337 } else {
338 Err(PluginError::NotFound(name.to_string()))
339 }
340 }
341
342 async fn load_manifest(&self, path: &Path) -> PluginResult<PluginManifest> {
343 let content = tokio::fs::read_to_string(path).await?;
344 let manifest: PluginManifest = serde_json::from_str(&content)?;
345 Ok(manifest)
346 }
347
348 async fn load_plugin_from_path(
349 &self,
350 _path: &Path,
351 _manifest: &PluginManifest,
352 ) -> PluginResult<Box<dyn Plugin>> {
353 Ok(Box::new(MockPlugin::new()))
357 }
358
359 fn validate_permissions(&self, permissions: &[Permission]) -> PluginResult<()> {
360 for permission in permissions {
362 match permission {
363 Permission::FileWrite => {
364 }
367 Permission::NetworkAccess => {
368 }
371 Permission::SystemInfo => {
372 }
374 _ => {
375 }
377 }
378 }
379 Ok(())
380 }
381}
382
383impl Default for PluginManager {
384 fn default() -> Self {
385 Self::new()
386 }
387}
388
389struct MockPlugin {
391 name: String,
392 version: String,
393 description: String,
394}
395
396impl MockPlugin {
397 fn new() -> Self {
398 Self {
399 name: "mock-plugin".to_string(),
400 version: "1.0.0".to_string(),
401 description: "Mock plugin for testing".to_string(),
402 }
403 }
404}
405
406impl Plugin for MockPlugin {
407 fn name(&self) -> &str {
408 &self.name
409 }
410
411 fn version(&self) -> &str {
412 &self.version
413 }
414
415 fn description(&self) -> &str {
416 &self.description
417 }
418
419 fn plugin_type(&self) -> PluginType {
420 PluginType::Extension
421 }
422
423 fn initialize(&mut self, _config: &serde_json::Value) -> PluginResult<()> {
424 Ok(())
425 }
426
427 fn cleanup(&mut self) -> PluginResult<()> {
428 Ok(())
429 }
430
431 fn get_capabilities(&self) -> Vec<String> {
432 vec!["test".to_string()]
433 }
434
435 fn execute(&self, command: &str, args: &serde_json::Value) -> PluginResult<serde_json::Value> {
436 match command {
437 "test" => Ok(serde_json::json!({
438 "status": "ok",
439 "args": args
440 })),
441 _ => Err(PluginError::ExecutionFailed(format!(
442 "Unknown command: {}",
443 command
444 ))),
445 }
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452
453 #[tokio::test]
454 async fn test_plugin_manager_creation() {
455 let manager = PluginManager::new();
456 assert_eq!(manager.api_version, "1.0.0");
457 assert!(manager.security_enabled);
458 }
459
460 #[tokio::test]
461 async fn test_plugin_discovery() {
462 let manager = PluginManager::new();
463 let plugins = manager.discover_plugins().await.unwrap();
464 }
467
468 #[tokio::test]
469 async fn test_mock_plugin() {
470 let plugin = MockPlugin::new();
471 assert_eq!(plugin.name(), "mock-plugin");
472 assert_eq!(plugin.version(), "1.0.0");
473 assert_eq!(plugin.description(), "Mock plugin for testing");
474 }
475
476 #[tokio::test]
477 async fn test_plugin_execution() {
478 let plugin = MockPlugin::new();
479 let result = plugin
480 .execute("test", &serde_json::json!({"key": "value"}))
481 .unwrap();
482 assert_eq!(result["status"], "ok");
483 assert_eq!(result["args"]["key"], "value");
484 }
485
486 #[tokio::test]
487 async fn test_plugin_unknown_command() {
488 let plugin = MockPlugin::new();
489 let result = plugin.execute("unknown", &serde_json::json!({}));
490 assert!(result.is_err());
491 }
492}