use std::{
any::{type_name, Any, TypeId},
collections::HashMap,
marker::PhantomData,
sync::Arc,
};
use anyhow::{bail, Result};
use super::{Data, Linker};
pub trait HostComponent: Send + Sync + 'static {
type Data: Send + Sized + 'static;
fn add_to_linker<T: Send>(
linker: &mut Linker<T>,
get: impl Fn(&mut Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> Result<()>;
fn build_data(&self) -> Self::Data;
}
impl<HC: HostComponent> HostComponent for Arc<HC> {
type Data = HC::Data;
fn add_to_linker<T: Send>(
linker: &mut Linker<T>,
get: impl Fn(&mut Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> Result<()> {
HC::add_to_linker(linker, get)
}
fn build_data(&self) -> Self::Data {
(**self).build_data()
}
}
#[derive(Clone, Copy)]
pub struct AnyHostComponentDataHandle(usize);
impl<T: HostComponent> From<HostComponentDataHandle<T>> for AnyHostComponentDataHandle {
fn from(value: HostComponentDataHandle<T>) -> Self {
value.inner
}
}
pub struct HostComponentDataHandle<HC: HostComponent> {
inner: AnyHostComponentDataHandle,
_phantom: PhantomData<fn() -> HC::Data>,
}
impl<HC: HostComponent> HostComponentDataHandle<HC> {
fn from_any(handle: AnyHostComponentDataHandle) -> Self {
Self {
inner: handle,
_phantom: PhantomData,
}
}
}
impl<HC: HostComponent> Clone for HostComponentDataHandle<HC> {
fn clone(&self) -> Self {
*self
}
}
impl<HC: HostComponent> Copy for HostComponentDataHandle<HC> {}
impl<HC: HostComponent> From<HostComponentDataHandle<Arc<HC>>> for HostComponentDataHandle<HC> {
fn from(value: HostComponentDataHandle<Arc<HC>>) -> Self {
Self::from_any(value.inner)
}
}
#[doc(hidden)]
pub trait DynSafeHostComponent {
fn build_data_box(&self) -> AnyData;
}
impl<T: HostComponent> DynSafeHostComponent for T
where
T::Data: Any + Send,
{
fn build_data_box(&self) -> AnyData {
Box::new(self.build_data())
}
}
type BoxHostComponent = Box<dyn DynSafeHostComponent + Send + Sync>;
#[derive(Default)]
pub struct HostComponentsBuilder {
handles: HashMap<TypeId, AnyHostComponentDataHandle>,
host_components: Vec<BoxHostComponent>,
}
impl HostComponentsBuilder {
pub fn add_host_component<T: Send, HC: HostComponent>(
&mut self,
linker: &mut Linker<T>,
host_component: HC,
) -> Result<HostComponentDataHandle<HC>> {
let type_id = TypeId::of::<HC>();
if self.handles.contains_key(&type_id) {
bail!(
"already have a host component of type {}",
type_name::<HC>()
)
}
let handle = AnyHostComponentDataHandle(self.host_components.len());
self.handles.insert(type_id, handle);
self.host_components.push(Box::new(host_component));
HC::add_to_linker(linker, move |data| {
data.host_components_data
.get_or_insert_any(handle)
.downcast_mut()
.unwrap()
})?;
Ok(HostComponentDataHandle::<HC> {
inner: handle,
_phantom: PhantomData,
})
}
pub fn build(self) -> HostComponents {
HostComponents {
handles: self.handles,
host_components: Arc::new(self.host_components),
}
}
}
pub struct HostComponents {
handles: HashMap<TypeId, AnyHostComponentDataHandle>,
host_components: Arc<Vec<BoxHostComponent>>,
}
impl HostComponents {
pub fn builder() -> HostComponentsBuilder {
Default::default()
}
pub fn new_data(&self) -> HostComponentsData {
let data = std::iter::repeat_with(Default::default)
.take(self.host_components.len())
.collect();
HostComponentsData {
data,
host_components: self.host_components.clone(),
}
}
pub fn find_handle<HC: HostComponent>(&self) -> Option<HostComponentDataHandle<HC>> {
self.handles
.get(&TypeId::of::<HC>())
.map(|handle| HostComponentDataHandle::from_any(*handle))
}
}
type AnyData = Box<dyn Any + Send>;
pub struct HostComponentsData {
data: Vec<Option<AnyData>>,
host_components: Arc<Vec<BoxHostComponent>>,
}
impl HostComponentsData {
pub fn set<HC: HostComponent>(&mut self, handle: HostComponentDataHandle<HC>, data: HC::Data) {
self.data[handle.inner.0] = Some(Box::new(data));
}
pub fn get_or_insert<HC: HostComponent>(
&mut self,
handle: HostComponentDataHandle<HC>,
) -> &mut HC::Data {
let data = self.get_or_insert_any(handle.inner);
data.downcast_mut().unwrap()
}
pub fn get_or_insert_any(&mut self, handle: AnyHostComponentDataHandle) -> &mut AnyData {
let idx = handle.0;
self.data[idx].get_or_insert_with(|| self.host_components[idx].build_data_box())
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestHC;
impl HostComponent for TestHC {
type Data = u8;
fn add_to_linker<T: Send>(
_linker: &mut Linker<T>,
_get: impl Fn(&mut Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> Result<()> {
Ok(())
}
fn build_data(&self) -> Self::Data {
0
}
}
#[test]
fn host_components_data() {
let engine = wasmtime::Engine::default();
let mut linker: crate::Linker<()> = crate::Linker::new(&engine);
let mut builder = HostComponents::builder();
let handle1 = builder
.add_host_component(&mut linker, Arc::new(TestHC))
.unwrap();
let handle2 = builder.add_host_component(&mut linker, TestHC).unwrap();
let host_components = builder.build();
let mut hc_data = host_components.new_data();
assert_eq!(hc_data.get_or_insert(handle1), &0);
hc_data.set(handle2, 1);
assert_eq!(hc_data.get_or_insert(handle2), &1);
}
#[test]
fn find_handle() {
let engine = wasmtime::Engine::default();
let mut linker: crate::Linker<()> = crate::Linker::new(&engine);
let mut builder = HostComponents::builder();
builder.add_host_component(&mut linker, TestHC).unwrap();
let host_components = builder.build();
let handle = host_components.find_handle::<TestHC>().unwrap();
let mut hc_data = host_components.new_data();
assert_eq!(hc_data.get_or_insert(handle), &0);
}
}