1use crate::binary_types::RbResponse;
6use crate::buffer::FfiBuffer;
7use crate::handle::{PluginHandle, PluginHandleManager};
8use crate::panic_guard::catch_panic;
9use dashmap::DashMap;
10use once_cell::sync::OnceCell;
11use rustbridge_core::{LogLevel, PluginConfig};
12use rustbridge_logging::{LogCallback, LogCallbackManager};
13use rustbridge_transport::ResponseEnvelope;
14use std::ffi::c_void;
15use std::panic::AssertUnwindSafe;
16use std::ptr;
17
18pub type FfiPluginHandle = *mut c_void;
20
21#[unsafe(no_mangle)]
37pub unsafe extern "C" fn plugin_init(
38 plugin_ptr: *mut c_void,
39 config_json: *const u8,
40 config_len: usize,
41 log_callback: Option<LogCallback>,
42) -> FfiPluginHandle {
43 match catch_panic(
45 0,
46 AssertUnwindSafe(|| unsafe {
47 plugin_init_impl(plugin_ptr, config_json, config_len, log_callback)
48 }),
49 ) {
50 Ok(handle) => handle,
51 Err(_error_buffer) => {
52 ptr::null_mut()
55 }
56 }
57}
58
59unsafe fn plugin_init_impl(
61 plugin_ptr: *mut c_void,
62 config_json: *const u8,
63 config_len: usize,
64 log_callback: Option<LogCallback>,
65) -> FfiPluginHandle {
66 if plugin_ptr.is_null() {
68 return ptr::null_mut();
69 }
70
71 let config = if config_json.is_null() || config_len == 0 {
73 PluginConfig::default()
74 } else {
75 let config_slice = unsafe { std::slice::from_raw_parts(config_json, config_len) };
77 match PluginConfig::from_json(config_slice) {
78 Ok(c) => c,
79 Err(e) => {
80 eprintln!("Failed to parse config: {}", e);
82 return ptr::null_mut();
83 }
84 }
85 };
86
87 LogCallbackManager::global().register_plugin(log_callback);
89
90 LogCallbackManager::global().set_level(config.log_level);
92
93 rustbridge_logging::init_logging();
95
96 crate::panic_guard::install_panic_hook();
98
99 let plugin: Box<Box<dyn rustbridge_core::Plugin>> =
102 unsafe { Box::from_raw(plugin_ptr as *mut Box<dyn rustbridge_core::Plugin>) };
103
104 let handle = match PluginHandle::new(*plugin, config) {
106 Ok(h) => h,
107 Err(e) => {
108 tracing::error!("Failed to create handle: {}", e);
109 return ptr::null_mut();
110 }
111 };
112
113 if let Err(e) = handle.start() {
115 tracing::error!("Failed to start plugin: {}", e);
116 return ptr::null_mut();
117 }
118
119 let id = PluginHandleManager::global().register(handle);
121
122 if let Some(h) = PluginHandleManager::global().get(id) {
124 h.set_id(id);
125 }
126
127 id as FfiPluginHandle
128}
129
130#[unsafe(no_mangle)]
146pub unsafe extern "C" fn plugin_call(
147 handle: FfiPluginHandle,
148 type_tag: *const std::ffi::c_char,
149 request: *const u8,
150 request_len: usize,
151) -> FfiBuffer {
152 let handle_id = handle as u64;
154 match catch_panic(
155 handle_id,
156 AssertUnwindSafe(|| unsafe { plugin_call_impl(handle, type_tag, request, request_len) }),
157 ) {
158 Ok(result) => result,
159 Err(error_buffer) => error_buffer,
160 }
161}
162
163unsafe fn plugin_call_impl(
165 handle: FfiPluginHandle,
166 type_tag: *const std::ffi::c_char,
167 request: *const u8,
168 request_len: usize,
169) -> FfiBuffer {
170 let id = handle as u64;
172 let plugin_handle = match PluginHandleManager::global().get(id) {
173 Some(h) => h,
174 None => return FfiBuffer::error(1, "Invalid handle"),
175 };
176
177 let type_tag_str = if type_tag.is_null() {
179 return FfiBuffer::error(4, "Type tag is null");
180 } else {
181 match unsafe { std::ffi::CStr::from_ptr(type_tag) }.to_str() {
183 Ok(s) => s,
184 Err(_) => return FfiBuffer::error(4, "Invalid type tag encoding"),
185 }
186 };
187
188 let request_data = if request.is_null() || request_len == 0 {
190 &[]
191 } else {
192 unsafe { std::slice::from_raw_parts(request, request_len) }
194 };
195
196 match plugin_handle.call(type_tag_str, request_data) {
198 Ok(response_data) => {
199 match ResponseEnvelope::success_raw(&response_data) {
201 Ok(envelope) => match envelope.to_bytes() {
202 Ok(bytes) => FfiBuffer::from_vec(bytes),
203 Err(e) => FfiBuffer::error(5, &format!("Serialization error: {}", e)),
204 },
205 Err(e) => FfiBuffer::error(5, &format!("Serialization error: {}", e)),
206 }
207 }
208 Err(e) => {
209 let envelope = ResponseEnvelope::from_error(&e);
210 match envelope.to_bytes() {
211 Ok(bytes) => {
212 let mut buf = FfiBuffer::from_vec(bytes);
213 buf.error_code = e.error_code();
214 buf
215 }
216 Err(se) => FfiBuffer::error(e.error_code(), &format!("{}: {}", e, se)),
217 }
218 }
219 }
220}
221
222#[unsafe(no_mangle)]
228pub unsafe extern "C" fn plugin_free_buffer(buffer: *mut FfiBuffer) {
229 unsafe {
230 if !buffer.is_null() {
231 (*buffer).free();
232 }
233 }
234}
235
236#[unsafe(no_mangle)]
248pub unsafe extern "C" fn plugin_shutdown(handle: FfiPluginHandle) -> bool {
249 let handle_id = handle as u64;
251 catch_panic(handle_id, AssertUnwindSafe(|| plugin_shutdown_impl(handle))).unwrap_or_default() }
253
254fn plugin_shutdown_impl(handle: FfiPluginHandle) -> bool {
256 let id = handle as u64;
257
258 let plugin_handle = match PluginHandleManager::global().remove(id) {
260 Some(h) => h,
261 None => return false,
262 };
263
264 let result = match plugin_handle.shutdown(5000) {
266 Ok(()) => true,
267 Err(e) => {
268 tracing::error!("Shutdown error: {}", e);
269 false
270 }
271 };
272
273 clear_binary_handlers();
275
276 LogCallbackManager::global().unregister_plugin();
280
281 result
282}
283
284#[unsafe(no_mangle)]
293pub unsafe extern "C" fn plugin_set_log_level(handle: FfiPluginHandle, level: u8) {
294 let id = handle as u64;
295
296 if let Some(plugin_handle) = PluginHandleManager::global().get(id) {
297 plugin_handle.set_log_level(LogLevel::from_u8(level));
298 }
299}
300
301#[unsafe(no_mangle)]
313pub unsafe extern "C" fn plugin_get_state(handle: FfiPluginHandle) -> u8 {
314 let id = handle as u64;
315
316 match PluginHandleManager::global().get(id) {
317 Some(h) => match h.state() {
318 rustbridge_core::LifecycleState::Installed => 0,
319 rustbridge_core::LifecycleState::Starting => 1,
320 rustbridge_core::LifecycleState::Active => 2,
321 rustbridge_core::LifecycleState::Stopping => 3,
322 rustbridge_core::LifecycleState::Stopped => 4,
323 rustbridge_core::LifecycleState::Failed => 5,
324 },
325 None => 255,
326 }
327}
328
329#[unsafe(no_mangle)]
340pub unsafe extern "C" fn plugin_get_rejected_count(handle: FfiPluginHandle) -> u64 {
341 let id = handle as u64;
342 match PluginHandleManager::global().get(id) {
343 Some(h) => h.rejected_request_count(),
344 None => 0,
345 }
346}
347
348pub type BinaryMessageHandler =
358 fn(handle: &PluginHandle, request: &[u8]) -> Result<Vec<u8>, rustbridge_core::PluginError>;
359
360static BINARY_HANDLERS: OnceCell<DashMap<u32, BinaryMessageHandler>> = OnceCell::new();
366
367fn binary_handlers() -> &'static DashMap<u32, BinaryMessageHandler> {
369 BINARY_HANDLERS.get_or_init(DashMap::new)
370}
371
372pub fn register_binary_handler(message_id: u32, handler: BinaryMessageHandler) {
377 binary_handlers().insert(message_id, handler);
378}
379
380pub(crate) fn clear_binary_handlers() {
385 binary_handlers().clear();
386}
387
388#[unsafe(no_mangle)]
404pub unsafe extern "C" fn plugin_call_raw(
405 handle: FfiPluginHandle,
406 message_id: u32,
407 request: *const c_void,
408 request_size: usize,
409) -> RbResponse {
410 let handle_id = handle as u64;
412 match catch_panic(
413 handle_id,
414 AssertUnwindSafe(|| unsafe {
415 plugin_call_raw_impl(handle, message_id, request, request_size)
416 }),
417 ) {
418 Ok(result) => result,
419 Err(error_buffer) => {
420 let msg = if error_buffer.is_error() && !error_buffer.data.is_null() {
422 let slice =
424 unsafe { std::slice::from_raw_parts(error_buffer.data, error_buffer.len) };
425 String::from_utf8_lossy(slice).into_owned()
426 } else {
427 "Internal error (panic)".to_string()
428 };
429 let mut buf = error_buffer;
431 unsafe { buf.free() };
433 RbResponse::error(11, &msg)
434 }
435 }
436}
437
438unsafe fn plugin_call_raw_impl(
440 handle: FfiPluginHandle,
441 message_id: u32,
442 request: *const c_void,
443 request_size: usize,
444) -> RbResponse {
445 let id = handle as u64;
447 let plugin_handle = match PluginHandleManager::global().get(id) {
448 Some(h) => h,
449 None => return RbResponse::error(1, "Invalid handle"),
450 };
451
452 if !plugin_handle.state().can_handle_requests() {
454 return RbResponse::error(1, "Plugin not in Active state");
455 }
456
457 let request_data = if request.is_null() || request_size == 0 {
459 &[]
460 } else {
461 unsafe { std::slice::from_raw_parts(request as *const u8, request_size) }
463 };
464
465 let handler = binary_handlers().get(&message_id).map(|r| *r);
467
468 match handler {
469 Some(h) => {
470 match h(&plugin_handle, request_data) {
472 Ok(response_bytes) => {
473 let mut response = RbResponse::empty();
476 let len = response_bytes.len();
477 let capacity = response_bytes.capacity();
478 let data = response_bytes.leak().as_mut_ptr();
479
480 response.error_code = 0;
481 response.len = len as u32;
482 response.capacity = capacity as u32;
483 response.data = data as *mut c_void;
484
485 response
486 }
487 Err(e) => RbResponse::error(e.error_code(), &e.to_string()),
488 }
489 }
490 None => RbResponse::error(6, &format!("Unknown message ID: {}", message_id)),
491 }
492}
493
494#[unsafe(no_mangle)]
500pub unsafe extern "C" fn rb_response_free(response: *mut RbResponse) {
501 unsafe {
502 if !response.is_null() {
503 (*response).free();
504 }
505 }
506}
507
508#[cfg(test)]
513#[path = "exports/exports_tests.rs"]
514mod exports_tests;
515
516#[cfg(test)]
517#[path = "exports/ffi_boundary_tests.rs"]
518mod ffi_boundary_tests;