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