nautilus_plugin/surfaces/
controller.rs1#![allow(unsafe_code)]
24
25use std::marker::PhantomData;
26
27use nautilus_common::timer::TimeEvent;
28
29use crate::{
30 boundary::{BorrowedStr, OwnedBytes, PluginError, PluginErrorCode, PluginResult},
31 host::{ControllerHostContext, ControllerHostVTable},
32 normalize::BoundaryNormalize,
33 panic::{guard, guard_infallible},
34};
35
36#[repr(C)]
38pub struct PluginControllerHandle {
39 _opaque: [u8; 0],
40}
41
42#[repr(C)]
48pub struct ControllerVTable {
49 pub prepare:
51 Option<unsafe extern "C" fn(request_json: BorrowedStr<'_>) -> PluginResult<OwnedBytes>>,
52
53 pub create: Option<
56 unsafe extern "C" fn(
57 host: *const ControllerHostVTable,
58 ctx: *const ControllerHostContext,
59 config_json: BorrowedStr<'_>,
60 ) -> *mut PluginControllerHandle,
61 >,
62
63 pub drop_handle: Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle)>,
65
66 pub type_name: Option<unsafe extern "C" fn() -> BorrowedStr<'static>>,
68
69 pub on_start:
70 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
71 pub on_stop:
72 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
73 pub on_resume:
74 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
75 pub on_reset:
76 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
77 pub on_dispose:
78 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
79 pub on_degrade:
80 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
81 pub on_fault:
82 Option<unsafe extern "C" fn(handle: *mut PluginControllerHandle) -> PluginResult<()>>,
83
84 pub on_time_event: Option<
85 unsafe extern "C" fn(
86 handle: *mut PluginControllerHandle,
87 event: *const TimeEvent,
88 ) -> PluginResult<()>,
89 >,
90}
91
92pub trait PluginController: 'static + Send + Sized {
98 const TYPE_NAME: &'static str;
100
101 #[allow(unused_variables)]
103 fn prepare(request_json: &str) -> anyhow::Result<Vec<u8>> {
104 Ok(Vec::new())
105 }
106
107 fn new(
110 host: *const ControllerHostVTable,
111 ctx: *const ControllerHostContext,
112 config_json: &str,
113 ) -> Self;
114
115 #[allow(unused_variables)]
116 fn on_start(&mut self) -> anyhow::Result<()> {
117 Ok(())
118 }
119
120 #[allow(unused_variables)]
121 fn on_stop(&mut self) -> anyhow::Result<()> {
122 Ok(())
123 }
124
125 #[allow(unused_variables)]
126 fn on_resume(&mut self) -> anyhow::Result<()> {
127 Ok(())
128 }
129
130 #[allow(unused_variables)]
131 fn on_reset(&mut self) -> anyhow::Result<()> {
132 Ok(())
133 }
134
135 #[allow(unused_variables)]
136 fn on_dispose(&mut self) -> anyhow::Result<()> {
137 Ok(())
138 }
139
140 #[allow(unused_variables)]
141 fn on_degrade(&mut self) -> anyhow::Result<()> {
142 Ok(())
143 }
144
145 #[allow(unused_variables)]
146 fn on_fault(&mut self) -> anyhow::Result<()> {
147 Ok(())
148 }
149
150 #[allow(unused_variables)]
151 fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
152 Ok(())
153 }
154}
155
156#[must_use]
158pub fn controller_vtable<T>() -> *const ControllerVTable
159where
160 T: PluginController,
161{
162 &VTableTag::<T>::VTABLE
163}
164
165struct VTableTag<T>(PhantomData<T>);
166
167impl<T> VTableTag<T>
168where
169 T: PluginController,
170{
171 const VTABLE: ControllerVTable = ControllerVTable {
172 prepare: Some(prepare_thunk::<T>),
173 create: Some(create_thunk::<T>),
174 drop_handle: Some(drop_handle_thunk::<T>),
175 type_name: Some(type_name_thunk::<T>),
176 on_start: Some(on_start_thunk::<T>),
177 on_stop: Some(on_stop_thunk::<T>),
178 on_resume: Some(on_resume_thunk::<T>),
179 on_reset: Some(on_reset_thunk::<T>),
180 on_dispose: Some(on_dispose_thunk::<T>),
181 on_degrade: Some(on_degrade_thunk::<T>),
182 on_fault: Some(on_fault_thunk::<T>),
183 on_time_event: Some(on_time_event_thunk::<T>),
184 };
185}
186
187unsafe extern "C" fn prepare_thunk<T: PluginController>(
188 request_json: BorrowedStr<'_>,
189) -> PluginResult<OwnedBytes> {
190 guard(|| {
191 let request = unsafe { request_json.as_str() };
194 T::prepare(request)
195 .map(OwnedBytes::from_vec)
196 .map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
197 })
198}
199
200unsafe extern "C" fn create_thunk<T: PluginController>(
201 host: *const ControllerHostVTable,
202 ctx: *const ControllerHostContext,
203 config_json: BorrowedStr<'_>,
204) -> *mut PluginControllerHandle {
205 guard_infallible("controller::create", || {
206 let cfg = unsafe { config_json.as_str() };
209 Box::into_raw(Box::new(T::new(host, ctx, cfg))).cast::<PluginControllerHandle>()
210 })
211}
212
213unsafe extern "C" fn drop_handle_thunk<T: PluginController>(handle: *mut PluginControllerHandle) {
214 if handle.is_null() {
215 return;
216 }
217 guard_infallible("controller::drop", || {
218 unsafe {
220 drop(Box::from_raw(handle.cast::<T>()));
221 }
222 });
223}
224
225unsafe extern "C" fn type_name_thunk<T: PluginController>() -> BorrowedStr<'static> {
226 BorrowedStr::from_str(T::TYPE_NAME)
227}
228
229fn handle_as_mut<'a, T: PluginController>(handle: *mut PluginControllerHandle) -> &'a mut T {
230 unsafe { &mut *handle.cast::<T>() }
233}
234
235fn ok_or_err<E: ::core::fmt::Display>(r: Result<(), E>) -> Result<(), PluginError> {
236 r.map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
237}
238
239macro_rules! lifecycle_thunk {
240 ($name:ident, $method:ident) => {
241 unsafe extern "C" fn $name<T: PluginController>(
242 handle: *mut PluginControllerHandle,
243 ) -> PluginResult<()> {
244 guard(|| {
245 let controller = handle_as_mut::<T>(handle);
246 ok_or_err(controller.$method())
247 })
248 }
249 };
250}
251
252lifecycle_thunk!(on_start_thunk, on_start);
253lifecycle_thunk!(on_stop_thunk, on_stop);
254lifecycle_thunk!(on_resume_thunk, on_resume);
255lifecycle_thunk!(on_reset_thunk, on_reset);
256lifecycle_thunk!(on_dispose_thunk, on_dispose);
257lifecycle_thunk!(on_degrade_thunk, on_degrade);
258lifecycle_thunk!(on_fault_thunk, on_fault);
259
260unsafe extern "C" fn on_time_event_thunk<T: PluginController>(
261 handle: *mut PluginControllerHandle,
262 event: *const TimeEvent,
263) -> PluginResult<()> {
264 guard(|| {
265 let event = unsafe { &*event }.boundary_normalized();
268 let controller = handle_as_mut::<T>(handle);
269 ok_or_err(controller.on_time_event(&event))
270 })
271}