schala/schala-repl/src/command_tree.rs

128 lines
4.1 KiB
Rust
Raw Normal View History

2021-10-14 01:33:46 -07:00
use crate::directive_actions::DirectiveAction;
2021-10-14 00:56:01 -07:00
use crate::language::ProgrammingLanguageInterface;
2021-10-14 01:33:46 -07:00
use crate::{InterpreterDirectiveOutput, Repl};
use colored::*;
2019-05-22 03:10:12 -07:00
/// 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,
2019-06-01 18:41:55 -07:00
/// and then execute it with any remaining arguments
2018-10-15 20:52:34 -07:00
#[derive(Clone)]
pub enum CommandTree {
2021-10-07 01:19:35 -07:00
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>),
2018-10-15 20:52:34 -07:00
}
impl CommandTree {
2021-10-07 01:19:35 -07:00
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,
}
}
2018-10-15 21:46:27 -07:00
2021-10-07 01:19:35 -07:00
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,
}
}
2021-10-07 01:19:35 -07:00
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,
}
2018-10-15 21:46:27 -07:00
}
2021-10-07 01:19:35 -07:00
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(_) => "",
}
2018-10-15 20:52:34 -07:00
}
2021-10-07 01:19:35 -07:00
pub fn get_children(&self) -> &Vec<CommandTree> {
use CommandTree::*;
match self {
Terminal { children, .. } | NonTerminal { children, .. } | Top(children) => children,
}
2018-10-15 20:52:34 -07:00
}
2021-10-07 01:19:35 -07:00
pub fn get_subcommands(&self) -> Vec<&str> {
self.get_children().iter().map(|x| x.get_cmd()).collect()
2018-10-15 20:52:34 -07:00
}
2021-10-14 00:56:01 -07:00
pub fn perform<L: ProgrammingLanguageInterface>(
&self,
repl: &mut Repl<L>,
2021-10-14 02:08:32 -07:00
arguments: &[&str],
2021-10-14 00:56:01 -07:00
) -> InterpreterDirectiveOutput {
2021-10-07 01:19:35 -07:00
let mut dir_pointer: &CommandTree = self;
let mut idx = 0;
2021-10-07 01:19:35 -07:00
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,
2021-10-14 02:08:32 -07:00
None => break Err("Command requires arguments".to_owned()),
2021-10-07 01:19:35 -07:00
};
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));
}
}
};
2021-10-07 01:19:35 -07:00
match res {
Ok((action, idx)) => action.perform(repl, &arguments[idx..]),
Err(err) => Some(err.red().to_string()),
}
}
2018-10-15 20:52:34 -07:00
}