use core::ffi::c_void;
use crate::abi::{Header, Layout, LayoutField, Str};
use crate::load::{hand_over, ok};
use crate::points::DescribedField;
use crate::{Host, Load, Off};
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct DescribedEvent {
pub name: String,
pub fields: Vec<DescribedField>,
pub size: u32,
}
impl DescribedEvent {
#[must_use]
pub fn new(name: &str, fields: Vec<DescribedField>, size: u32) -> Self {
Self {
name: name.to_owned(),
fields,
size,
}
}
fn with_spec<R>(&self, with: impl FnOnce(&crate::abi::EventSpec) -> R) -> R {
let fields: Vec<LayoutField> = self
.fields
.iter()
.map(|f| LayoutField {
name: Str::new(&f.name),
hash: f.hash,
})
.collect();
let spec = crate::abi::EventSpec {
header: Header::of::<crate::abi::EventSpec>(),
name: Str::new(&self.name),
layout: Layout {
fields: fields.as_ptr(),
count: u32::try_from(fields.len()).unwrap_or(u32::MAX),
size: self.size,
},
};
with(&spec)
}
}
#[derive(Clone, Copy, Debug)]
pub struct DescribedEmitter {
host: Host,
id: u32,
size: usize,
}
impl DescribedEmitter {
pub fn send(&self, data: &[u8]) {
if data.len() != self.size {
return;
}
let _status =
(self.host.api().send_event)(self.host.handle(), self.id, data.as_ptr().cast());
}
}
struct Handler<F> {
size: usize,
run: F,
}
impl Load<'_> {
pub fn on_described_mod_event<F>(
&mut self,
event: &DescribedEvent,
handler: F,
) -> Result<(), Off>
where
F: Fn(&[u8]) + Send + Sync + 'static,
{
let host = self.host();
let handler = Handler {
size: usize::try_from(event.size).unwrap_or(0),
run: handler,
};
event.with_spec(|spec| {
hand_over(
handler,
|user| {
(host.api().subscribe_event)(host.handle(), spec, deliver_described::<F>, user)
},
&format!("event {}", event.name),
)
})
}
pub unsafe fn define_described_event(
&mut self,
event: &DescribedEvent,
) -> Result<DescribedEmitter, Off> {
let host = self.host();
let mut id = 0;
let status =
event.with_spec(|spec| (host.api().define_event)(host.handle(), spec, &raw mut id));
ok(
status,
DescribedEmitter {
host,
id,
size: usize::try_from(event.size).unwrap_or(0),
},
|| {
format!(
"event {}: named after another mod or defined twice",
event.name
)
},
)
}
}
unsafe extern "system" fn deliver_described<F: Fn(&[u8])>(data: *const c_void, user: *mut c_void) {
let handler = unsafe { &*user.cast::<Handler<F>>() };
if data.is_null() {
return;
}
let data = unsafe { core::slice::from_raw_parts(data.cast::<u8>(), handler.size) };
crate::host::guard(
"panic in a mod event handler",
|| (),
|| (handler.run)(data),
);
}