module-registry

Crates.iomodule-registry
lib.rsmodule-registry
version0.1.0
created_at2025-10-24 23:21:17.966023+00
updated_at2025-10-24 23:21:17.966023+00
descriptionDynamic module/plugin registry with compile-time discovery and runtime instantiation
homepage
repositoryhttps://github.com/redasgard/module-registry
max_upload_size
id1899457
size319,918
Yevhen Pervushyn (ilyk)

documentation

README

Module Registry

Crates.io Documentation License: MIT Test Coverage

Dynamic module/plugin registry with compile-time discovery and runtime instantiation for Rust.

Features

  • Compile-Time Discovery: Automatic module registration using inventory
  • Runtime Instantiation: Create modules dynamically by name
  • Type-Safe: Generic factory functions with trait objects
  • Thread-Safe: Built on RwLock for concurrent access
  • Zero-Cost: Minimal runtime overhead
  • Flexible: Works with any module type and custom configuration

Installation

[dependencies]
module-registry = "0.1"

# With tracing support
module-registry = { version = "0.1", features = ["tracing"] }

Quick Start

1. Define Your Module Trait

use module_registry::Module;
use anyhow::Result;

pub trait TextProcessor: Module {
    fn process(&self, input: &str) -> Result<String>;
}

2. Implement a Module

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())
    }
}

3. Register and Use

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");

Advanced Usage

Global Registry

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")?;

Compile-Time Registration

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

Module Metadata

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);
}

List Available Modules

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");
}

Use Cases

Plugin Systems

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
}

Service Locator

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")?;

Provider Pattern

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)?;

API Reference

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) -> bool

Check if a module is registered.

get_metadata(name) -> Option<ModuleMetadata>

Get metadata for a module.

clear()

Clear all registered modules (useful for testing).

Testing

# Run tests
cargo test

# Run with tracing
cargo test --features tracing

# Run example
cargo run --example plugin_system

Architecture

┌─────────────────────────┐
│  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>  │
              └────────────────┘

Performance

  • Registration: O(1) - HashMap insertion
  • Lookup: O(1) - HashMap access
  • Memory: Minimal - only stores metadata and function pointers
  • Thread Safety: RwLock allows multiple concurrent readers

Comparison

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

Origin

Extracted from Red Asgard, a security platform where it manages dynamic loading of analyzers, language processors, and security tools.

License

Licensed under MIT License. See LICENSE-MIT for details.

Contributing

Contributions welcome! Areas of interest:

  • Additional examples
  • Performance optimizations
  • Documentation improvements
  • Type-safety enhancements

Contact

Commit count: 0

cargo fmt