use std::sync::Mutex;
use super::bootstrap_stream::{
release_bootstrap_receiver, release_result_receiver, wrap_result_receiver,
BootstrapStreamConsumer,
};
use super::types::FfiStr;
use super::vtables::BootstrapProviderVtable;
use drasi_lib::bootstrap::{BootstrapProvider, BootstrapResult};
pub struct FfiBootstrapProviderProxy {
pub(crate) vtable: Mutex<BootstrapProviderVtable>,
}
unsafe impl Send for FfiBootstrapProviderProxy {}
unsafe impl Sync for FfiBootstrapProviderProxy {}
impl FfiBootstrapProviderProxy {
pub fn new(vtable: BootstrapProviderVtable) -> Self {
Self {
vtable: Mutex::new(vtable),
}
}
}
#[async_trait::async_trait]
impl BootstrapProvider for FfiBootstrapProviderProxy {
async fn bootstrap(
&self,
request: drasi_lib::bootstrap::BootstrapRequest,
context: &drasi_lib::bootstrap::BootstrapContext,
event_tx: drasi_lib::channels::events::BootstrapEventSender,
_settings: Option<&drasi_lib::config::SourceSubscriptionSettings>,
) -> anyhow::Result<BootstrapResult> {
let (consumer, (result_rx, _result_guard)) = {
let (vtable_state, vtable_bootstrap_fn) = {
let vtable = self.vtable.lock().expect("vtable mutex poisoned");
(vtable.state, vtable.bootstrap_fn)
};
let node_ffi: Vec<FfiStr> = request
.node_labels
.iter()
.map(|s| FfiStr::from_str(s))
.collect();
let rel_ffi: Vec<FfiStr> = request
.relation_labels
.iter()
.map(|s| FfiStr::from_str(s))
.collect();
let stream_ptr = (vtable_bootstrap_fn)(
vtable_state,
FfiStr::from_str(&request.query_id),
node_ffi.as_ptr(),
node_ffi.len(),
rel_ffi.as_ptr(),
rel_ffi.len(),
FfiStr::from_str(&request.request_id),
FfiStr::from_str(&context.server_id),
FfiStr::from_str(&context.source_id),
);
if stream_ptr.is_null() {
anyhow::bail!("Bootstrap provider failed to start (null stream)");
}
let stream = unsafe { *Box::from_raw(stream_ptr) };
if stream.events.is_null() || stream.result.is_null() {
if !stream.events.is_null() {
release_bootstrap_receiver(unsafe { *Box::from_raw(stream.events) });
}
if !stream.result.is_null() {
release_result_receiver(unsafe { *Box::from_raw(stream.result) });
}
anyhow::bail!("Bootstrap provider returned an incomplete stream");
}
let events = unsafe { *Box::from_raw(stream.events) };
let result = unsafe { *Box::from_raw(stream.result) };
(
BootstrapStreamConsumer::new(events),
wrap_result_receiver(result),
)
};
let forwarded = consumer.forward_into(&event_tx).await;
let outcome = result_rx
.await
.map_err(|_| anyhow::anyhow!("Bootstrap result channel dropped without a result"))?;
let result = outcome?;
if result.event_count != forwarded {
log::warn!(
"Bootstrap event count mismatch: provider reported {} but {forwarded} events \
were delivered",
result.event_count
);
}
log::debug!(
"FFI bootstrap stream complete: {forwarded} events forwarded, provider reported {}",
result.event_count
);
Ok(result)
}
}