#[cfg(test)]
#[path = "./command_test.rs"]
mod command_test;
use crate::types::env::Env;
use crate::types::error::ScriptError;
use crate::types::instruction::Instruction;
use crate::types::runtime::StateValue;
use std::collections::HashMap;
pub struct CommandInvocationContext<'a> {
pub arguments: Vec<String>,
pub state: &'a mut HashMap<String, StateValue>,
pub variables: &'a mut HashMap<String, String>,
pub output_variable: Option<String>,
pub instructions: &'a Vec<Instruction>,
pub commands: &'a mut Commands,
pub line: usize,
pub env: &'a mut Env,
}
#[derive(Debug, Clone)]
pub enum GoToValue {
Label(String),
Line(usize),
}
#[derive(Debug, Clone)]
pub enum CommandResult {
Continue(Option<String>),
GoTo(Option<String>, GoToValue),
Error(String),
Crash(String),
Exit(Option<String>),
}
pub trait Command {
fn name(&self) -> String;
fn aliases(&self) -> Vec<String> {
vec![]
}
fn help(&self) -> String {
format!("No documentation found for command: {}", self.name())
}
fn clone_and_box(&self) -> Box<dyn Command>;
fn run(&self, mut _context: CommandInvocationContext) -> CommandResult {
CommandResult::Crash(format!("Not implemented for command: {}", &self.name()).to_string())
}
}
pub type CommandBox = Box<dyn Command>;
impl Clone for Box<dyn Command> {
fn clone(&self) -> Box<dyn Command> {
self.clone_and_box()
}
}
#[derive(Clone)]
pub struct Commands {
pub commands: HashMap<String, CommandBox>,
pub aliases: HashMap<String, String>,
}
impl Commands {
pub fn new() -> Commands {
Commands {
commands: HashMap::new(),
aliases: HashMap::new(),
}
}
pub fn set(&mut self, command: CommandBox) -> Result<(), ScriptError> {
let name = command.name();
let aliases = command.aliases();
if self.commands.contains_key(&name) {
return Err(ScriptError::Initialization(format!(
"Command: {} already defined.",
&name
)));
}
for alias in &aliases {
if self.aliases.contains_key(alias) {
return Err(ScriptError::Initialization(format!(
"Alias: {} for command: {} already defined.",
&alias, &name
)));
}
}
self.commands.insert(name.clone(), command);
self.aliases.remove(&name);
for alias in &aliases {
self.aliases.insert(alias.to_string(), name.clone());
}
Ok(())
}
pub fn get(&self, name: &str) -> Option<&CommandBox> {
let command_name = match self.aliases.get(name) {
Some(ref value) => value,
None => name,
};
match self.commands.get(command_name) {
Some(ref value) => Some(value),
None => None,
}
}
pub fn exists(&self, name: &str) -> bool {
let command = self.get(name);
command.is_some()
}
pub fn get_for_use(&mut self, name: &str) -> Option<CommandBox> {
let command_name = match self.aliases.get(name) {
Some(ref value) => value,
None => name,
};
match self.commands.get(command_name) {
Some(value) => Some(value.clone()),
None => None,
}
}
pub fn get_all_command_names(&self) -> Vec<String> {
let mut names = vec![];
for key in self.commands.keys() {
names.push(key.to_string());
}
names.sort();
names
}
pub fn remove(&mut self, name: &str) -> bool {
let command_name = match self.aliases.get(name) {
Some(ref value) => value,
None => name,
};
match self.commands.remove(command_name) {
Some(command) => {
let aliases = command.aliases();
for alias in &aliases {
self.aliases.remove(alias);
}
true
}
None => false,
}
}
}