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, help_msg: Option, action: DirectiveAction, }, NonTerminal { name: String, children: Vec, help_msg: Option, action: DirectiveAction, }, Top(Vec), } 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, 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::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(""), CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""), CommandTree::Top(_) => "", } } pub fn get_children(&self) -> &Vec { 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( &self, repl: &mut Repl, 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()), } } }