use std::{
ffi::{c_ulong, c_void},
sync::Arc,
};
use async_cffi::{CffiFuture, CffiPointerBuffer, SafePtr};
use futures::future::BoxFuture;
use n_observer::{AnyArc, InnerObserverReceiver, Observable, Publisher};
use crate::CffiInnerObserverReceiver;
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CffiPublisher {
pub self_ptr: *const c_void,
pub add_observer_fut:
extern "C" fn(*const c_void, CffiInnerObserverReceiver, c_ulong) -> *const c_void,
pub notify_fut: extern "C" fn(*const c_void, *const c_void) -> *const c_void,
}
impl Publisher for CffiPublisher {
fn add_observer(
&self,
observer: Box<dyn InnerObserverReceiver>,
input_index: usize,
) -> BoxFuture<'_, Option<AnyArc>> {
let observer = {
let cffi_observer = (*observer).into();
Box::leak(observer);
cffi_observer
};
let input_index = input_index as c_ulong;
let add_observer_fn = self.add_observer_fut;
let self_ptr = SafePtr(self.self_ptr);
Box::pin(async move {
let self_ptr = self_ptr;
let observer = observer;
let input_index = input_index;
let fut = (add_observer_fn)(self_ptr.0, observer, input_index);
if fut.is_null() {
panic!("C function returned null pointer");
}
let fut = unsafe {
(fut as *mut CffiFuture)
.as_mut()
.expect("CffiFuture cannot be null")
};
let ret = fut.await;
assert!(!ret.is_null());
let ret = ret as *const *const c_void;
let ret = *unsafe { ret.as_ref().unwrap() };
let ret = if ret.is_null() {
None
} else {
Some(Arc::new(SafePtr(ret)) as AnyArc)
};
ret
})
}
fn notify(&self, data: AnyArc) -> BoxFuture<'_, ()> {
let data = *data.downcast::<SafePtr>().expect("data must be SafePtr");
let notify_fn = self.notify_fut;
let self_ptr = SafePtr(self.self_ptr);
Box::pin(async move {
let self_ptr = self_ptr;
let data = data;
let fut = (notify_fn)(self_ptr.0, data.0);
if fut.is_null() {
panic!("C function returned null pointer");
}
let fut = unsafe {
(fut as *mut CffiFuture)
.as_mut()
.expect("CffiFuture cannot be null")
};
fut.await;
})
}
}
unsafe impl Send for CffiPublisher {}
unsafe impl Sync for CffiPublisher {}
impl CffiPublisher {
extern "C" fn add_observer_fut_impl(
self_ptr: *const c_void,
observer: CffiInnerObserverReceiver,
input_index: c_ulong,
) -> *const c_void {
let self_ref = unsafe {
(self_ptr as *const &(dyn Publisher + Send + Sync))
.as_ref()
.expect("Self pointer cannot be null")
};
let observer = Box::new(observer);
let input_index = input_index as usize;
let fut = Box::pin(async move {
let ret = self_ref.add_observer(observer, input_index).await;
let ret = ret
.map(|ret| ret.downcast::<SafePtr>().unwrap().0)
.unwrap_or(std::ptr::null());
SafePtr(ret)
});
CffiFuture::from_rust_future_boxed(fut).into_raw()
}
extern "C" fn notify_fut_impl(self_ptr: *const c_void, data: *const c_void) -> *const c_void {
let self_ref = unsafe {
(self_ptr as *const &(dyn Publisher + Send + Sync))
.as_ref()
.expect("Self pointer cannot be null")
};
let data = Arc::new(SafePtr(data));
let fut = self_ref.notify(data);
CffiFuture::from_rust_future_boxed(fut).into_raw()
}
}
impl From<&dyn Publisher> for CffiPublisher {
fn from(inner: &dyn Publisher) -> Self {
CffiPublisher {
self_ptr: Box::into_raw(Box::new(inner)) as *const c_void,
add_observer_fut: CffiPublisher::add_observer_fut_impl,
notify_fut: CffiPublisher::notify_fut_impl,
}
}
}