4 releases (2 breaking)
new 0.3.1 | Mar 13, 2025 |
---|---|
0.3.0 | Mar 13, 2025 |
0.2.0 | Mar 11, 2025 |
0.1.1 | Mar 10, 2025 |
0.1.0 |
|
#583 in Rust patterns
243 downloads per month
79KB
486 lines
steckrs
A lightweight, trait-based plugin system for Rust applications. The name "steckrs" is a wordplay combining the German word "Stecker" (meaning "plug" or "connector") and the Rust file extension (.rs).
Features
- Type-Safe Extension Points: Define clear interfaces where plugins can add functionality
- Dynamic Plugin Lifecycle: Load, enable, disable, and unload plugins at runtime (currently only statically compiled)
- Hook Registration: Register implementations for extension points with proper type safety
- Low Boilerplate: Convenient macros make plugin implementation concise and readable
- Minimal Dependencies: Built with a focus on being lightweight and efficient
Installation
From crates.io
# in your rust/cargo project
cargo add steckrs
From source
git clone https://github.com/PlexSheep/steckrs.git
cd steckrs
cargo build --release
ls ./target/release/libsteckrs.rlib # here is the rlib if you want that
Quick Start
Here's a simple example of how to use steckrs to create a plugin-enabled application:
use steckrs::{extension_point, simple_plugin, PluginManager};
// Define an extension point with documentation
extension_point!(
GreeterExtension:
GreeterTrait;
/// foo
fn greet(&self, name: &str) -> String;
);
// Implement a hook
/// English implementation of the greeter
struct EnglishGreeter;
impl GreeterTrait for EnglishGreeter {
fn greet(&self, name: &str) -> String {
format!("Hello, {}!", name)
}
}
// Create a plugin with documentation
simple_plugin!(
/// A Plugin providing English greeting functionality
HelloPlugin,
"hello_plugin",
"A simple greeting plugin",
hooks: [(GreeterExtension, EnglishGreeter)]
);
fn main() {
// Create plugin manager
let mut plugin_manager = PluginManager::new();
// Load and enable the plugin
plugin_manager
.load_plugin(Box::new(HelloPlugin::new()))
.unwrap();
plugin_manager.enable_plugin(HelloPlugin::ID).unwrap();
// Get all enabled hooks (plugins could be disabled)
let hooks = plugin_manager.get_enabled_hooks_by_ep::<GreeterExtension>();
// Execute all hooks relevant for this extension point
for (_id, hook) in hooks {
println!("{}", hook.inner().greet("World"));
}
}
Core Concepts
Extension Points
Extension points define interfaces where plugins can add functionality. Each extension point:
- Is defined as a trait that plugins implement
- Specifies the contract that plugins must fulfill
- Provides type-safe interaction between the core application and plugins
Plugins
Plugins are self-contained data structures that implement functionality for extension points. Each plugin:
- Has a unique identifier
- Can be enabled or disabled at runtime
- Can register multiple hooks to different extension points
- Has lifecycle methods (
on_load
,on_unload
)
Hooks
Hooks are implementations of extension points that plugins register. They:
- Implement the trait defined by an extension point
- Are invoked when the application calls that extension point
- Can be uniquely identified by their plugin ID, extension point, and optional discriminator
Macros
steckrs provides several convenience macros to reduce boilerplate:
extension_point!
- Defines an extension point and its associated traitsimple_plugin!
- Creates a simple plugin with minimal boilerplateregister_hook!
- Registers a hook with the hook registry
Advanced Usage
For more complex scenarios, you can implement the Plugin
trait directly, allowing for more customized plugin behavior and state management. A good starting point would be defining a plugin with simple_plugin!
and then expanding the macro.
Use Cases
- Modular Applications: Break monolithic applications into pluggable components
- Extensible Libraries: Allow users to extend your library with custom functionality
- Framework Development: Build frameworks that can be customized without modifying core code
- Runtime Customization: Add or modify functionality without having to hardcode small things
Planned features
- Dynamic Loading: It would be cool if plugins could be loaded from dynamic libraries in the future.
- Generics Support: Currently, handling functions with type parameters is
difficult in the traits that the extension points implement, because they cannot
be made into trade objects with
dyn
then.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
Distributed under the LGPL License. See LICENSE
for more information.
Dependencies
~0.6–1.2MB
~24K SLoC