use crate::inventory::{Inventory, PluginId, PluginInventory};
pub type RegisterTrampolineFn = extern "C" fn(i64, *const u8);
pub type QueryTrampolineFn = extern "C" fn(i64) -> u64;
pub unsafe trait PluginInfo {
fn id() -> PluginId;
fn register(inventory: &mut impl Inventory);
#[must_use]
fn inventory() -> PluginInventory {
let mut inventory = PluginInventory::new();
Self::register(&mut inventory);
inventory
}
}
pub trait Plugin: Sized {
fn load_from(loader: impl Fn(&str) -> *const u8) -> Result<Self, PluginLoadError>;
fn register_trampoline_fn(&self) -> RegisterTrampolineFn;
fn query_trampoline_fn(&self) -> QueryTrampolineFn;
fn verify_api_guard(&self) -> Result<(), PluginLoadError> {
Ok(())
}
}
pub trait Loader {
fn load_plugin<T: Plugin>(&self) -> Result<T, PluginLoadError>;
}
#[derive(Debug)]
pub struct PluginLoadError {
pub kind: PluginLoadErrorKind,
}
#[derive(Debug)]
pub enum PluginLoadErrorKind {
SymbolNotFound(String),
LoadFailed(String),
ApiMismatch { expected: u64, actual: u64 },
}
impl PluginLoadError {
pub fn symbol_not_found(name: impl Into<String>) -> Self {
Self { kind: PluginLoadErrorKind::SymbolNotFound(name.into()) }
}
pub fn load_failed(msg: impl Into<String>) -> Self {
Self { kind: PluginLoadErrorKind::LoadFailed(msg.into()) }
}
#[must_use]
pub fn api_mismatch(expected: u64, actual: u64) -> Self {
Self { kind: PluginLoadErrorKind::ApiMismatch { expected, actual } }
}
}
impl std::fmt::Display for PluginLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.kind {
PluginLoadErrorKind::SymbolNotFound(name) => write!(f, "symbol not found: {name}"),
PluginLoadErrorKind::LoadFailed(msg) => write!(f, "failed to load plugin: {msg}"),
PluginLoadErrorKind::ApiMismatch { expected, actual } => {
write!(f, "API mismatch: Rust side expects hash 0x{expected:016x} but plugin reports 0x{actual:016x}. Rebuild the plugin against the current Rust API.")
}
}
}
}
impl std::error::Error for PluginLoadError {}