use colored::*;
use std::io::{stdin, stdout};
use std::io::Write;
use std::str::SplitWhitespace;
use std::str::FromStr;
use super::core::MpCore;
type Command = Option<()>;
type MapHelp = (&'static str, &'static str, &'static str, Option<MapFn>);
type MapFn = fn (&mut MpConsole,
command: &str,
arguments: &mut SplitWhitespace) -> Command;
const PROMPT: &'static str = "@ ";
const UNTITLED_PROJECT: &'static str = "untitled";
const MAP_HELP: [MapHelp; 5] = [
("add", "[type] [*name]", "Add a data.", Some(MpConsole::add)),
("revert", "[?steps]", "Cancel the previous command.", Some(MpConsole::revert)),
("help", "[?command]", "Print help.", Some(MpConsole::help)),
("source", "[path]", "Perform a pre-written script file.", Some(MpConsole::source)),
("exit", "", "Exit the console.", None),
];
struct MpConsole {
project_name: &'static str,
core: MpCore,
}
impl MpConsole {
fn new(project_path: Option<&str>) -> MpConsole {
init();
match project_path {
Some(path) => {
unimplemented!()
},
None => MpConsole {
project_name: UNTITLED_PROJECT,
core: MpCore::new(),
},
}
}
pub fn begin_interactive(&mut self) -> Command {
loop {
write_prompt(self.project_name)?;
let mut input = String::new();
stdin().read_line(&mut input).unwrap();
let mut args = input.trim().split_whitespace();
let command = match args.next() {
Some(c) => c,
None => continue,
};
let mut cmd_found = false;
for (cmd, _, _, map_fn) in MAP_HELP.iter() {
if *cmd == command {
match map_fn {
Some(map_fn) => map_fn(self, command, &mut args),
None => {
exit();
return Some(())
},
};
cmd_found = true;
break
}
}
if ! cmd_found {
command_not_found(command)
}
}
}
fn add(&mut self, command: &str, args: &mut SplitWhitespace) -> Command {
let t = args.next_str(command)?;
let name = args.rest();
args.finish(command)?;
println!(" * Type: {}", t);
if name.len() > 0 {
println!(" * Name: {}", name);
}
yet()
}
fn revert(&mut self, command: &str, args: &mut SplitWhitespace) -> Command {
let steps = args.next_int(command)?;
args.finish(command)?;
println!(" * Steps: {}", steps);
yet()
}
fn help(&mut self, _: &str, args: &mut SplitWhitespace) -> Command {
help(args.next());
None
}
fn source(&mut self, command: &str, args: &mut SplitWhitespace) -> Command {
let path = args.next_str(command)?;
args.finish(command)?;
yet()
}
}
fn init() {
println!();
println!(" Machine Pseudo-Code");
println!();
println!(" * Copyright (C) 2019 {}", env!("CARGO_PKG_AUTHORS"));
println!(" * Homepage: \"{}\"", env!("CARGO_PKG_HOMEPAGE"));
println!(" * Version: {}", env!("CARGO_PKG_VERSION"));
println!();
}
fn write_prompt(project_name: &str) -> Command {
print!("{}{}", format!("[{}]", project_name).bright_green(), PROMPT);
match stdout().flush() {
Ok(_) => Some(()),
Err(_) => None,
}
}
fn help(command: Option<&str>) {
match command {
Some(cmd) => help_specific_command(cmd),
None => {
println!("Usage: <command> [options...]");
for (command, args, desc, _) in &MAP_HELP {
println!(" {:21} {}", format!("{} {}", command, args), desc)
}
}
}
}
fn err_not_integer(value: &str) {
println!("Value {:?} must be Integer.", value);
}
fn help_specific_command(command: &str) {
for (cmd, args, desc, _) in &MAP_HELP {
if *cmd == command {
println!("{}", desc);
println!(" * Usage: {} {}", command, args);
return
}
}
command_not_found(command)
}
fn command_not_found(command: &str) {
println!(" * Command not found: {:?}", command)
}
fn exit() {
println!("Goodbye.")
}
pub fn begin_interactive(project_path: Option<&str>) {
MpConsole::new(project_path).begin_interactive();
}
trait SplitWhitespaceForConsole<'a> {
fn next_str(&mut self, command: &str) -> Option<&'a str>;
fn next_int(&mut self, command: &str) -> Option<isize>;
fn rest(&mut self) -> String;
fn finish(&mut self, command: &str) -> Command;
}
impl<'a> SplitWhitespaceForConsole<'a> for SplitWhitespace<'a> {
fn next_str(&mut self, command: &str) -> Option<&'a str> {
match self.next() {
Some(arg) => Some(arg),
None => {
help_specific_command(command);
None
},
}
}
fn next_int(&mut self, command: &str) -> Option<isize> {
match self.next_str(command) {
Some(s) => match isize::from_str(s) {
Ok(v) => Some(v),
Err(_) => {
err_not_integer(s);
help_specific_command(command);
None
},
}
None => None,
}
}
fn rest(&mut self) -> String {
let mut result = String::new();
loop {
match self.next() {
Some(s) => {
result = if result.len() > 0 { result + " " + s }
else { result + s };
},
None => return result,
}
}
}
fn finish(&mut self, command: &str) -> Command {
match self.next() {
Some(arg) => {
println!("Unexpected argument: {:?}", arg);
help_specific_command(command);
None
},
None => Some(()),
}
}
}
fn yet() -> Command {
println!(" * Not implemented in this version.");
println!(" * Please use nightly version.");
None
}