use std::mem::ManuallyDrop;
use std::path::Path;
use std::sync::Arc;
use libloading::Library;
use thiserror::Error;
use crate::native_abi::CheckedPluginRoot;
use crate::{LoadedNativePlugin, NativePluginContractError};
#[derive(Debug, Error)]
pub enum PluginLoadError {
#[error("failed to open plugin library at {path}: {source}")]
OpenLibrary {
path: String,
#[source]
source: libloading::Error,
},
#[error("failed to resolve plugin entry symbol `{symbol}`: {source}")]
ResolveEntrySymbol {
symbol: &'static str,
#[source]
source: libloading::Error,
},
#[error("native plugin root violates the ABI contract: {0}")]
NativeContract(#[from] NativePluginContractError),
}
impl LoadedNativePlugin {
pub fn load_development(path: impl AsRef<Path>) -> Result<Self, PluginLoadError> {
let path = path.as_ref();
tracing::warn!(
path = %path.display(),
"loading an unsigned raw Native plugin library under explicit development policy"
);
Self::load_unchecked(path)
}
pub(crate) fn load_host_verified(path: impl AsRef<Path>) -> Result<Self, PluginLoadError> {
Self::load_unchecked(path.as_ref())
}
fn load_unchecked(path: &Path) -> Result<Self, PluginLoadError> {
let path_string = path.display().to_string();
let library =
unsafe { Library::new(path) }.map_err(|source| PluginLoadError::OpenLibrary {
path: path_string,
source,
})?;
let library = Arc::new(LibraryHolder {
library: ManuallyDrop::new(library),
});
let entry = unsafe {
library
.library
.get::<player_plugin_abi::VesperPluginEntryPoint>(
player_plugin_abi::VESPER_PLUGIN_ENTRY_SYMBOL,
)
}
.map_err(|source| PluginLoadError::ResolveEntrySymbol {
symbol: player_plugin_abi::VESPER_PLUGIN_ENTRY_SYMBOL_NAME,
source,
})?;
let root = unsafe { entry() };
let checked =
unsafe { CheckedPluginRoot::from_raw(root, Some(library)) }?;
Ok(Self::from_checked(checked))
}
}
#[derive(Debug)]
pub(crate) struct LibraryHolder {
#[allow(dead_code)]
pub(crate) library: ManuallyDrop<Library>,
}