1 | use std::io::Write; |
2 | |
3 | use clap::Command; |
4 | |
5 | fn 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 | |
29 | fn 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 | |
51 | fn 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 | |
84 | fn 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 | |