1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
use colored::*; use std::io; use std::io::{stdout, Write}; pub mod traits; pub use crate::traits::*; struct NErrorHandler; impl NErrorHandler { fn new() -> Self { NErrorHandler } } impl ErrorHandler for NErrorHandler { fn input_void(&self) { unimplemented!() } fn wrong_command(&self, command: &str) { Console::log(LogTypes::ERR, format!("{}: not a valid command", command)); } } pub struct NHelpCommand<'a> { commands: &'a Vec<Box<dyn Command>>, } impl NHelpCommand<'_> { pub fn new(commands_register: &'static CommandsRegister) -> Self { NHelpCommand { commands: &commands_register.commands, } } fn get_command(&self, command_str: &str) -> Option<&Box<dyn Command>> { for command in self.commands { if command.get_command_name() == command_str { return Some(command); } for alias in command.get_command_alias() { if alias == command_str { return Some(command); } } } None } } impl Command for NHelpCommand<'_> { fn get_command_name(&self) -> &str { "help" } fn get_command_alias(&self) -> Vec<&str> { vec!["h", "?"] } fn get_help(&self) -> &str { "Get help for all commands:\ How to use: `help [command]`\ Aliases: `h` and `?`" } fn on_command(&self, args: Vec<&str>) { if args.len() < 1 { let mut content = String::new(); for command in self.commands { content.push_str(command.get_command_name()); content.push('\n'); content.push_str(command.get_help()); content.push_str("\n\n"); } Console::print(content); } else if args.len() == 1 { let command: &Box<dyn Command>; if let Some(c) = self.get_command(args[0]) { command = c; } else { return; } let mut content = String::new(); content.push_str(command.get_command_name()); content.push('\n'); content.push_str(command.get_help()); content.push_str("\n\n"); Console::print(content); } } } pub struct CommandsRegister { commands: Vec<Box<dyn Command>>, error_handler: Box<dyn ErrorHandler>, } impl CommandsRegister { pub fn new() -> CommandsRegister { CommandsRegister { commands: vec![], error_handler: Box::new(NErrorHandler::new()), } } pub fn get_error_handler(&self) -> &dyn ErrorHandler { self.error_handler.as_ref() } pub fn set_error_handler(&mut self, error_handler: impl ErrorHandler + 'static) { self.error_handler = Box::new(error_handler); } pub fn get_command(&self, command_str: &str) -> Option<&Box<dyn Command>> { for command in &self.commands { if command.get_command_name() == command_str { return Some(command); } for alias in command.get_command_alias() { if alias == command_str { return Some(command); } } } None } pub fn start(&self) { self.commands.iter().for_each(|command| { command.start(); }); } pub fn end(&self) { self.commands.iter().for_each(|command| { command.end(); }); } pub fn register_command(&mut self, command: impl Command + 'static) { self.commands.push(Box::new(command)); } pub fn check_input(&self, input: String) -> bool { let mut input = input.split_whitespace(); let command_or_alias = input.next().unwrap(); let mut args: Vec<&str> = vec![]; for arg in input { args.push(arg); } if let Some(command) = self.get_command(command_or_alias) { command.on_command(args); return true; } false } } pub enum LogTypes { INFO, WARN, ERR, } pub struct Console { prompt: String, commands_register: CommandsRegister, } impl Console { pub fn new(prompt: String, commands_register: CommandsRegister) -> Console { Console { prompt, commands_register, } } pub fn log(log_type: LogTypes, message: String) { match log_type { LogTypes::INFO => println!("{} {}", "[INFO]".green(), message), LogTypes::WARN => println!("{} {}", "[WARNING]".yellow(), message), LogTypes::ERR => println!("{} {}", "[ERROR]".red(), message), } } pub fn print(message: String) { println!("{}", message); } pub fn update(&self) { let mut input = String::new(); print!("{}", self.prompt); stdout().flush().unwrap(); io::stdin() .read_line(&mut input) .expect("Failed to read line"); if input.trim().is_empty() { self.commands_register.get_error_handler().input_void(); return; } let copy = input.clone(); let command = copy.split_ascii_whitespace().next().unwrap(); let handled = self.commands_register.check_input(input); if !handled { self.commands_register .get_error_handler() .wrong_command(command); } } } #[cfg(test)] mod tests {}