Developing a plugin
A practical walkthrough — we'll build, test and ship the uppercase-extractor
WASM plugin (the same one in plugins/examples/wasm-extractor).
1. Scaffold
cargo new --lib uppercase-extractor && cd uppercase-extractor
mkdir wit && cp <loadr repo>/crates/loadr-plugin-api/wit/loadr.wit wit/
[package]
name = "uppercase-extractor"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.58"
serde_json = "1"
2. Implement
#![allow(unused)] fn main() { wit_bindgen::generate!({ path: "wit", world: "loadr-plugin" }); struct Plugin; impl exports::loadr::plugin::meta::Guest for Plugin { fn describe() -> exports::loadr::plugin::meta::Info { exports::loadr::plugin::meta::Info { name: "uppercase-extractor".into(), version: env!("CARGO_PKG_VERSION").into(), kind: "extractor".into(), description: "boundary extractor that upper-cases the match".into(), } } } impl exports::loadr::plugin::extractor::Guest for Plugin { fn extract(body: Vec<u8>, _headers: Vec<(String, String)>, config: String) -> Option<String> { let cfg: serde_json::Value = serde_json::from_str(&config).ok()?; let (left, right) = (cfg["left"].as_str()?, cfg["right"].as_str()?); let text = String::from_utf8_lossy(&body); let start = text.find(left)? + left.len(); let end = text[start..].find(right)? + start; Some(text[start..end].to_uppercase()) } } export!(Plugin); }
3. Build & package
rustup target add wasm32-wasip2
cargo build --release --target wasm32-wasip2
mkdir dist
cp target/wasm32-wasip2/release/uppercase_extractor.wasm dist/
cat > dist/plugin.toml <<'EOF'
[plugin]
name = "uppercase-extractor"
version = "0.1.0"
kind = "extractor"
type = "wasm"
entry = "uppercase_extractor.wasm"
description = "Boundary extractor that upper-cases the match"
EOF
4. Install & use
loadr plugin install ./dist
loadr plugin info uppercase-extractor
plugins: [ { name: uppercase-extractor, config: { left: "token=", right: ";" } } ]
Testing tips
- Drive the component directly in a Rust test with
loadr_plugin_api::WasmExtractor::load(path)— exactly what loadr's own test suite does for the examples. - For native plugins: build with
cargo build, thenNativePlugin::load("target/debug/libmy_plugin.so")in a test. - Keep configs JSON-serializable and document them in your README; loadr
passes the
config:value through verbatim.
Versioning rules
- WASM: the WIT package version (
loadr:plugin@0.1.0) is the contract. - Native:
abi_stablelayout checking is the contract; additionally the root module carriesabi_version— bump on breaking changes and loadr will refuse mismatches with a clean message.