Skip to main content

Crate wasm_link

Crate wasm_link 

Source
Expand description

A WebAssembly plugin runtime for building modular applications.

Plugins are small, single-purpose WASM components that connect through abstract interfaces. Each plugin declares a plug (the interface it implements) and zero or more sockets (interfaces it depends on). wasm_link links these into a dependency tree and handles cross-plugin dispatch.

§Core Concepts

  • Interface: A contract declaring what an implementer exports and what a consumer may import. Defined via InterfaceData.

  • Plug: A plugin’s declaration that it implements an interface (exports its functions).

  • Socket: A plugin’s declaration that it depends on an interface (expects to call into implementations provided by other plugins).

  • Cardinality: Each interface specifies how many plugins may implement it via InterfaceCardinality. This affects how dispatch results are returned.

  • Root Socket: The entry point interface that the host application calls into. Other interfaces are internal - only accessible to plugins, not the host.

§Example

use wasm_link::{
    InterfaceData, InterfaceCardinality, FunctionData, ReturnKind,
    PluginCtxView, PluginData, PluginTree, Socket,
    Engine, Component, Linker, ResourceTable, Val,
};

// Declare your fixture sources
#[derive( Clone )]
struct Func { name: String, return_kind: ReturnKind }
impl FunctionData for Func {
    fn name( &self ) -> &str { self.name.as_str() }
    fn return_kind( &self ) -> ReturnKind { self.return_kind.clone() }
    // Determine whether a function is a resource method
    // a constructor is not considered to be a method
    fn is_method( &self ) -> bool { false }
}

struct Interface { id: &'static str, funcs: Vec<Func> }
impl InterfaceData for Interface {
    type Id = &'static str ;
    type Error = std::convert::Infallible ;
    type Function = Func ;
    type FunctionIter<'a> = std::slice::Iter<'a, Func> ;
    type ResourceIter<'a> = std::iter::Empty<&'a String> ;
    fn id( &self ) -> Result<&Self::Id, Self::Error> { Ok( &self.id ) }
    fn cardinality( &self ) -> Result<&InterfaceCardinality, Self::Error> { Ok( &InterfaceCardinality::ExactlyOne ) }
    fn package_name( &self ) -> Result<&str, Self::Error> { Ok( "my:package/example" ) }
    fn functions( &self ) -> Result<Self::FunctionIter<'_>, Self::Error> { Ok( self.funcs.iter()) }
    fn resources( &self ) -> Result<Self::ResourceIter<'_>, Self::Error> { Ok( std::iter::empty()) }
}

struct Plugin { id: &'static str, plug: &'static str, resource_table: ResourceTable }
impl PluginCtxView for Plugin {
    fn resource_table( &mut self ) -> &mut ResourceTable { &mut self.resource_table }
}
impl PluginData for Plugin {
    type Id = &'static str ;
    type InterfaceId = &'static str ;
    type Error = std::convert::Infallible ;
    type SocketIter<'a> = std::iter::Empty<&'a Self::InterfaceId> ;
    fn id( &self ) -> Result<&Self::Id, Self::Error> { Ok( &self.id ) }
    fn plug( &self ) -> Result<&Self::InterfaceId, Self::Error> { Ok( &self.plug ) }
    fn sockets( &self ) -> Result<Self::SocketIter<'_>, Self::Error> { Ok( std::iter::empty()) }
    fn component( &self, engine: &Engine ) -> Result<Component, Self::Error> {
        /* initialise your component here */
    }
}

// Now construct some plugins and related data
let root_interface_id = "root" ;
let plugins = [ Plugin { id: "foo", plug: root_interface_id, resource_table: ResourceTable::new() }];
let interfaces = [ Interface { id: root_interface_id, funcs: vec![
    Func { name: "get-value".to_string(), return_kind: ReturnKind::MayContainResources }
]}];

// First you need to tell wasm_link about your plugins, interfaces and where you want
// the execution to begin. wasm_link will try it's best to load in all the plugins,
// upon encountering an error, it will try to salvage as much of the remaining data
// as possible returning a list of failures alongside the `PluginTree`.
let ( tree, build_errors ) = PluginTree::new( root_interface_id, interfaces, plugins );
assert!( build_errors.is_empty() );

// Once you've got your `PluginTree` constructed, you can link the plugins together
// Since some plugins may fail to load, it is only at this point that the cardinality
// requirements are satisfied by the plugins that managed to get loaded, otherwise it
// tries to salvage as much of the tree as can be loaded returning a list of failures
// alongside the loaded `PluginTreeHead` - the root node of the `PluginTree`.
let engine = Engine::default();
let linker = Linker::new( &engine );
let ( tree_head, load_errors ) = tree.load( &engine, &linker ).unwrap();
assert!( load_errors.is_empty() );

// Now you can dispatch any function on the root interface.
// This will dispatch the function for all plugins plugged in to the root socket returning
// a Result for each in the shape determined by the interface cardinality.
let result = tree_head.dispatch( "my:package/example", "get-value", true, &[] );
match result {
    Socket::ExactlyOne( Ok( Val::U32( n ))) => assert_eq!( n, 42 ),
    Socket::ExactlyOne( Err( e )) => panic!( "dispatch error: {e}" ),
    _ => panic!( "unexpected cardinality" ),
}

Structs§

Component
A compiled WebAssembly Component.
Engine
An Engine which is a global context for compilation and management of wasm modules.
Linker
A type used to instantiate Components.
PluginTree
An unloaded plugin dependency tree.
PluginTreeHead
The root node of a loaded plugin tree.
ResourceTable
The ResourceTable type maps a Resource<T> to its T.

Enums§

DispatchError
Errors that can occur when dispatching a function call to plugins.
InterfaceCardinality
Specifies how many plugins may or must implement an interface.
LoadError
Errors that can occur while loading and linking plugins.
PluginTreeError
Error that can occur during plugin tree construction.
ReturnKind
Categorizes a function’s return for dispatch handling.
Socket
Container for plugin instances or dispatch results.
Val
Represents possible runtime values which a component function can either consume or produce

Traits§

FunctionData
Metadata about a function declared by an interface.
InterfaceData
Trait for accessing interface metadata from a user-defined source.
PluginCtxView
Trait for accessing a ResourceTable from the store’s data type.
PluginData
Trait for accessing plugin metadata and WASM binaries from a user-defined source.

Type Aliases§

PartialResult
Represents an operation that may partially succeed or fail. Ok: Core success data plus errors from partial failures that allowed completion. Err: Primary failure cause plus errors that likely contributed to the overall failure.
PartialSuccess
Represents a successful operation where some parts failed but didn’t prevent overall success. The Vec<E> contains errors from the failed parts that were handled gracefully.