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 #[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 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); }
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}