Skip to main content

oxidite_plugin/
loader.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3use std::sync::Arc;
4use crate::Plugin;
5use oxidite_core::Result;
6
7/// Plugin loader responsible for loading plugins from disk
8pub struct PluginLoader;
9
10impl PluginLoader {
11    pub fn new() -> Self {
12        Self
13    }
14    
15    /// Load a plugin from a shared library file
16    #[cfg(not(target_arch = "wasm32"))]
17    pub fn load_from_file<P: AsRef<Path>>(&self, _path: P) -> Result<Arc<dyn Plugin>> {
18        // For now, just return an error since we don't have actual plugin loading implemented
19        // This avoids the libloading error
20        Err(oxidite_core::Error::InternalServerError(
21            "Plugin loading from file not implemented in this version".to_string()
22        ))
23    }
24    
25    /// Scan a directory for plugin files
26    pub fn scan_directory<P: AsRef<Path>>(&self, path: P) -> Result<Vec<PathBuf>> {
27        let dir = path.as_ref();
28        if !dir.exists() {
29            return Ok(Vec::new());
30        }
31
32        let mut plugins = Vec::new();
33        for entry in fs::read_dir(dir)
34            .map_err(|e| oxidite_core::Error::InternalServerError(e.to_string()))?
35        {
36            let entry = entry
37                .map_err(|e| oxidite_core::Error::InternalServerError(e.to_string()))?;
38            let path = entry.path();
39
40            let is_plugin_file = path.extension()
41                .and_then(|e| e.to_str())
42                .map(|ext| matches!(ext, "so" | "dylib" | "dll"))
43                .unwrap_or(false);
44            if is_plugin_file {
45                plugins.push(path);
46            }
47        }
48
49        Ok(plugins)
50    }
51    
52    /// Load all plugins from a directory
53    pub async fn load_from_directory<P: AsRef<Path>>(&self, path: P) -> Result<Vec<Arc<dyn Plugin>>> {
54        println!("Scanning for plugins in: {:?}", path.as_ref());
55        let mut plugins = Vec::new();
56        for plugin_path in self.scan_directory(path)? {
57            match self.load_from_file(&plugin_path) {
58                Ok(plugin) => plugins.push(plugin),
59                Err(e) => {
60                    eprintln!("Failed to load plugin {:?}: {}", plugin_path, e);
61                }
62            }
63        }
64        Ok(plugins)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::PluginInfo;
72    
73    // Example plugin implementation for testing
74    struct TestPlugin;
75    
76    #[async_trait::async_trait]
77    impl Plugin for TestPlugin {
78        fn info(&self) -> PluginInfo {
79            PluginInfo::new(
80                "test-plugin",
81                "Test Plugin",
82                "1.0.0",
83                "A test plugin for Oxidite",
84                "Test Author"
85            )
86        }
87    }
88    
89    #[test]
90    fn test_plugin_info() {
91        let plugin = TestPlugin;
92        let info = plugin.info();
93        
94        assert_eq!(info.id, "test-plugin");
95        assert_eq!(info.name, "Test Plugin");
96        assert_eq!(info.version, "1.0.0");
97    }
98}