Skip to main content

#
repl/
repl.rs

    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}