wasm-link 0.4.0

A WebAssembly plugin runtime based around Wasmtime intended for building fully modular applications
Documentation
use std::collections::HashMap ;

use wasm_link::{ Binding, Engine, Linker, Remap, Val };
use wasm_link::cardinality::ExactlyOne ;

fixtures! {
	bindings = { root: "root", dependency: "dependency" };
	plugins  = { startup: "startup", child: "child" };
}

#[test]
fn dependant_dispatch_encodes_child_errors() -> Result<(), Box<dyn std::error::Error>> {
	let engine = Engine::default();
	let linker = Linker::new( &engine );
	let plugins = fixtures::plugins( &engine );
	let bindings = fixtures::bindings();
	let child = plugins.child.plugin
		.remap_interfaces( HashMap::from([(
			"root".to_string(),
			Remap::found_as_with_item_resolution_table(
				"root",
				HashMap::from([( "get-value".to_string(), "missing".to_string() )]),
			),
		)]))
		.instantiate( &engine, &linker )?;
	let dependency = Binding::new(
		bindings.dependency.package,
		HashMap::from([( bindings.dependency.name, bindings.dependency.spec )]),
		ExactlyOne( "child".to_string(), child ),
	);
	let startup = plugins.startup.plugin.link( &engine, linker, vec![ dependency ])?;
	let root = Binding::new(
		bindings.root.package,
		HashMap::from([( bindings.root.name, bindings.root.spec )]),
		ExactlyOne( "startup".to_string(), startup ),
	);

	let result = root.dispatch( "root", "get-value", &[] )?;
	assert!( matches!(
		&result,
		ExactlyOne( _, Ok( Val::Tuple( items ))) if matches!( items.as_slice(),
			[ Val::String( id ), Val::Result( Err( Some( error ))) ] if
			id == "child"
			&& matches!( &**error, Val::Variant( name, Some( message )) if
				name == "invalid-function"
				&& matches!( &**message, Val::String( function ) if function.ends_with( ":missing" ))
			)
		)
	), "unexpected dispatch result: {result:#?}" );
	Ok(())
}

#[test]
fn async_dependant_dispatch_encodes_child_errors() -> Result<(), Box<dyn std::error::Error>> {
	futures::executor::block_on( async {
		let engine = Engine::default();
		let linker = Linker::new( &engine );
		let executor = futures::executor::ThreadPool::new()?;
		let plugins = fixtures::plugins( &engine );
		let bindings = fixtures::bindings();
		let child = plugins.child.plugin
			.remap_interfaces( HashMap::from([(
				"root".to_string(),
				Remap::found_as_with_item_resolution_table(
					"root",
					HashMap::from([( "get-value".to_string(), "missing".to_string() )]),
				),
			)]))
			.instantiate_async( &engine, &linker, executor.clone() ).await?;
		let dependency = Binding::new(
			bindings.dependency.package,
			HashMap::from([( bindings.dependency.name, bindings.dependency.spec )]),
			ExactlyOne( "child".to_string(), child ),
		);
		let startup = plugins.startup.plugin.link_async(
			&engine,
			linker,
			vec![ dependency ],
			executor,
		).await?;
		let root = Binding::new(
			bindings.root.package,
			HashMap::from([( bindings.root.name, bindings.root.spec )]),
			ExactlyOne( "startup".to_string(), startup ),
		);

		let result = root.dispatch_async( "root", "get-value", &[] ).await?;
		assert!( matches!(
			&result,
			ExactlyOne( _, Ok( Val::Tuple( items ))) if matches!( items.as_slice(),
				[ Val::String( id ), Val::Result( Err( Some( error ))) ] if
				id == "child"
				&& matches!( &**error, Val::Variant( name, Some( message )) if
					name == "invalid-function"
					&& matches!( &**message, Val::String( function ) if function.ends_with( ":missing" ))
				)
			)
		), "unexpected dispatch result: {result:#?}" );
		Ok(())
	})
}