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::CffiPublisher;
use crate::CffiInnerObserverReceiver;
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CffiObservable {
pub self_ptr: *const c_void,
pub cffi_publisher: CffiPublisher,
pub cffi_inner_observer_receiver: CffiInnerObserverReceiver,
pub get_fut: extern "C" fn(*const c_void) -> *const c_void,
}
impl Publisher for CffiObservable {
fn add_observer(
&self,
observer: Box<dyn InnerObserverReceiver>,
input_index: usize,
) -> BoxFuture<'_, Option<AnyArc>> {
self.as_publisher().add_observer(observer, input_index)
}
fn notify(&self, data: AnyArc) -> BoxFuture<'_, ()> {
self.as_publisher().notify(data)
}
}
impl InnerObserverReceiver for CffiObservable {
fn update(&self, data: Vec<Option<AnyArc>>) -> BoxFuture<'_, ()> {
self.as_inner_observer_receiver().update(data)
}
fn hold_strong_publisher_ref(
&self,
publisher: Arc<dyn Publisher + Send + Sync>,
) -> BoxFuture<'_, ()> {
self.as_inner_observer_receiver()
.hold_strong_publisher_ref(publisher)
}
}
impl Observable<SafePtr> for CffiObservable {
fn get(&self) -> BoxFuture<'_, Option<Arc<SafePtr>>> {
let get_fn = self.get_fut;
let self_ptr = SafePtr(self.self_ptr);
Box::pin(async move {
let self_ptr = self_ptr;
let fut = (get_fn)(self_ptr.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")
};
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)))
};
ret
})
}
}
unsafe impl Send for CffiObservable {}
unsafe impl Sync for CffiObservable {}
impl CffiObservable {
fn as_publisher(&self) -> &dyn Publisher {
&self.cffi_publisher
}
fn as_inner_observer_receiver(&self) -> &dyn InnerObserverReceiver {
&self.cffi_inner_observer_receiver
}
extern "C" fn get_fut_impl(self_ptr: *const c_void) -> *const c_void {
let self_ref = unsafe {
(self_ptr as *const &(dyn Observable<SafePtr> + Send + Sync))
.as_ref()
.expect("Self pointer cannot be null")
};
let fut = Box::pin(async move {
let ret = self_ref.get().await;
let ret = ret
.map(|ret| {
let ret = *ret;
ret.0
})
.unwrap_or(std::ptr::null());
SafePtr(ret)
});
CffiFuture::from_rust_future_boxed(fut).into_raw()
}
}
impl From<&dyn Observable<SafePtr>> for CffiObservable {
fn from(inner: &dyn Observable<SafePtr>) -> Self {
CffiObservable {
self_ptr: Box::into_raw(Box::new(inner)) as *const c_void,
cffi_publisher: {
let publisher = Box::new(inner as &dyn Publisher);
let ret: CffiPublisher = (*publisher).into();
Box::leak(publisher);
ret
},
cffi_inner_observer_receiver: {
let inner_observer_receiver = Box::new(inner as &dyn InnerObserverReceiver);
let ret: CffiInnerObserverReceiver = (*inner_observer_receiver).into();
Box::leak(inner_observer_receiver);
ret
},
get_fut: CffiObservable::get_fut_impl,
}
}
}