Pumpkin/pumpkin/src/plugin/loader/native.rs
2025-12-08 20:52:03 +01:00

71 lines
2.1 KiB
Rust

use std::any::Any;
use libloading::Library;
use crate::plugin::loader::{PluginLoadFuture, PluginUnloadFuture};
use super::{LoaderError, Path, Plugin, PluginLoader, PluginMetadata};
pub struct NativePluginLoader;
impl PluginLoader for NativePluginLoader {
fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> {
Box::pin(async {
let path = path.to_owned();
let library = unsafe { Library::new(&path) }
.map_err(|e| LoaderError::LibraryLoad(e.to_string()))?;
// 2. Extract Metadata (METADATA)
let metadata = unsafe {
&**library
.get::<*const PluginMetadata>(b"METADATA")
.map_err(|_| LoaderError::MetadataMissing)?
};
// 3. Extract Plugin Factory (plugin)
let plugin_factory = unsafe {
library
.get::<fn() -> Box<dyn Plugin>>(b"plugin")
.map_err(|_| LoaderError::EntrypointMissing)?
};
Ok((
plugin_factory(),
metadata.clone(),
Box::new(library) as Box<dyn Any + Send + Sync>,
))
})
}
fn can_load(&self, path: &Path) -> bool {
let ext = path
.extension()
.and_then(|s| s.to_str())
.unwrap_or_default();
if cfg!(target_os = "windows") {
ext.eq_ignore_ascii_case("dll")
} else if cfg!(target_os = "macos") {
ext.eq_ignore_ascii_case("dylib")
} else {
ext.eq_ignore_ascii_case("so")
}
}
fn unload(&self, data: Box<dyn Any + Send + Sync>) -> PluginUnloadFuture<'_> {
Box::pin(async {
data.downcast::<Library>()
.map_or(Err(LoaderError::InvalidLoaderData), |library| {
drop(library);
Ok(())
})
})
}
/// Windows specific issue: Windows locks DLLs, so we must indicate they cannot be unloaded.
fn can_unload(&self) -> bool {
!cfg!(target_os = "windows")
}
}