#![allow(unsafe_code)]
use std::marker::PhantomData;
use nautilus_common::timer::TimeEvent;
use crate::{
boundary::{BorrowedStr, OwnedBytes, PluginError, PluginErrorCode, PluginResult},
host::{ControllerHostContext, ControllerHostVTable},
normalize::BoundaryNormalize,
panic::{guard, guard_infallible},
};
#[repr(C)]
pub struct PluginControllerHandle {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct ControllerVTable {
pub prepare:
Option<unsafe extern "C" fn(request_json: BorrowedStr<'_>) -> PluginResult<OwnedBytes>>,
pub create: Option<
unsafe extern "C" fn(
host: *const ControllerHostVTable,
ctx: *const ControllerHostContext,
config_json: BorrowedStr<'_>,
) -> *mut PluginControllerHandle,
>,
pub drop_handle: Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle)>,
pub type_name: Option<unsafe extern "C" fn() -> BorrowedStr<'static>>,
pub on_start:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_stop:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_resume:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_reset:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_dispose:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_degrade:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_fault:
Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
pub on_time_event: Option<
unsafe extern "C" fn(
handle: *mut PluginControllerHandle,
event: *const TimeEvent,
) -> PluginResult<()>,
>,
}
pub trait PluginController: 'static + Send + Sized {
const TYPE_NAME: &'static str;
#[allow(unused_variables)]
fn prepare(request_json: &str) -> anyhow::Result<Vec<u8>> {
Ok(Vec::new())
}
fn new(
host: *const ControllerHostVTable,
ctx: *const ControllerHostContext,
config_json: &str,
) -> Self;
#[allow(unused_variables)]
fn on_start(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_stop(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_resume(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_reset(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_dispose(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_degrade(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_fault(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
Ok(())
}
}
#[must_use]
pub fn controller_vtable<T>() -> *const ControllerVTable
where
T: PluginController,
{
&VTableTag::<T>::VTABLE
}
struct VTableTag<T>(PhantomData<T>);
impl<T> VTableTag<T>
where
T: PluginController,
{
const VTABLE: ControllerVTable = ControllerVTable {
prepare: Some(prepare_thunk::<T>),
create: Some(create_thunk::<T>),
drop_handle: Some(drop_handle_thunk::<T>),
type_name: Some(type_name_thunk::<T>),
on_start: Some(on_start_thunk::<T>),
on_stop: Some(on_stop_thunk::<T>),
on_resume: Some(on_resume_thunk::<T>),
on_reset: Some(on_reset_thunk::<T>),
on_dispose: Some(on_dispose_thunk::<T>),
on_degrade: Some(on_degrade_thunk::<T>),
on_fault: Some(on_fault_thunk::<T>),
on_time_event: Some(on_time_event_thunk::<T>),
};
}
unsafe extern "C" fn prepare_thunk<T: PluginController>(
request_json: BorrowedStr<'_>,
) -> PluginResult<OwnedBytes> {
guard(|| {
let request = unsafe { request_json.as_str() };
T::prepare(request)
.map(OwnedBytes::from_vec)
.map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
})
}
unsafe extern "C" fn create_thunk<T: PluginController>(
host: *const ControllerHostVTable,
ctx: *const ControllerHostContext,
config_json: BorrowedStr<'_>,
) -> *mut PluginControllerHandle {
guard_infallible("controller::create", || {
let cfg = unsafe { config_json.as_str() };
Box::into_raw(Box::new(T::new(host, ctx, cfg))).cast::<PluginControllerHandle>()
})
}
unsafe extern "C" fn drop_handle_thunk<T: PluginController>(handle: *mut PluginControllerHandle) {
if handle.is_null() {
return;
}
guard_infallible("controller::drop", || {
unsafe {
drop(Box::from_raw(handle.cast::<T>()));
}
});
}
unsafe extern "C" fn type_name_thunk<T: PluginController>() -> BorrowedStr<'static> {
BorrowedStr::from_str(T::TYPE_NAME)
}
fn handle_as_mut<'a, T: PluginController>(handle: *mut PluginControllerHandle) -> &'a mut T {
unsafe { &mut *handle.cast::<T>() }
}
fn ok_or_err<E: ::core::fmt::Display>(r: Result<(), E>) -> Result<(), PluginError> {
r.map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
}
macro_rules! lifecycle_thunk {
($name:ident, $method:ident) => {
unsafe extern "C" fn $name<T: PluginController>(
handle: *mut PluginControllerHandle,
) -> PluginResult<()> {
guard(|| {
let controller = handle_as_mut::<T>(handle);
ok_or_err(controller.$method())
})
}
};
}
lifecycle_thunk!(on_start_thunk, on_start);
lifecycle_thunk!(on_stop_thunk, on_stop);
lifecycle_thunk!(on_resume_thunk, on_resume);
lifecycle_thunk!(on_reset_thunk, on_reset);
lifecycle_thunk!(on_dispose_thunk, on_dispose);
lifecycle_thunk!(on_degrade_thunk, on_degrade);
lifecycle_thunk!(on_fault_thunk, on_fault);
unsafe extern "C" fn on_time_event_thunk<T: PluginController>(
handle: *mut PluginControllerHandle,
event: *const TimeEvent,
) -> PluginResult<()> {
guard(|| {
let event = unsafe { &*event }.boundary_normalized();
let controller = handle_as_mut::<T>(handle);
ok_or_err(controller.on_time_event(&event))
})
}