mangofetch_core/core/manager/
plugin_manager.rs1use crate::core::manager::plugin_host::CorePluginHost;
2use anyhow::Context;
3use libloading::{Library, Symbol};
4use mangofetch_plugin_sdk::MangoFetchPlugin;
5use std::collections::HashMap;
6use std::path::PathBuf;
7use std::sync::Arc;
8
9type PluginInitFn = unsafe extern "C" fn() -> *mut dyn MangoFetchPlugin;
10
11pub struct PluginInstance {
12 pub plugin: Box<dyn MangoFetchPlugin>,
13 _library: Library, }
15
16pub struct PluginManager {
17 plugins: HashMap<String, PluginInstance>,
18 host: Arc<CorePluginHost>,
19}
20
21impl PluginManager {
22 pub fn new() -> Self {
23 Self {
24 plugins: HashMap::new(),
25 host: Arc::new(CorePluginHost),
26 }
27 }
28
29 pub async fn load_plugins(&mut self) -> anyhow::Result<()> {
30 let plugins_dir = crate::core::paths::app_data_dir()
31 .context("Failed to get app data directory")?
32 .join("plugins");
33
34 if !plugins_dir.exists() {
35 std::fs::create_dir_all(&plugins_dir)?;
36 return Ok(());
37 }
38
39 for entry in std::fs::read_dir(plugins_dir)? {
40 let entry = entry?;
41 let path = entry.path();
42
43 if path.is_file() {
44 if let Some(ext) = path.extension() {
45 let ext_str = ext.to_string_lossy();
46 if ext_str == "dll" || ext_str == "so" || ext_str == "dylib" {
47 if let Err(e) = self.load_single_plugin(path.clone()) {
48 tracing::error!("Failed to load plugin from {:?}: {}", path, e);
49 }
50 }
51 }
52 }
53 }
54
55 Ok(())
56 }
57
58 fn load_single_plugin(&mut self, path: PathBuf) -> anyhow::Result<()> {
59 unsafe {
60 let lib = Library::new(&path).context("Failed to load dynamic library")?;
61
62 let init_fn: Symbol<PluginInitFn> = lib
63 .get(b"mangofetch_plugin_init")
64 .context("Failed to find mangofetch_plugin_init symbol")?;
65
66 let plugin_ptr = init_fn();
67 let mut plugin = Box::from_raw(plugin_ptr);
68
69 plugin
70 .initialize(self.host.clone())
71 .context("Failed to initialize plugin")?;
72
73 let id = plugin.id().to_string();
74 tracing::info!("Loaded plugin: {} ({}) from {:?}", plugin.name(), id, path);
75
76 self.plugins.insert(
77 id,
78 PluginInstance {
79 plugin,
80 _library: lib,
81 },
82 );
83 }
84
85 Ok(())
86 }
87
88 pub fn get_plugin(&self, id: &str) -> Option<&dyn MangoFetchPlugin> {
89 self.plugins.get(id).map(|p| p.plugin.as_ref())
90 }
91
92 pub fn list_plugins(&self) -> Vec<(&String, &str)> {
93 self.plugins
94 .iter()
95 .map(|(id, p)| (id, p.plugin.name()))
96 .collect()
97 }
98}
99
100impl Default for PluginManager {
101 fn default() -> Self {
102 Self::new()
103 }
104}