sen-plugin-sdk: SDK for creating WASM plugins
This SDK provides utilities and helpers for creating WASM plugins with minimal boilerplate. Using this SDK, you can create a fully functional plugin in under 30 lines of code.
Table of Contents
- Project Setup
- Quick Start
- Arguments
- Error Handling
- Advanced Usage
- Manual Implementation
- Best Practices
- Troubleshooting
Project Setup
1. Create a New Plugin Project
2. Configure Cargo.toml
Your complete Cargo.toml should look like:
[]
= "my-plugin"
= "0.1.0"
= "2021"
[]
= ["cdylib"] # Required for WASM output
[]
= { = "0.7" }
# Optimize for size (optional but recommended)
[]
= "s"
= true
= true
3. Install WASM Target (One-Time)
4. Build Your Plugin
The output file will be at:
target/wasm32-unknown-unknown/release/my_plugin.wasm
Quick Start
A minimal plugin requires:
- A struct implementing the [
Plugin] trait - The [
export_plugin!] macro to generate WASM exports
use *;
;
export_plugin!;
Arguments
Positional Arguments
Positional arguments are passed in order:
new
.arg
.arg
Usage: copy src.txt dst.txt
In execute(), args are: ["src.txt", "dst.txt"]
Options (Flags with Values)
Named options with long and short forms:
new
.arg
.arg
.arg
Usage: greet Alice -g "Good morning" --count 3
Required Arguments
Mark arguments as required:
positional
.required
.help
Default Values
Provide fallback values:
option
.short
.default
.help
Argument Parsing in execute()
Arguments are passed as a Vec<String> in the order they appear.
The host handles option parsing; your plugin receives resolved values:
Error Handling
Plugins return [ExecuteResult] which can be:
Success
success
User Error (Exit Code 1)
For expected errors like invalid input:
System Error (Exit Code 101)
For unexpected internal errors:
Advanced Usage
Subcommands
Create nested command structures:
new
.subcommand
.subcommand
.subcommand
Plugin Metadata
Add author and version information:
new
.version
// Note: author is set on CommandSpec, not PluginManifest
Manual Implementation
If you need more control, you can implement the WASM exports manually
instead of using the SDK. This is what the export_plugin! macro generates:
use ;
use ;
// 1. Memory allocator for host-guest communication
pub extern "C"
// 2. Memory deallocator
pub extern "C"
// 3. Return plugin manifest (command specification)
pub extern "C"
// 4. Execute the command
pub extern "C"
// Helper: Pack pointer and length into i64
// Helper: Serialize value to guest memory
Best Practices
Do
- Keep plugins focused: One plugin, one responsibility
- Validate inputs early: Check arguments at the start of
execute() - Return meaningful errors: Include context in error messages
- Use default values: Make common cases convenient
- Document your commands: Use
.help()on all arguments
Don't
- Don't panic: Always return
ExecuteResult::user_errororsystem_error - Don't use unwrap(): Prefer
unwrap_or,unwrap_or_default, or match - Don't allocate excessively: WASM has limited memory
- Don't block forever: The host has CPU limits (fuel)
Example: Robust Argument Handling
Troubleshooting
Build Errors
Error: can't find crate for std
Make sure you're building for the correct target:
Error: crate-type must be cdylib
Add to your Cargo.toml:
[]
= ["cdylib"]
Runtime Errors
Error: API version mismatch
Your plugin was built with a different API version. Rebuild with the
matching sen-plugin-sdk version.
Error: Function not found: plugin_manifest
Make sure you have export_plugin!(YourPlugin); at the end of your lib.rs.
Error: Fuel exhausted
Your plugin is taking too long (possible infinite loop). The host limits CPU usage to prevent runaway plugins.
Debugging Tips
- Test locally first: Write unit tests for your
execute()logic - Check WASM size: Large plugins may have unnecessary dependencies
- Simplify arguments: Start with positional args, add options later
Examples
See the examples/ directory for complete working plugins:
examples/hello-plugin/: Manual implementation (no SDK)examples/greet-plugin/: SDK-based with options