use wasmtime::{ Engine, Store };
use wasmtime::component::{ Component, ResourceTable, Linker, Val };
use crate::BindingAny ;
use crate::plugin_instance::PluginInstance ;
use crate::Function ;
pub trait PluginContext: Send {
fn resource_table( &mut self ) -> &mut ResourceTable ;
}
#[must_use = "call .instantiate() or .link() to create a PluginInstance"]
pub struct Plugin<Ctx: 'static> {
component: Component,
context: Ctx,
#[allow( clippy::type_complexity )]
fuel_limiter: Option<Box<dyn FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send>>,
#[allow( clippy::type_complexity )]
epoch_limiter: Option<Box<dyn FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send>>,
#[allow( clippy::type_complexity )]
memory_limiter: Option<Box<dyn (FnMut( &mut Ctx ) -> &mut dyn wasmtime::ResourceLimiter) + Send + Sync>>,
}
impl<Ctx> Plugin<Ctx>
where
Ctx: PluginContext + 'static,
{
pub fn new(
component: Component,
context: Ctx,
) -> Self {
Self {
component,
context,
fuel_limiter: None,
epoch_limiter: None,
memory_limiter: None,
}
}
pub fn with_fuel_limiter( mut self, limiter: impl FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send + 'static ) -> Self {
self.fuel_limiter = Some( Box::new( limiter ));
self
}
pub fn with_epoch_limiter( mut self, limiter: impl FnMut( &mut Store<Ctx>, &str, &str, &Function ) -> u64 + Send + 'static ) -> Self {
self.epoch_limiter = Some( Box::new( limiter ));
self
}
pub fn with_memory_limiter(
mut self,
limiter: impl (FnMut( &mut Ctx ) -> &mut dyn wasmtime::ResourceLimiter) + Send + Sync + 'static,
) -> Self {
self.memory_limiter = Some( Box::new( limiter ));
self
}
pub fn link<PluginId, Sockets>(
self,
engine: &Engine,
mut linker: Linker<Ctx>,
sockets: Sockets,
) -> Result<PluginInstance<Ctx>, wasmtime::Error>
where
PluginId: Eq + std::hash::Hash + Clone + std::fmt::Debug + Send + Sync + Into<Val> + 'static,
Sockets: IntoIterator,
Sockets::Item: Into<BindingAny<PluginId, Ctx>>,
{
sockets.into_iter()
.map( Into::into )
.try_for_each(| binding | binding.add_to_linker( &mut linker ))?;
Self::instantiate( self, engine, &linker )
}
pub fn instantiate(
self,
engine: &Engine,
linker: &Linker<Ctx>
) -> Result<PluginInstance<Ctx>, wasmtime::Error> {
let mut store = Store::new( engine, self.context );
if let Some( limiter ) = self.memory_limiter { store.limiter( limiter ); }
let instance = linker.instantiate( &mut store, &self.component )?;
Ok( PluginInstance {
store,
instance,
fuel_limiter: self.fuel_limiter,
epoch_limiter: self.epoch_limiter,
})
}
}
impl<Ctx: std::fmt::Debug + 'static> std::fmt::Debug for Plugin<Ctx> {
fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result {
f.debug_struct( "Plugin" )
.field( "component", &"<Component>" )
.field( "context", &self.context )
.field( "fuel_limiter", &self.fuel_limiter.as_ref().map(| _ | "<closure>" ))
.field( "epoch_limiter", &self.epoch_limiter.as_ref().map(| _ | "<closure>" ))
.field( "memory_limiter", &self.memory_limiter.as_ref().map(| _ | "<closure>" ))
.finish_non_exhaustive()
}
}