use rustc_hash::FxHashMap as HashMap;
pub trait Command: Send + Sync {
fn execute(&self, args: &[String]) -> Result<String, String>;
fn description(&self) -> &str;
}
pub struct CommandRegistry {
commands: HashMap<String, Box<dyn Command>>,
}
impl CommandRegistry {
#[must_use]
pub fn new() -> Self {
let mut registry = Self {
commands: HashMap::default(),
};
registry.register("help".to_string(), Box::new(HelpCommand::new(®istry)));
registry.register("version".to_string(), Box::new(VersionCommand));
registry
}
pub fn register(&mut self, name: String, command: Box<dyn Command>) {
self.commands.insert(name, command);
}
pub fn execute(&self, name: &str, args: &[String]) -> Result<String, String> {
self.commands.get(name).map_or_else(
|| Err(format!("unknown command: {name}")),
|cmd| cmd.execute(args),
)
}
#[must_use]
pub fn list_commands(&self) -> Vec<&str> {
self.commands
.keys()
.map(std::string::String::as_str)
.collect()
}
}
impl Default for CommandRegistry {
fn default() -> Self {
Self::new()
}
}
struct HelpCommand {
registry_snapshot: Vec<String>,
}
impl HelpCommand {
fn new(registry: &CommandRegistry) -> Self {
Self {
registry_snapshot: registry
.list_commands()
.iter()
.map(std::string::ToString::to_string)
.collect(),
}
}
}
impl Command for HelpCommand {
fn execute(&self, _args: &[String]) -> Result<String, String> {
Ok(format!("available commands: {:?}", self.registry_snapshot))
}
fn description(&self) -> &'static str {
"show available commands"
}
}
struct VersionCommand;
impl Command for VersionCommand {
fn execute(&self, _args: &[String]) -> Result<String, String> {
Ok(env!("CARGO_PKG_VERSION").to_string())
}
fn description(&self) -> &'static str {
"show engine version"
}
}