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