use std::sync::Arc ;
use std::collections::{ HashMap, HashSet };
use futures::lock::Mutex ;
use wasmtime::component::{ Linker, ResourceType, Val };
use crate::{ Binding, PluginContext, PluginInstanceAsync, PluginInstanceSync };
use crate::cardinality::Cardinality ;
use crate::linker::{
dispatch_all,
dispatch_all_async,
dispatch_all_async_blocking,
dispatch_method,
dispatch_method_async,
dispatch_method_async_blocking,
};
use crate::resource_wrapper::ResourceWrapper ;
#[derive( Debug, Clone, Default )]
pub struct Interface {
functions: HashMap<String, Function>,
resources: HashSet<String>,
}
impl Interface {
pub fn new(
functions: HashMap<String, Function>,
resources: HashSet<String>,
) -> Self {
Self { functions, resources }
}
#[inline]
pub(crate) fn function( &self, name: &str ) -> Option<&Function> {
self.functions.get( name )
}
#[inline]
pub(crate) fn add_to_linker<PluginId, Ctx, Plugins>(
&self,
linker: &mut Linker<Ctx>,
package_name: &str,
interface_ident: &str,
interface_name: &str,
binding: &Binding<PluginId, Ctx, Plugins, PluginInstanceSync<Ctx>>,
) -> Result<(), wasmtime::Error>
where
PluginId: std::hash::Hash + Eq + Clone + Send + Sync + Into<Val> + 'static,
Ctx: PluginContext,
Plugins: Cardinality<PluginId, PluginInstanceSync<Ctx>> + 'static,
<Plugins as Cardinality<PluginId, PluginInstanceSync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceSync<Ctx>>>>: Send + Sync,
<Plugins as Cardinality<PluginId, PluginInstanceSync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceSync<Ctx>>>>: Cardinality<PluginId, Arc<Mutex<PluginInstanceSync<Ctx>>>>,
<<Plugins as Cardinality<PluginId, PluginInstanceSync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceSync<Ctx>>>> as Cardinality<PluginId, Arc<Mutex<PluginInstanceSync<Ctx>>>>>::Rebind<Val>: Into<Val>,
{
let mut linker_root = linker.root();
let mut linker_instance = linker_root.instance( interface_ident )?;
self.functions.iter().try_for_each(|( name, metadata )| {
let package_name_clone = package_name.to_string();
let interface_name_clone = interface_name.to_string();
let binding_clone = binding.clone();
let name_clone = name.clone();
let metadata_clone = metadata.clone();
macro_rules! link {( $dispatch: expr ) => {
linker_instance.func_new( name, move | ctx, _ty, args, results | Ok(
results[0] = $dispatch( &binding_clone, ctx, &package_name_clone, &interface_name_clone, &name_clone, &metadata_clone, args )
))
}}
match metadata.kind() {
FunctionKind::Freestanding => link!( dispatch_all ),
FunctionKind::Method => link!( dispatch_method ),
}
})?;
self.resources.iter().try_for_each(| resource | linker_instance
.resource( resource.as_str(), ResourceType::host::<Arc<ResourceWrapper<PluginId>>>(), ResourceWrapper::<PluginId>::drop )
)?;
Ok(())
}
#[inline]
pub(crate) fn add_to_linker_async<PluginId, Ctx, Plugins>(
&self,
linker: &mut Linker<Ctx>,
package_name: &str,
interface_ident: &str,
interface_name: &str,
binding: &Binding<PluginId, Ctx, Plugins, PluginInstanceAsync<Ctx>>,
) -> Result<(), wasmtime::Error>
where
PluginId: std::hash::Hash + Eq + Clone + Send + Sync + Into<Val> + 'static,
Ctx: PluginContext,
Plugins: Cardinality<PluginId, PluginInstanceAsync<Ctx>> + 'static,
<Plugins as Cardinality<PluginId, PluginInstanceAsync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceAsync<Ctx>>>>: Send + Sync,
<Plugins as Cardinality<PluginId, PluginInstanceAsync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceAsync<Ctx>>>>: Cardinality<PluginId, Arc<Mutex<PluginInstanceAsync<Ctx>>>>,
<<Plugins as Cardinality<PluginId, PluginInstanceAsync<Ctx>>>::Rebind<Arc<Mutex<PluginInstanceAsync<Ctx>>>> as Cardinality<PluginId, Arc<Mutex<PluginInstanceAsync<Ctx>>>>>::Rebind<Val>: Into<Val> + Send,
{
let mut linker_root = linker.root();
let mut linker_instance = linker_root.instance( interface_ident )?;
self.functions.iter().try_for_each(|( name, metadata )| {
let package_name = package_name.to_string();
let interface_name = interface_name.to_string();
let binding = binding.clone();
let function_name = name.clone();
let function = metadata.clone();
macro_rules! link_concurrent {( $dispatch: expr ) => {
linker_instance.func_new_concurrent( name, move | ctx, _ty, args, results | {
let package_name = package_name.clone();
let interface_name = interface_name.clone();
let binding = binding.clone();
let function_name = function_name.clone();
let function = function.clone();
Box::pin( async move {
results[0] = $dispatch(
&binding, ctx, &package_name, &interface_name, &function_name, &function, args,
).await;
Ok(())
})
})
}}
macro_rules! link_blocking {( $dispatch: expr ) => {
linker_instance.func_new_async( name, move | ctx, _ty, args, results | {
let package_name = package_name.clone();
let interface_name = interface_name.clone();
let binding = binding.clone();
let function_name = function_name.clone();
let function = function.clone();
Box::new( async move {
results[0] = $dispatch(
&binding, ctx, &package_name, &interface_name, &function_name, &function, args,
).await;
Ok(())
})
})
}}
match ( metadata.is_async(), metadata.kind() ) {
( true, FunctionKind::Freestanding ) => link_concurrent!( dispatch_all_async ),
( true, FunctionKind::Method ) => link_concurrent!( dispatch_method_async ),
( false, FunctionKind::Freestanding ) => link_blocking!( dispatch_all_async_blocking ),
( false, FunctionKind::Method ) => link_blocking!( dispatch_method_async_blocking ),
}
})?;
self.resources.iter().try_for_each(| resource | linker_instance.resource( resource.as_str(), ResourceType::host::<Arc<ResourceWrapper<PluginId>>>(), ResourceWrapper::<PluginId>::drop ))?;
Ok(())
}
}
#[derive( Debug, Clone, Copy, Eq, PartialEq )]
pub enum FunctionKind {
Freestanding,
Method,
}
#[derive( Debug, Clone )]
pub struct Function {
kind: FunctionKind,
return_kind: ReturnKind,
is_async: bool,
}
impl Function {
pub fn new(
kind: FunctionKind,
return_kind: ReturnKind,
) -> Self {
Self { kind, return_kind, is_async: false }
}
pub fn new_async(
kind: FunctionKind,
return_kind: ReturnKind,
) -> Self {
Self { kind, return_kind, is_async: true }
}
pub fn return_kind( &self ) -> ReturnKind { self.return_kind }
pub fn kind( &self ) -> FunctionKind { self.kind }
pub fn is_async( &self ) -> bool { self.is_async }
}
#[derive( Copy, Clone, Eq, PartialEq, Hash, Debug, Default )]
pub enum ReturnKind {
#[default] Void,
MayContainResources,
AssumeNoResources,
}
impl std::fmt::Display for ReturnKind {
fn fmt( &self, f: &mut std::fmt::Formatter ) -> Result<(), std::fmt::Error> {
match self {
Self::Void => write!( f, "Function returns no data" ),
Self::MayContainResources => write!( f, "Return type may contain resources" ),
Self::AssumeNoResources => write!( f, "Function is assumed to not return any resources" ),
}
}
}