Skip to main content

Module registry

Module registry 

Source
Expand description

Command registry module

This module provides the central registry for storing and managing commands. The registry maintains the mapping between command names/aliases and their definitions and handlers.

§Architecture

The registry serves as a lookup table for the executor:

Configuration → Registry → Executor
    (YAML)        (Store)    (Execute)

§Flow

  1. Initialization: Commands are registered during application startup
  2. Lookup: During execution, the executor queries the registry
  3. Dispatch: The registry returns the appropriate handler

§Design Principles

§Separation of Concerns

The registry separates:

  • Definition (from config module): What the command accepts
  • Implementation (from executor module): What the command does
  • Lookup (this module): How to find commands

§Efficient Lookup

The registry uses HashMaps for O(1) lookup by:

  • Command name
  • Command alias

This ensures fast command resolution even with many registered commands.

§Validation

The registry validates during registration:

  • No duplicate command names
  • No duplicate aliases
  • No conflicts between names and aliases

§Quick Start

use dynamic_cli::registry::CommandRegistry;
use dynamic_cli::config::schema::CommandDefinition;
use dynamic_cli::executor::CommandHandler;
use std::collections::HashMap;

// 1. Create a registry
let mut registry = CommandRegistry::new();

// 2. Define a command
let definition = CommandDefinition {
    name: "hello".to_string(),
    aliases: vec!["hi".to_string()],
    description: "Say hello".to_string(),
    required: false,
    arguments: vec![],
    options: vec![],
    implementation: "hello_handler".to_string(),
};

// 3. Create a handler
struct HelloCommand;
impl CommandHandler for HelloCommand {
    fn execute(
        &self,
        _ctx: &mut dyn dynamic_cli::context::ExecutionContext,
        args: &HashMap<String, String>,
    ) -> dynamic_cli::Result<()> {
        let default_world = "World".to_string();
        let name = args.get("name").unwrap_or(&default_world);
        println!("Hello, {}!", name);
        Ok(())
    }
}

// 4. Register the command
registry.register(definition, Box::new(HelloCommand))?;

// 5. Use the registry
if let Some(handler) = registry.get_handler("hello") {
    // Execute the command
}

// Works with aliases too!
if let Some(handler) = registry.get_handler("hi") {
    // Same handler
}

§Examples

§Basic Registration

use dynamic_cli::registry::CommandRegistry;

let mut registry = CommandRegistry::new();

registry.register(definition, Box::new(TestCmd))?;

§Command with Multiple Aliases

let definition = CommandDefinition {
    name: "simulate".to_string(),
    aliases: vec![
        "sim".to_string(),
        "run".to_string(),
        "exec".to_string(),
    ],
    description: "Run a simulation".to_string(),
    required: false,
    arguments: vec![],
    options: vec![],
    implementation: "simulate_handler".to_string(),
};

registry.register(definition, Box::new(SimCmd))?;

// All these work:
assert!(registry.contains("simulate"));
assert!(registry.contains("sim"));
assert!(registry.contains("run"));
assert!(registry.contains("exec"));

§Listing All Commands

// Get all commands for help text
for cmd in registry.list_commands() {
    println!("{}: {}", cmd.name, cmd.description);
}

§Error Handling

// Try to register duplicate
let result = registry.register(def2, Box::new(TestCmd));

match result {
    Err(DynamicCliError::Registry(RegistryError::DuplicateRegistration { name, .. })) => {
        eprintln!("Command '{}' already registered", name);
    }
    _ => {}
}

§Integration with Other Modules

§With Config Module

The registry stores CommandDefinition from the config module:

use dynamic_cli::config::loader::load_config;
use dynamic_cli::registry::CommandRegistry;

let config = load_config("commands.yaml")?;
let mut registry = CommandRegistry::new();

for cmd_def in config.commands {
    let handler = create_handler(&cmd_def.implementation);
    registry.register(cmd_def, handler)?;
}

§With Executor Module

The executor queries the registry to find handlers:

use dynamic_cli::registry::CommandRegistry;

fn execute_command(
    registry: &CommandRegistry,
    command_name: &str,
    context: &mut dyn ExecutionContext,
    args: &HashMap<String, String>,
) -> Result<()> {
    let handler = registry.get_handler(command_name)
        .ok_or_else(|| anyhow::anyhow!("Unknown command"))?;
     
    handler.execute(context, args)
}

§Thread Safety

The registry is designed for setup-once, use-many pattern:

use std::sync::Arc;

// Setup phase (single-threaded)
let mut registry = CommandRegistry::new();
// ... register commands ...

// Usage phase (can be multi-threaded)
let registry = Arc::new(registry);
let registry_clone = registry.clone();

std::thread::spawn(move || {
    // Safe to use in multiple threads
    if let Some(handler) = registry_clone.get_handler("test") {
        // ...
    }
});

Re-exports§

pub use command_registry::CommandRegistry;

Modules§

command_registry
Command registry implementation