Extism runtime and rust-sdk
This repo contains the code for the Extism runtime and rust-sdk. It can be embedded in any Rust application to call Extism plug-ins.
Note: If you're unsure what Extism is or what an SDK is see our homepage: https://extism.org.
Installation
Cargo
To use the extism
crate, you can add it to your Cargo file:
[]
= "1.0.0"
Environment variables
There are a few environment variables that can be used for debugging purposes:
EXTISM_ENABLE_WASI_OUTPUT=1
: show WASI stdout/stderrEXTISM_MEMDUMP=extism.mem
: dump Extism linear memory to a fileEXTISM_COREDUMP=extism.core
: write coredump to a file when a WebAssembly function trapsEXTISM_DEBUG=1
: generate debug informationEXTISM_PROFILE=perf|jitdump|vtune
: enable Wasmtime profilingEXTISM_CACHE_CONFIG=path/to/config.toml
: enable Wasmtime cache, see the docs for details about configuration. Setting this to an empty string will disable caching.
Note: The debug and coredump info will only be written if the plug-in has an error.
Getting Started
This guide should walk you through some of the concepts in Extism and the extism
crate.
Creating A Plug-in
The primary concept in Extism is the plug-in. You can think of a plug-in as a code module stored in a .wasm
file.
Since you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:
use *;
Note: See the Manifest docs as it has a rich schema and a lot of options.
Calling A Plug-in's Exports
This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: count_vowels
. We can call exports using Extism::Plugin::call:
let res = plugin..unwrap;
println!;
# =>
All exports have a simple interface of bytes-in and bytes-out. This plug-in happens to take a string and return a JSON encoded string with a report of results.
The call
function uses extism-convert to determine which input/output types can be used. If we wanted to use a concrete type for
the count_vowels
result, we could defined a struct:
Then we can use Json to get the JSON results decoded into VowelCount
:
let Json = plugin..unwrap;
println!;
# => VowelCount
Plug-in State
Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by the use of variables. Our count vowels plug-in remembers the total number of vowels it's ever counted in the "total" key in the result. You can see this by making subsequent calls to the export:
let res = plugin..unwrap;
println!;
# =>
let res = plugin..unwrap;
println!;
# =>
These variables will persist until this plug-in is freed or you initialize a new one.
Configuration
Plug-ins may optionally take a configuration object. This is a static way to configure the plug-in. Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example:
let manifest = new;
let mut plugin = new;
let res = plugin..unwrap;
println!;
# =>
let mut plugin = new.with_config_key;
let res = plugin..unwrap;
println!;
# =>
Host Functions
Let's extend our count-vowels example a little bit: Instead of storing the total
in an ephemeral plug-in var, let's store it in a persistent key-value store!
Wasm can't use our KV store on it's own. This is where Host Functions come in.
Host functions allow us to grant new capabilities to our plug-ins from our application. They are simply some Rust functions you write which can be passed down and invoked from any language inside the plug-in.
Let's load the manifest like usual but load up this count_vowels_kvstore
plug-in:
let url = url;
let manifest = new;
Note: The source code for this is here and is written in rust, but it could be written in any of our PDK languages.
Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our its import interface for a KV store.
We want to expose two functions to our plugin, kv_write(key: String, value: Bytes)
which writes a bytes value to a key and kv_read(key: String) -> Bytes
which reads the bytes at the given key
.
use *;
// pretend this is redis or something :)
type KVStore = BTreeMap;
// When a first argument separated with a semicolon is provided to `host_fn` it is used as the
// variable name and type for the `UserData` parameter
host_fn!;
host_fn!;
Note: In order to write host functions you should get familiar with the methods on the Extism::CurrentPlugin and Extism::CurrentPlugin types.
Now we can invoke the event:
let res = plugin..unwrap;
println!;
# => Read from key=count-vowels"
# => Writing value=3 from key=count-vowels"
# =>
let res = plugin..unwrap;
println!;
# => Read from key=count-vowels"
# => Writing value=6 from key=count-vowels"
# =>