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
- Initialization: Commands are registered during application startup
- Lookup: During execution, the executor queries the registry
- 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