69 releases (18 breaking)
new 0.20.1 | Dec 3, 2024 |
---|---|
0.19.0 | Oct 27, 2024 |
0.16.1 | Jul 26, 2024 |
0.11.7 | Mar 18, 2024 |
0.2.0 | Jul 21, 2023 |
#43 in WebAssembly
1,120 downloads per month
Used in 7 crates
(3 directly)
130KB
3K
SLoC
warpgate
Warpgate is a library for downloading, resolving, and managing Extism powered WASM plugins.
The warp in warpgate stands for Web Assembly Runtime Plugins. Pretty stellar huh.
Loading plugins
Before a WASM file can be used, it must be loaded. Depending on the locator strategy used, loading a plugin can mean referencing a local file, downloading a file from a secure URL, or even making API requests to specific registries.
To begin, instantiate a PluginLoader
and provide a directory path in which to cache .wasm
files, and a temporary directory in which to download and unpack files.
use warpgate::PluginLoader;
let root = get_cache_root();
let loader = PluginLoader::new(root.join("plugins"), root.join("temp"));
Plugins can then be loaded with the load_plugin
method, which requires a unique ID (becomes the file name), and a PluginLocator
enum variant (in which to locate the .wasm
file). This method returns an absolute path to the cached .wasm
file on the host machine.
use warpgate::PluginLocator;
let wasm_file = loader.load_plugin(PluginLocator::Url {
url: "https://registry.com/path/to/file.wasm".into(),
});
Locator strategies
A locator strategy defines instructions on where to locate the necessary .wasm
file, and is represented as variants on the PluginLocator
enum.
This enum also supports deserializing from strings (as shown below in comments), which can be useful for configuration files.
The following strategies are currently supported:
Local files
File is available on the local host machine. When deserialized, the path
field must be converted to an absolute path manually.
// file://./path/to/file.wasm
PluginLocator::File {
file: "path/to/file.wasm".into(),
path: Some(PathBuf::from("/absolute/path/to/file.wasm")),
}
Secure URLs
Download a file from a secure https
URL.
// https://registry.com/path/to/file.wasm
PluginLocator::Url {
url: "https://registry.com/path/to/file.wasm".into(),
}
GitHub releases
Download an asset from a GitHub release. This approach communicates with the GitHub API, and requires a .wasm
file to be attached as an asset.
Defining a GITHUB_TOKEN
environment variable is recommended to avoid rate limiting.
// github://org/repo
// github://org/[email protected]
// github://org/repo/project
PluginLocator::GitHub(GitHubLocator{
repo_slug: "org/repo".into(),
project_name: None,
tag: Some("v1.2.3".into()), // Latest if `None`
})
The
project_name
field exists to support monorepos. When defined, it will look for a tag/release that starts with the project name. For example, if the project name wasexample_plugin
, it will matchexample_plugin-v1.2.3
orexample_plugin@v1.2.3
tags.
Extism plugin containers
Another mechanism of this library is providing the PluginContainer
struct; a wrapper around Extism's Plugin
and Manifest
types. The container provides convenience methods for calling functions with serde compatible input and output types, and caching the result for subsequent calls. This is extremely useful in avoiding unnecessary overhead when communicating between the WASM guest and host.
To make use of the container, instantiate an instance with a Manifest
, and optional host functions.
use warpgate::{Id, PluginContainer, PluginManifest, Wasm};
// Load the plugin and create a manifest
let wasm_file = loader.load_plugin(locator);
let manifest = PluginManifest::new([Wasm::file(wasm_file)]);
// Create a container
let container = PluginContainer::new(Id::new("id")?, manifest, [host, funcs])?;
// Or
let container = PluginContainer::new_without_functions(Id::new("id")?, manifest)?;
From here, you can call functions on the plugin with the call_func
(no input) and call_func_with
methods. To call and cache functions, use the alternative cache_func
and cache_func_with
methods.
Furthermore, these methods require a serde struct for outputs, and optionally for inputs. Non-serde based functions can be handled with the call
method.
let output: AddOutput = container.cache_func_with("add", AddInput {
left: 10,
right: 20,
})?;
dbg!(output.sum);
Dependencies
~39–61MB
~1M SLoC