| Crates.io | module-registry |
| lib.rs | module-registry |
| version | 0.1.0 |
| created_at | 2025-10-24 23:21:17.966023+00 |
| updated_at | 2025-10-24 23:21:17.966023+00 |
| description | Dynamic module/plugin registry with compile-time discovery and runtime instantiation |
| homepage | |
| repository | https://github.com/redasgard/module-registry |
| max_upload_size | |
| id | 1899457 |
| size | 319,918 |
Dynamic module/plugin registry with compile-time discovery and runtime instantiation for Rust.
inventoryRwLock for concurrent access[dependencies]
module-registry = "0.1"
# With tracing support
module-registry = { version = "0.1", features = ["tracing"] }
use module_registry::Module;
use anyhow::Result;
pub trait TextProcessor: Module {
fn process(&self, input: &str) -> Result<String>;
}
struct UpperCaseProcessor;
impl Module for UpperCaseProcessor {
fn name(&self) -> &str {
"uppercase"
}
fn module_type(&self) -> &str {
"text_processor"
}
}
impl TextProcessor for UpperCaseProcessor {
fn process(&self, input: &str) -> Result<String> {
Ok(input.to_uppercase())
}
}
use module_registry::ModuleRegistry;
// Create registry
let registry = ModuleRegistry::new();
// Register module
registry.register(
"uppercase",
"text_processor",
|| Ok(Box::new(Box::new(UpperCaseProcessor) as Box<dyn TextProcessor>))
);
// Create instance
let any_module = registry.create_any("uppercase")?;
let module = any_module.downcast::<Box<dyn TextProcessor>>()?;
// Use it
let result = module.process("hello world")?;
assert_eq!(result, "HELLO WORLD");
Use a single global registry across your application:
use module_registry::ModuleRegistry;
// Access global registry
let global = ModuleRegistry::global();
// Register modules
global.register("module1", "type1", create_module1);
global.register("module2", "type2", create_module2);
// Use anywhere in your app
let module = global.create_any("module1")?;
Use the register_module! macro for automatic registration:
use module_registry::register_module;
register_module!("my_module", "MyModule", create_my_module);
// Module is automatically registered at startup
Store and retrieve metadata about modules:
registry.register_with_metadata(
"my_module",
"processor",
"create_my_module",
"my_app::processors::my_module",
"MyModuleStruct",
factory_fn,
);
// Later, retrieve metadata
if let Some(metadata) = registry.get_metadata("my_module") {
println!("Module: {}", metadata.name);
println!("Type: {}", metadata.module_type);
println!("Struct: {}", metadata.struct_name);
println!("Path: {}", metadata.module_path);
}
let modules = registry.list_modules();
for module_name in modules {
println!("Available: {}", module_name);
}
// Check if specific module exists
if registry.has_module("my_module") {
println!("Module is registered");
}
Build extensible applications with runtime-loaded plugins:
// Define plugin interface
trait Plugin: Module {
fn initialize(&mut self) -> Result<()>;
fn execute(&self, context: &Context) -> Result<Output>;
}
// Users can add plugins at compile time or runtime
registry.register("my_plugin", "plugin", create_my_plugin);
// Load and execute plugins dynamically
for plugin_name in registry.list_modules() {
let plugin = registry.create_any(&plugin_name)?;
// ... execute plugin
}
Implement the service locator pattern:
trait Service: Module {
fn start(&mut self) -> Result<()>;
fn stop(&mut self) -> Result<()>;
}
// Register services
registry.register("database", "service", create_db_service);
registry.register("cache", "service", create_cache_service);
// Locate and use services
let db = registry.create_any("database")?;
Register multiple providers for the same interface:
trait DataProvider: Module {
fn fetch_data(&self) -> Result<Vec<u8>>;
}
// Register different providers
registry.register("postgres", "provider", create_postgres);
registry.register("mongodb", "provider", create_mongo);
registry.register("redis", "provider", create_redis);
// Choose provider at runtime
let provider_name = config.get_provider();
let provider = registry.create_any(&provider_name)?;
ModuleRegistry::new()Create a new empty registry.
ModuleRegistry::global()Get the global singleton registry instance.
register(name, module_type, factory)Register a module with a factory function.
create_any(name) -> Result<Box<dyn Any + Send + Sync>>Create a module instance (returns Any, must downcast).
create<T>(name) -> Result<Box<T>>Create and downcast to a specific type (experimental).
list_modules() -> Vec<String>Get all registered module names.
has_module(name) -> boolCheck if a module is registered.
get_metadata(name) -> Option<ModuleMetadata>Get metadata for a module.
clear()Clear all registered modules (useful for testing).
# Run tests
cargo test
# Run with tracing
cargo test --features tracing
# Run example
cargo run --example plugin_system
┌─────────────────────────┐
│ Application Code │
└─────────────────────────┘
│
│ register/create
▼
┌─────────────────────────┐
│ ModuleRegistry │
│ (Thread-Safe Store) │
└─────────────────────────┘
│
├──── Module 1 (Factory Fn)
├──── Module 2 (Factory Fn)
└──── Module N (Factory Fn)
│
│ create instance
▼
┌────────────────┐
│ Box<dyn Trait> │
└────────────────┘
| Feature | module-registry | Other Solutions |
|---|---|---|
| Compile-time discovery | ✅ inventory | Manual |
| Type-safe | ✅ Generic traits | Varies |
| Thread-safe | ✅ RwLock | Varies |
| Metadata | ✅ Full | Limited |
| Global registry | ✅ Built-in | DIY |
| Testing support | ✅ clear() method | Limited |
Extracted from Red Asgard, a security platform where it manages dynamic loading of analyzers, language processors, and security tools.
Licensed under MIT License. See LICENSE-MIT for details.
Contributions welcome! Areas of interest: