use std::collections::HashMap;
use slash_lang::parser::ast::Command;
use crate::command::SlashCommand;
use crate::env::SlenvLoader;
use crate::executor::{CommandOutput, CommandRunner, ExecutionError, PipeValue};
pub struct CommandRegistry {
commands: HashMap<String, Box<dyn SlashCommand>>,
env: SlenvLoader,
}
impl CommandRegistry {
pub fn new(env: SlenvLoader) -> Self {
Self {
commands: HashMap::new(),
env,
}
}
pub fn register(&mut self, cmd: Box<dyn SlashCommand>) {
self.commands.insert(cmd.name().to_string(), cmd);
}
pub fn env(&self) -> &SlenvLoader {
&self.env
}
pub fn env_mut(&mut self) -> &mut SlenvLoader {
&mut self.env
}
}
impl CommandRunner for CommandRegistry {
fn run(
&self,
cmd: &Command,
input: Option<&PipeValue>,
) -> Result<CommandOutput, ExecutionError> {
let handler = self
.commands
.get(&cmd.name)
.ok_or_else(|| ExecutionError::Runner(format!("unknown command: /{}", cmd.name)))?;
let primary = cmd.primary.as_ref().map(|p| self.env.resolve(p));
let resolved_args: Vec<slash_lang::parser::ast::Arg> = cmd
.args
.iter()
.map(|a| slash_lang::parser::ast::Arg {
name: a.name.clone(),
value: a.value.as_ref().map(|v| self.env.resolve(v)),
})
.collect();
let valid_methods = handler.methods();
for arg in &resolved_args {
if !valid_methods.iter().any(|m| m.name == arg.name) {
let known: Vec<&str> = valid_methods.iter().map(|m| m.name).collect();
return Err(ExecutionError::Runner(format!(
"/{}: unknown method '.{}' — valid methods: {}",
cmd.name,
arg.name,
if known.is_empty() {
"(none)".to_string()
} else {
known.join(", ")
},
)));
}
}
handler.execute(primary.as_deref(), &resolved_args, input)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::executor::{Execute, Executor};
fn registry() -> CommandRegistry {
CommandRegistry::new(SlenvLoader::empty())
}
#[test]
fn unknown_command_errors() {
let reg = registry();
let ex = Executor::new(reg);
let prog = slash_lang::parser::parse("/nonexistent").unwrap();
assert!(ex.execute(&prog).is_err());
}
#[test]
fn env_resolution_in_args() {
let mut env = SlenvLoader::empty();
env.insert_mut("MSG", "resolved");
let reg = CommandRegistry::new(env);
assert_eq!(reg.env().resolve("$MSG"), "resolved");
}
}