modcli/loader/
plugins.rs

1use crate::command::Command;
2use libloading::{Library, Symbol};
3use std::fs;
4use std::path::PathBuf;
5
6pub struct PluginLoader {
7    path: PathBuf,
8}
9
10impl PluginLoader {
11    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
12        Self { path: path.into() }
13    }
14
15    pub fn load_plugins(&self) -> Vec<Box<dyn Command>> {
16        let mut plugins = vec![];
17
18        // Determine the platform-specific extension we should load
19        #[cfg(target_os = "linux")]
20        let allowed_ext = "so";
21        #[cfg(target_os = "macos")]
22        let allowed_ext = "dylib";
23        #[cfg(target_os = "windows")]
24        let allowed_ext = "dll";
25
26        if let Ok(entries) = fs::read_dir(&self.path) {
27            for entry in entries.flatten() {
28                let path = entry.path();
29                let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
30
31                // Only try to load the native extension for this platform
32                if ext == allowed_ext {
33                    unsafe {
34                        match Library::new(&path) {
35                            Ok(lib) => {
36                                match lib
37                                    .get::<Symbol<fn() -> Box<dyn Command>>>(b"register_command")
38                                {
39                                    Ok(func) => {
40                                        let command = func();
41                                        plugins.push(command);
42                                        std::mem::forget(lib); // keep plugin alive
43                                    }
44                                    Err(err) => {
45                                        eprintln!(
46                                            "[plugin] missing register_command in {}: {err}",
47                                            path.display()
48                                        );
49                                    }
50                                }
51                            }
52                            Err(err) => {
53                                eprintln!("[plugin] skipping {}: {}", path.display(), err);
54                            }
55                        }
56                    }
57                }
58            }
59        }
60
61        plugins
62    }
63}