1use std::{ffi::CStr, os::raw::c_char, sync::Arc};
4
5use xaeroflux::actors::subject::Subject;
6use xaeroflux::{XaeroPoolManager, event::XaeroEvent};
8
9#[repr(C)]
11pub struct FfiSubject {
12 _private: [u8; 0],
13}
14
15#[unsafe(no_mangle)]
23pub unsafe extern "C" fn xf_subject_new(
24 name: *const c_char,
25 workspace_name: *const c_char,
26 object_name: *const c_char,
27) -> *mut FfiSubject {
28 if name.is_null() || workspace_name.is_null() || object_name.is_null() {
30 return std::ptr::null_mut();
31 }
32
33 XaeroPoolManager::init();
35
36 let name_rs = match unsafe { CStr::from_ptr(name) }.to_str() {
38 Ok(s) => s,
39 Err(_) => return std::ptr::null_mut(),
40 };
41 let workspace_name_rs = match unsafe { CStr::from_ptr(workspace_name) }.to_str() {
42 Ok(s) => s,
43 Err(_) => return std::ptr::null_mut(),
44 };
45 let object_name_rs = match unsafe { CStr::from_ptr(object_name) }.to_str() {
46 Ok(s) => s,
47 Err(_) => return std::ptr::null_mut(),
48 };
49
50 let mut hasher = blake3::Hasher::new();
52 hasher.update(workspace_name_rs.as_bytes());
53 let workspace_name_hash_rs = hasher.finalize();
54
55 let subject = match std::panic::catch_unwind(|| {
57 Subject::new_with_workspace(
58 String::from(name_rs),
59 *workspace_name_hash_rs.as_bytes(),
60 String::from(workspace_name_rs),
61 String::from(object_name_rs),
62 )
63 }) {
64 Ok(s) => s,
65 Err(_) => return std::ptr::null_mut(),
66 };
67
68 let boxed_subject = Box::new(subject);
70 Box::into_raw(boxed_subject) as *mut FfiSubject
71}
72
73pub type MapCallback = extern "C" fn(evt: *const XaeroEvent) -> *mut XaeroEvent;
78
79#[unsafe(no_mangle)]
80pub extern "C" fn xf_subject_map(handle: *mut FfiSubject, cb: MapCallback) -> *mut FfiSubject {
81 if handle.is_null() {
82 return std::ptr::null_mut();
83 }
84
85 let _subject = unsafe { &mut *(handle as *mut Subject) };
87
88 let _rust_callback = move |evt: Arc<XaeroEvent>| -> Arc<XaeroEvent> {
90 let raw_evt = Arc::as_ptr(&evt);
92 let result_ptr = cb(raw_evt);
93
94 if result_ptr.is_null() {
95 evt
97 } else {
98 unsafe { Arc::from_raw(result_ptr) }
101 }
102 };
103
104 handle
109}
110
111pub type FilterCallback = extern "C" fn(evt: *const XaeroEvent) -> bool;
116
117#[unsafe(no_mangle)]
118pub extern "C" fn xf_subject_filter(
119 handle: *mut FfiSubject,
120 cb: FilterCallback,
121) -> *mut FfiSubject {
122 if handle.is_null() {
123 return std::ptr::null_mut();
124 }
125
126 let _subject = unsafe { &mut *(handle as *mut Subject) };
128
129 let _rust_callback = move |evt: &Arc<XaeroEvent>| -> bool {
131 let raw_evt = Arc::as_ptr(evt);
133 cb(raw_evt)
134 };
135
136 handle
141}
142
143#[unsafe(no_mangle)]
147pub extern "C" fn xf_subject_filter_merkle_proofs(handle: *mut FfiSubject) -> *mut FfiSubject {
148 if handle.is_null() {
149 return std::ptr::null_mut();
150 }
151
152 let _subject = unsafe { &mut *(handle as *mut Subject) };
154
155 let _merkle_filter = |evt: &Arc<XaeroEvent>| -> bool { evt.merkle_proof().is_some() };
157
158 handle
163}
164
165#[unsafe(no_mangle)]
169pub extern "C" fn xf_subject_blackhole(handle: *mut FfiSubject) -> *mut FfiSubject {
170 if handle.is_null() {
171 return std::ptr::null_mut();
172 }
173
174 let _subject = unsafe { &mut *(handle as *mut Subject) };
176
177 handle
182}
183
184#[unsafe(no_mangle)]
190pub extern "C" fn xf_subject_unsafe_run(handle: *mut FfiSubject) {
191 if handle.is_null() {
192 return;
193 }
194
195 let _ = std::panic::catch_unwind(|| {
197 let _subject = unsafe { Box::from_raw(handle as *mut Subject) };
199 });
201}
202
203#[unsafe(no_mangle)]
208pub unsafe extern "C" fn xf_event_get_data(
209 evt: *const XaeroEvent,
210 out_len: *mut usize,
211) -> *const u8 {
212 if evt.is_null() || out_len.is_null() {
213 return std::ptr::null();
214 }
215
216 let event = unsafe { &*evt };
217 let data = event.data();
218 unsafe { *out_len = data.len() };
219 data.as_ptr()
220}
221
222#[unsafe(no_mangle)]
226pub unsafe extern "C" fn xf_event_get_type(evt: *const XaeroEvent) -> u8 {
227 if evt.is_null() {
228 return 0;
229 }
230
231 let event = unsafe { &*evt };
232 event.event_type()
233}
234
235#[unsafe(no_mangle)]
239pub unsafe extern "C" fn xf_event_get_timestamp(evt: *const XaeroEvent) -> u64 {
240 if evt.is_null() {
241 return 0;
242 }
243
244 let event = unsafe { &*evt };
245 event.latest_ts
246}
247
248#[unsafe(no_mangle)]
252pub unsafe extern "C" fn xf_event_create(
253 data: *const u8,
254 data_len: usize,
255 event_type: u8,
256 timestamp: u64,
257) -> *mut XaeroEvent {
258 if data.is_null() {
259 return std::ptr::null_mut();
260 }
261
262 let data_slice = unsafe { std::slice::from_raw_parts(data, data_len) };
264
265 match XaeroPoolManager::create_xaero_event(
267 data_slice, event_type, None, None, None, timestamp,
271 ) {
272 Ok(event) => {
273 let raw_ptr = Arc::into_raw(event);
276 raw_ptr as *mut XaeroEvent
277 }
278 Err(_) => {
279 std::ptr::null_mut()
281 }
282 }
283}
284
285#[unsafe(no_mangle)]
289pub unsafe extern "C" fn xf_event_free(evt: *mut XaeroEvent) {
290 if !evt.is_null() {
291 let _event = unsafe { Arc::from_raw(evt as *const XaeroEvent) };
294 }
296}
297
298#[cfg(test)]
299mod ffi_tests {
300 use std::ffi::CString;
301
302 use xaeroflux::{event::EventType, initialize};
303
304 use super::*;
305
306 #[allow(dead_code)]
307 fn test_subject_creation_and_cleanup() {
309 initialize();
311 XaeroPoolManager::init();
312
313 let name = CString::new("test_subject").expect("failed_to_unravel");
314 let workspace = CString::new("test_workspace").expect("failed_to_unravel");
315 let object = CString::new("test_object").expect("failed_to_unravel");
316
317 let subject_ptr =
318 unsafe { xf_subject_new(name.as_ptr(), workspace.as_ptr(), object.as_ptr()) };
319
320 assert!(!subject_ptr.is_null(), "Subject creation failed");
321
322 xf_subject_unsafe_run(subject_ptr);
324 }
325
326 #[allow(dead_code)]
327 fn test_event_helpers() {
329 initialize();
331 XaeroPoolManager::init();
332
333 let test_data = b"small"; let event_type = EventType::ApplicationEvent(1).to_u8();
335 let timestamp = 12345;
336
337 let event_ptr =
339 unsafe { xf_event_create(test_data.as_ptr(), test_data.len(), event_type, timestamp) };
340
341 assert!(!event_ptr.is_null(), "Failed to create event");
342
343 let mut data_len: usize = 0;
345 let data_ptr = unsafe { xf_event_get_data(event_ptr, &mut data_len) };
346 assert!(!data_ptr.is_null(), "Failed to get event data");
347 assert_eq!(data_len, test_data.len());
348
349 let retrieved_data = unsafe { std::slice::from_raw_parts(data_ptr, data_len) };
350 assert_eq!(retrieved_data, test_data);
351
352 let retrieved_type = unsafe { xf_event_get_type(event_ptr) };
354 assert_eq!(retrieved_type, event_type);
355
356 let retrieved_timestamp = unsafe { xf_event_get_timestamp(event_ptr) };
358 assert_eq!(retrieved_timestamp, timestamp);
359
360 unsafe { xf_event_free(event_ptr) };
362 }
363}