Updates the requirements on [cbindgen](https://github.com/mozilla/cbindgen) to permit the latest version. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/mozilla/cbindgen/blob/master/CHANGES">cbindgen's changelog</a>.</em></p> <blockquote> <h1>0.27.0</h1> <pre><code> * Revert: The `Config` struct now has a private member. * Allow users to specify a crate version for bindings generation ([#901](https://github.com/mozilla/cbindgen/issues/901)). * Update MSRV to 1.74 ([#912](https://github.com/mozilla/cbindgen/issues/912), [#987](https://github.com/mozilla/cbindgen/issues/987)). * Support #[deprecated] on enum variants ([#933](https://github.com/mozilla/cbindgen/issues/933)). * Support integrating the package_version information in a header file comment ([#939](https://github.com/mozilla/cbindgen/issues/939)). * Add a language backend ([#942](https://github.com/mozilla/cbindgen/issues/942)). * Support generics with defaulted args ([#959](https://github.com/mozilla/cbindgen/issues/959)). * Add `VaList` compatibility ([#970](https://github.com/mozilla/cbindgen/issues/970)). </code></pre> <h1>0.26.0</h1> <pre><code> * Fix swapping of `>>=` and `<<=` in constants. * Add support for #[deprecated] ([#860](https://github.com/mozilla/cbindgen/issues/860)). * Built-in support for bitflags 2.0. * Support for "C-unwind" ABI. * Generate bindings for non-public extern items if they are #[no_mangle]. </code></pre> <h2>0.25.0</h2> <pre><code> * Re-release of yanked 0.24.6 as a major release * Update MSRV to 1.57 * Support variadic arguments (`...`) ([#805](https://github.com/mozilla/cbindgen/issues/805)) * Add --depfile option ([#820](https://github.com/mozilla/cbindgen/issues/820)) * Breaking changes: The `Config` struct now has a private member. </code></pre> <h2>0.24.6 (YANKED: depfile option was breaking, see <a href="https://redirect.github.com/mozilla/cbindgen/issues/841">#841</a>)</h2> <pre><code> * Update MSRV to 1.57 * Support variadic arguments (`...`) ([#805](https://github.com/mozilla/cbindgen/issues/805)) * Add --depfile option ([#820](https://github.com/mozilla/cbindgen/issues/820)) </code></pre> <h2>0.24.5</h2> <pre><code> * Don't enforce tempfile version. </code></pre> <h2>0.24.4</h2> <pre><code> * Move expand infinite recursion fix ([#799](https://github.com/mozilla/cbindgen/issues/799)) * Add with_cpp_compat to the builder ([#796](https://github.com/mozilla/cbindgen/issues/796)) * Handle never type in return position consistently ([#780](https://github.com/mozilla/cbindgen/issues/780)) * Fix warnings ([#816](https://github.com/mozilla/cbindgen/issues/816), [#819](https://github.com/mozilla/cbindgen/issues/819)) * Updated documentation ([#788](https://github.com/mozilla/cbindgen/issues/788), [#791](https://github.com/mozilla/cbindgen/issues/791), [#792](https://github.com/mozilla/cbindgen/issues/792), [#810](https://github.com/mozilla/cbindgen/issues/810), [#823](https://github.com/mozilla/cbindgen/issues/823)) </code></pre> <h2>0.24.3</h2> <pre><code> * Make struct expressions correctly generated through typedefs ([#768](https://github.com/mozilla/cbindgen/issues/768)). </code></pre> <h2>0.24.2</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="58c6156b0d"><code>58c6156</code></a> Bump MSRV again for clap.</li> <li><a href="103a8de0ac"><code>103a8de</code></a> Cargo update and version bump.</li> <li><a href="67cb560430"><code>67cb560</code></a> Update CHANGES since v0.26.0</li> <li><a href="316298182e"><code>3162981</code></a> Output condition for globals.</li> <li><a href="e469e44c00"><code>e469e44</code></a> Add missing parens to log message</li> <li><a href="3cbb637bbf"><code>3cbb637</code></a> Update cython expectations from the previous patch.</li> <li><a href="785e066e03"><code>785e066</code></a> Fix variadic arguments when used in function pointer</li> <li><a href="aa8ea654e1"><code>aa8ea65</code></a> deps: Update syn to 2.0</li> <li><a href="9f632843b8"><code>9f63284</code></a> Generalize Item to expose documentation and generic params</li> <li><a href="1dda6162e5"><code>1dda616</code></a> Used documented method of specifying variadic arguments</li> <li>Additional commits viewable in <a href="https://github.com/mozilla/cbindgen/compare/0.26.0...v0.27.0">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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:
[dependencies]
extism = "1.4.1"
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 extism::*;
fn main() {
let url = Wasm::url(
"https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"
);
let manifest = Manifest::new([url]);
let mut plugin = Plugin::new(&manifest, [], true).unwrap();
let res = plugin.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
}
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.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
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:
#[derive(Debug, serde::Deserialize)]
struct VowelCount {
count: usize,
total: usize,
vowels: String,
}
Then we can use Json to get the JSON results decoded into VowelCount:
let Json(res) = plugin.call::<&str, Json<VowelCount>>("count_vowels", "Hello, world!").unwrap();
println!("{:?}", res);
# => VowelCount {count: 3, total: 3, vowels: "aeiouAEIOU"}
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.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
# => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}
let res = plugin.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
# => {"count": 3, "total": 9, "vowels": "aeiouAEIOU"}
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 = Manifest::new([url]);
let mut plugin = Plugin::new(&manifest, [], true);
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
println!("{}", res);
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
let mut plugin = Plugin::new(&manifest, [], true).with_config_key("vowels", "aeiouyAEIOUY");
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
println!("{}", res);
# => {"count": 4, "total": 4, "vowels": "aeiouyAEIOUY"}
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 = Wasm::url(
"https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm"
);
let manifest = Manifest::new([url]);
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 extism::*;
// pretend this is redis or something :)
type KVStore = std::collections::BTreeMap<String, Vec<u8>>;
// 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!(kv_read(user_data: KVStore; key: String) -> u32 {
let kv = user_data.get()?;
let kv = kv.lock().unwrap();
let value = kv
.get(&key)
.map(|x| u32::from_le_bytes(x.clone().try_into().unwrap()))
.unwrap_or_else(|| 0u32);
Ok(value)
});
host_fn!(kv_write(user_data: KVStore; key: String, value: u32) {
let kv = user_data.get()?;
let mut kv = kv.lock().unwrap();
kv.insert(key, value.to_le_bytes().to_vec());
Ok(())
});
fn main() {
let kv_store = UserData::new(KVStore::default());
let url = Wasm::url(
"https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm",
);
let manifest = Manifest::new([url]);
let mut plugin = PluginBuilder::new(manifest)
.with_wasi(true)
.with_function(
"kv_read",
[PTR],
[PTR],
kv_store.clone(),
kv_read,
)
.with_function(
"kv_write",
[PTR, PTR],
[],
kv_store.clone(),
kv_write,
)
.build()
.unwrap();
for _ in 0..5 {
let res = plugin
.call::<&str, &str>("count_vowels", "Hello, world!")
.unwrap();
println!("{}", res);
}
}
Note
: In order to write host functions you should get familiar with the methods on the CurrentPlugin and UserData types.
Now we can invoke the event:
let res = plugin.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
# => Read from key=count-vowels"
# => Writing value=3 from key=count-vowels"
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
let res = plugin.call::<&str, &str>("count_vowels", "Hello, world!").unwrap();
println!("{}", res);
# => Read from key=count-vowels"
# => Writing value=6 from key=count-vowels"
# => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}