#![forbid(unsafe_code)] use crate::commands::{ Command, CommandMap, Mode }; use crate::error::*; use std::io::Write; mod commands; mod error; struct TopLevelMode { command_map: Box>, } impl TopLevelMode { fn new() -> Result { let mut command_map = Box::new(CommandMap::new()); command_map.insert(Command::new("help", |mode, _params| { println!("help Print this message."); println!("quit Exit the program."); Ok(Some(mode)) }))?; command_map.insert(Command::new("quit", |_mode, _params| { Ok(None) }))?; Ok(TopLevelMode { command_map }) } } impl Mode for TopLevelMode { fn prompt_text(&self) -> Result<&str> { Ok("secrets>") } fn dispatch(self: Box, params: &[&str]) -> Result>> { if params.len() > 0 { if let Some(command) = self.command_map.get(params[0]) { (command.implementation)(self, ¶ms[1..]) } else { println!("No command named {}. Type help if you need help.", params[0]); Ok(Some(self)) } } else { default_empty_command()?; Ok(Some(self)) } } } fn default_empty_command() -> Result<()> { println!("No command? Type help if you need help."); Ok(()) } fn main() -> Result<()> { help(); let mut current_mode: Option> = Some(Box::new(TopLevelMode::new()?)); while let Some(mode) = current_mode { if let Some(input) = prompt(mode.prompt_text()?)? { let words: Vec<&str> = input.split_whitespace().collect(); current_mode = mode.dispatch(&words[..])?; } else { current_mode = None; } } Ok(()) } fn help() { println!("Type a command (there are none yet)"); } fn prompt(raw: &str) -> Result> { let mut stdout = std::io::stdout(); stdout.write_all(format!("\n{} ", raw).as_bytes())?; stdout.flush()?; Ok(std::io::stdin().lines().next().transpose()?) }