wasm-link 0.4.1

A WebAssembly plugin runtime based around Wasmtime intended for building fully modular applications
Documentation
use wasmtime::{ AsContextMut, Config, Engine, Store };
use wasmtime::component::{ Component, FutureReader, Linker, ResourceTable, StreamReader, Val };

use super::wrap_resources ;
use crate::PluginContext ;



struct Context { table: ResourceTable }

impl PluginContext for Context {
	fn resource_table( &mut self ) -> &mut ResourceTable { &mut self.table }
}

#[test]
fn wraps_nested_component_values_without_resources() -> Result<(), crate::DispatchError> {
	let mut store = Store::new( &Engine::default(), Context { table: ResourceTable::new() });
	let values = [
		Val::Bool( true ),
		Val::List( vec![ Val::U32( 1 ) ]),
		Val::Map( vec![( Val::String( "key".to_string() ), Val::U32( 2 ))]),
		Val::Record( vec![( "field".to_string(), Val::U32( 3 ))]),
		Val::Tuple( vec![ Val::U32( 4 ) ]),
		Val::Variant( "value".to_string(), Some( Box::new( Val::U32( 5 )))),
		Val::Option( Some( Box::new( Val::U32( 6 )))),
		Val::Result( Ok( Some( Box::new( Val::U32( 7 ))))),
		Val::Result( Err( Some( Box::new( Val::U32( 8 ))))),
	];

	values.into_iter().try_for_each(| value |
		wrap_resources( value, "plugin".to_string(), &mut store.as_context_mut() ).map( drop )
	)?;
	Ok(())
}

#[test]
fn rejects_async_values_during_cross_plugin_transfer() -> Result<(), Box<dyn std::error::Error>> {
	let mut config = Config::new();
	config.concurrency_support( true );
	let engine = Engine::new( &config )?;
	let mut store = Store::new( &engine, Context { table: ResourceTable::new() });
	let future = FutureReader::new( &mut store, async { Ok::<_, wasmtime::Error>( 1_u32 )})?
		.try_into_future_any( &mut store )?;
	let stream = StreamReader::new( &mut store, vec![ 1_u32 ])?
		.try_into_stream_any( &mut store )?;

	assert!( matches!(
		wrap_resources( Val::Future( future ), "plugin".to_string(), &mut store.as_context_mut() ),
		Err( crate::DispatchError::UnsupportedType( name )) if name == "future"
	));
	assert!( matches!(
		wrap_resources( Val::Stream( stream ), "plugin".to_string(), &mut store.as_context_mut() ),
		Err( crate::DispatchError::UnsupportedType( name )) if name == "stream"
	));
	Ok(())
}

#[test]
fn rejects_error_contexts_during_cross_plugin_transfer() -> Result<(), Box<dyn std::error::Error>> {
	futures::executor::block_on( async {
		let mut config = Config::new();
		config.wasm_component_model_async( true );
		config.wasm_component_model_error_context( true );
		let engine = Engine::new( &config )?;
		let component = Component::from_file(
			&engine,
			concat!( env!( "CARGO_MANIFEST_DIR" ), "/tests/linker/error_context.wat" ),
		)?;
		let linker = Linker::<Context>::new( &engine );
		let mut store = Store::new( &engine, Context { table: ResourceTable::new() });
		let instance = linker.instantiate_async( &mut store, &component ).await?;
		let function = instance.get_func( &mut store, "make-error-context" )
			.ok_or( "missing make-error-context export" )?;
		let mut results = [ Val::Bool( false ) ];
		function.call_async( &mut store, &[], &mut results ).await?;
		assert!( matches!(
			wrap_resources( results[0].clone(), "plugin".to_string(), &mut store.as_context_mut() ),
			Err( crate::DispatchError::UnsupportedType( name )) if name == "error-context"
		));
		Ok::<_, Box<dyn std::error::Error>>(())
	})
}