117 lines
3.9 KiB
Rust
117 lines
3.9 KiB
Rust
use colored::*;
|
|
|
|
use crate::{
|
|
directive_actions::DirectiveAction, language::ProgrammingLanguageInterface, InterpreterDirectiveOutput,
|
|
Repl,
|
|
};
|
|
|
|
/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
|
|
/// Terminal, it will use the `DirectiveAction` found there to find an appropriate function to execute,
|
|
/// and then execute it with any remaining arguments
|
|
#[derive(Clone)]
|
|
pub enum CommandTree {
|
|
Terminal {
|
|
name: String,
|
|
children: Vec<CommandTree>,
|
|
help_msg: Option<String>,
|
|
action: DirectiveAction,
|
|
},
|
|
NonTerminal {
|
|
name: String,
|
|
children: Vec<CommandTree>,
|
|
help_msg: Option<String>,
|
|
action: DirectiveAction,
|
|
},
|
|
Top(Vec<CommandTree>),
|
|
}
|
|
|
|
impl CommandTree {
|
|
pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> CommandTree {
|
|
CommandTree::NonTerminal {
|
|
name: s.to_string(),
|
|
help_msg: help.map(|x| x.to_string()),
|
|
children: vec![],
|
|
action: DirectiveAction::Null,
|
|
}
|
|
}
|
|
|
|
pub fn terminal(
|
|
s: &str,
|
|
help: Option<&str>,
|
|
children: Vec<CommandTree>,
|
|
action: DirectiveAction,
|
|
) -> CommandTree {
|
|
CommandTree::Terminal { name: s.to_string(), help_msg: help.map(|x| x.to_string()), children, action }
|
|
}
|
|
|
|
pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
|
|
CommandTree::NonTerminal {
|
|
name: s.to_string(),
|
|
help_msg: help.map(|x| x.to_string()),
|
|
children,
|
|
action: DirectiveAction::Null,
|
|
}
|
|
}
|
|
|
|
pub fn get_cmd(&self) -> &str {
|
|
match self {
|
|
CommandTree::Terminal { name, .. } => name.as_str(),
|
|
CommandTree::NonTerminal { name, .. } => name.as_str(),
|
|
CommandTree::Top(_) => "",
|
|
}
|
|
}
|
|
pub fn get_help(&self) -> &str {
|
|
match self {
|
|
CommandTree::Terminal { help_msg, .. } =>
|
|
help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
|
|
CommandTree::NonTerminal { help_msg, .. } =>
|
|
help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
|
|
CommandTree::Top(_) => "",
|
|
}
|
|
}
|
|
pub fn get_children(&self) -> &Vec<CommandTree> {
|
|
use CommandTree::*;
|
|
match self {
|
|
Terminal { children, .. } | NonTerminal { children, .. } | Top(children) => children,
|
|
}
|
|
}
|
|
pub fn get_subcommands(&self) -> Vec<&str> {
|
|
self.get_children().iter().map(|x| x.get_cmd()).collect()
|
|
}
|
|
|
|
pub fn perform<L: ProgrammingLanguageInterface>(
|
|
&self,
|
|
repl: &mut Repl<L>,
|
|
arguments: &[&str],
|
|
) -> InterpreterDirectiveOutput {
|
|
let mut dir_pointer: &CommandTree = self;
|
|
let mut idx = 0;
|
|
|
|
let res: Result<(DirectiveAction, usize), String> = loop {
|
|
match dir_pointer {
|
|
CommandTree::Top(subcommands) | CommandTree::NonTerminal { children: subcommands, .. } => {
|
|
let next_command = match arguments.get(idx) {
|
|
Some(cmd) => cmd,
|
|
None => break Err("Command requires arguments".to_owned()),
|
|
};
|
|
idx += 1;
|
|
match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) {
|
|
Some(command_tree) => {
|
|
dir_pointer = command_tree;
|
|
}
|
|
None => break Err(format!("Command {} not found", next_command)),
|
|
};
|
|
}
|
|
CommandTree::Terminal { action, .. } => {
|
|
break Ok((action.clone(), idx));
|
|
}
|
|
}
|
|
};
|
|
|
|
match res {
|
|
Ok((action, idx)) => action.perform(repl, &arguments[idx..]),
|
|
Err(err) => Some(err.red().to_string()),
|
|
}
|
|
}
|
|
}
|