use std::{collections::HashMap, marker::PhantomData, path::Path, sync::Arc}; use anyhow::Error; use dlopen2::wrapper::Container; use semver::Version; use tracing::{debug, trace}; use crate::{ callback::{OperationHandlerCallback, SettingGetterCallback, ValueGetterCallback}, new_stack::{ ObjectOperationPair, ObjectSettingPair, ObjectValuePair, PluginMeta, PluginState, RuntimeHandlers, TypeMetadata, }, FFISettingMeta, FFIValueChangeHandler, FFIValueMeta, GiteratedPluginApi, InitializationVTable, ObjectVtable, OperationVTable, SettingVtable, ValueVTable, }; #[derive(Clone)] pub struct PluginHandle { pub meta: PluginMeta, pub raw: Arc>, pub initialization: Arc, pub state: PluginState, } impl PluginHandle { pub fn from_dylib(path: &str) -> Result { let mut handle = unsafe { Container::load(path) }?; // Initialize the raw handle let init_state = Self::initialize_raw_handle(&mut handle)?; let metadata = Self::get_meta(&mut handle)?; let initalization = Self::initialize_registration(&mut handle)?; trace!( "Loaded plugin {} (Version: {})", metadata.name, metadata.version ); Ok(Self { raw: Arc::new(handle), meta: metadata, initialization: Arc::new(initalization), state: init_state, }) } /// Builds the Plugin's Substack. /// /// Builds the Plugin into a substack, which can then be provided to the Giterated Runtime. pub fn build_substack(&mut self) -> Result<(), Error> { todo!() } fn get_meta(handle: &mut Container) -> Result { let meta = unsafe { handle.plugin_meta() }; let name = unsafe { std::slice::from_raw_parts(meta.name, meta.name_len) }; let version = unsafe { std::slice::from_raw_parts(meta.version, meta.version_len) }; let name = std::str::from_utf8(name).unwrap(); let version = std::str::from_utf8(version).unwrap(); Ok(PluginMeta { name: String::from(name), version: Version::parse(version).unwrap(), }) } pub fn initialize_registration( handle: &mut Container, ) -> Result { debug!("Initializing plugin registration..."); let mut builder = PluginInitializationTable::default(); // SAFETY: The lifetime of the returned type is only valid as long // as the builder that returned it lives let func_table = unsafe { builder.func_table() }; let state = Box::new(PluginInitializationState::new()); unsafe { handle.load_initialization_vtable(&func_table) }; let state = unsafe { handle.initialize_registration(Box::into_raw(state)) }; debug!("Plugin handle initialized!"); Ok(unsafe { *Box::from_raw(state) }) } fn initialize_raw_handle( handle: &mut Container, ) -> Result { debug!("Initializing plugin handle..."); let state = unsafe { handle.initialize() }; debug!("Plugin handle initialized!"); Ok(state) } } #[derive(Debug, thiserror::Error)] pub enum CreationError { #[error("an error occured opening the library {0}")] LoadingLibrary(#[from] dlopen2::Error), } pub struct PluginSubstackBuilder {} #[derive(Default)] pub struct PluginInitializationState { pub type_metadata: TypeMetadata, pub operation_handlers: HashMap, OperationHandlerCallback>, pub value_getters: HashMap, ValueGetterCallback>, pub setting_getters: HashMap, SettingGetterCallback>, } impl PluginInitializationState { pub fn new() -> Self { Self::default() } } #[derive(Default)] pub struct PluginInitializationTable<'a> { _marker: PhantomData<&'a ()>, } impl<'a> PluginInitializationTable<'a> { pub unsafe fn func_table(&mut self) -> InitializationVTable { InitializationVTable { register_value_change, register_object, register_operation, register_setting, register_value, operation_handler, value_getter, setting_getter, } } } unsafe extern "C" fn register_value_change( state: *mut PluginInitializationState, object_kind: &str, value_name: &str, vtable: FFIValueChangeHandler, ) { todo!() } unsafe extern "C" fn register_object( state: *mut PluginInitializationState, object_kind: &'static str, vtable: ObjectVtable, ) { let mut state = Box::from_raw(state); state.type_metadata.register_object(object_kind, vtable); Box::into_raw(state); } unsafe extern "C" fn register_operation( state: *mut PluginInitializationState, object_kind: &'static str, operation_name: &'static str, vtable: OperationVTable, ) { let mut state = Box::from_raw(state); state .type_metadata .register_operation(object_kind, operation_name, vtable); Box::into_raw(state); } unsafe extern "C" fn register_setting( state: *mut PluginInitializationState, object_kind: &'static str, setting_name: &'static str, vtable: SettingVtable, ) { let mut state = Box::from_raw(state); state .type_metadata .register_setting(object_kind, setting_name, vtable); Box::into_raw(state); } unsafe extern "C" fn register_value( state: *mut PluginInitializationState, object_kind: &'static str, value_name: &'static str, vtable: ValueVTable, ) { let mut state = Box::from_raw(state); state .type_metadata .register_value(object_kind, value_name, vtable); Box::into_raw(state); } unsafe extern "C" fn operation_handler( state: *mut PluginInitializationState, object_kind: &'static str, operation_name: &'static str, handler: OperationHandlerCallback, ) { let mut state = Box::from_raw(state); trace!("Operation handler for {}::{}", object_kind, operation_name); state.operation_handlers.insert( ObjectOperationPair::new(object_kind, operation_name), handler, ); Box::into_raw(state); } unsafe extern "C" fn value_getter( state: *mut PluginInitializationState, object_kind: &'static str, value_name: &'static str, handler: ValueGetterCallback, ) { let mut state = Box::from_raw(state); trace!("Value getter for {}::{}", object_kind, value_name); state .value_getters .insert(ObjectValuePair::new(object_kind, value_name), handler); Box::into_raw(state); } unsafe extern "C" fn setting_getter( state: *mut PluginInitializationState, object_kind: &'static str, setting_name: &'static str, handler: SettingGetterCallback, ) { let mut state = Box::from_raw(state); trace!("Setting getter for {}::{}", object_kind, setting_name); state .setting_getters .insert(ObjectSettingPair::new(object_kind, setting_name), handler); Box::into_raw(state); }