1use std::io::Write; 2 3use clap::Command; 4 5fn main() -> Result<(), String> { 6 loop { 7 let line = readline()?; 8 let line = line.trim(); 9 if line.is_empty() { 10 continue; 11 } 12 13 match respond(line) { 14 Ok(quit) => { 15 if quit { 16 break; 17 } 18 } 19 Err(err) => { 20 write!(std::io::stdout(), "{err}").map_err(|e| e.to_string())?; 21 std::io::stdout().flush().map_err(|e| e.to_string())?; 22 } 23 } 24 } 25 26 Ok(()) 27} 28 29fn respond(line: &str) -> Result<bool, String> { 30 let args = shlex::split(line).ok_or("error: Invalid quoting")?; 31 let matches = cli() 32 .try_get_matches_from(args) 33 .map_err(|e| e.to_string())?; 34 match matches.subcommand() { 35 Some(("ping", _matches)) => { 36 write!(std::io::stdout(), "Pong").map_err(|e| e.to_string())?; 37 std::io::stdout().flush().map_err(|e| e.to_string())?; 38 } 39 Some(("quit", _matches)) => { 40 write!(std::io::stdout(), "Exiting ...").map_err(|e| e.to_string())?; 41 std::io::stdout().flush().map_err(|e| e.to_string())?; 42 return Ok(true); 43 } 44 Some((name, _matches)) => unimplemented!("{}", name), 45 None => unreachable!("subcommand required"), 46 } 47 48 Ok(false) 49} 50 51fn cli() -> Command { 52 // strip out usage 53 const PARSER_TEMPLATE: &str = "\ 54 {all-args} 55 "; 56 // strip out name/version 57 const APPLET_TEMPLATE: &str = "\ 58 {about-with-newline}\n\ 59 {usage-heading}\n {usage}\n\ 60 \n\ 61 {all-args}{after-help}\ 62 "; 63 64 Command::new("repl") 65 .multicall(true) 66 .arg_required_else_help(true) 67 .subcommand_required(true) 68 .subcommand_value_name("APPLET") 69 .subcommand_help_heading("APPLETS") 70 .help_template(PARSER_TEMPLATE) 71 .subcommand( 72 Command::new("ping") 73 .about("Get a response") 74 .help_template(APPLET_TEMPLATE), 75 ) 76 .subcommand( 77 Command::new("quit") 78 .alias("exit") 79 .about("Quit the REPL") 80 .help_template(APPLET_TEMPLATE), 81 ) 82} 83 84fn readline() -> Result<String, String> { 85 write!(std::io::stdout(), "$ ").map_err(|e| e.to_string())?; 86 std::io::stdout().flush().map_err(|e| e.to_string())?; 87 let mut buffer = String::new(); 88 std::io::stdin() 89 .read_line(&mut buffer) 90 .map_err(|e| e.to_string())?; 91 Ok(buffer) 92} 93