5 releases (2 stable)
Uses new Rust 2024
| new 1.0.1 | Mar 7, 2026 |
|---|---|
| 0.10.0 | Feb 14, 2026 |
| 0.9.1 | Feb 7, 2026 |
| 0.9.0 | Feb 6, 2026 |
#377 in FFI
300KB
5.5K
SLoC
rustbridge-consumer
Rust consumer for dynamically loading and invoking rustbridge plugins.
Overview
This crate enables Rust applications to load and invoke rustbridge plugin bundles (.rbp files) or shared libraries at runtime. It provides the same functionality available to Java, Kotlin, C#, and Python consumers.
Key Features
- Dynamic Loading: Load plugins at runtime without compile-time linking
- Bundle Support: Load from
.rbpbundles with automatic platform detection - JSON Transport: Make calls using JSON serialization
- Binary Transport: High-performance binary struct transport (7x faster)
- Lifecycle Management: Full OSGI-inspired lifecycle state machine
- Logging Integration: Route plugin logs through host callbacks
Usage
Loading a Shared Library
use rustbridge_consumer::{NativePluginLoader, ConsumerResult};
fn main() -> ConsumerResult<()> {
// Load plugin from shared library
let plugin = NativePluginLoader::load("target/release/libmy_plugin.so")?;
// Make a JSON call
let response = plugin.call("echo", r#"{"message": "Hello"}"#)?;
println!("Response: {response}");
// Plugin is automatically shut down when dropped
Ok(())
}
Loading from a Bundle
use rustbridge_consumer::{NativePluginLoader, PluginConfig, ConsumerResult};
fn main() -> ConsumerResult<()> {
let config = PluginConfig::default();
// Load from bundle (extracts library for current platform)
let plugin = NativePluginLoader::load_bundle_with_config(
"my-plugin-1.0.0.rbp",
&config,
None, // No log callback
)?;
// Make typed calls with automatic serialization
let response: MyResponse = plugin.call_typed("my.handler", &MyRequest { ... })?;
Ok(())
}
With Logging
use rustbridge_consumer::{NativePluginLoader, LogCallbackFn};
use rustbridge_core::LogLevel;
fn main() -> ConsumerResult<()> {
// Create a log callback
let log_callback: LogCallbackFn = Box::new(|level, target, message| {
println!("[{level}] {target}: {message}");
});
let plugin = NativePluginLoader::load_with_config(
"target/release/libmy_plugin.so",
&PluginConfig::default(),
Some(log_callback),
)?;
// Plugin logs will now be routed to our callback
Ok(())
}
Binary Transport
For performance-critical paths, use binary transport:
use rustbridge_consumer::NativePluginLoader;
fn main() -> ConsumerResult<()> {
let plugin = NativePluginLoader::load("target/release/libmy_plugin.so")?;
// Check if binary transport is available
if plugin.has_binary_transport() {
// Make binary call (message_id maps to registered handler)
let response_bytes = plugin.call_raw(1, &request_bytes)?;
}
Ok(())
}
Plugin Lifecycle
Plugins follow an OSGI-inspired lifecycle:
Installed -> Starting -> Active -> Stopping -> Stopped
|
(any) -> Failed
- Installed: Plugin loaded but not initialized
- Starting: Plugin is initializing
- Active: Plugin is ready to handle requests
- Stopping: Plugin is shutting down
- Stopped: Plugin has been shut down
- Failed: Plugin encountered a fatal error
Error Handling
All operations return ConsumerResult<T> which wraps ConsumerError:
use rustbridge_consumer::{ConsumerError, ConsumerResult};
fn handle_plugin() -> ConsumerResult<()> {
let plugin = NativePluginLoader::load("path/to/plugin.so")?;
match plugin.call("handler", "{}") {
Ok(response) => println!("Success: {response}"),
Err(ConsumerError::NotActive(state)) => {
println!("Plugin not active: {state:?}");
}
Err(ConsumerError::CallFailed(e)) => {
println!("Call failed with code {}: {e}", e.error_code());
}
Err(e) => return Err(e),
}
Ok(())
}
License
MIT OR Apache-2.0
Dependencies
~11–26MB
~273K SLoC