Skip to main content

## Module repl

Source
Available on crate feature `unstable-doc` only.
Expand description

## §Example: Command REPL (Builder API)

    use std::io::Write;

    use clap::Command;

    fn main() -> Result<(), String> {
        loop {
            let line = readline()?;
            let line = line.trim();
            if line.is_empty() {
                continue;
            }

            match respond(line) {
                Ok(quit) => {
                    if quit {
                        break;
                    }
                }
                Err(err) => {
                    write!(std::io::stdout(), "{err}").map_err(|e| e.to_string())?;
                    std::io::stdout().flush().map_err(|e| e.to_string())?;
                }
            }
        }

        Ok(())
    }

    fn respond(line: &str) -> Result<bool, String> {
        let args = shlex::split(line).ok_or("error: Invalid quoting")?;
        let matches = cli()
            .try_get_matches_from(args)
            .map_err(|e| e.to_string())?;
        match matches.subcommand() {
            Some(("ping", _matches)) => {
                write!(std::io::stdout(), "Pong").map_err(|e| e.to_string())?;
                std::io::stdout().flush().map_err(|e| e.to_string())?;
            }
            Some(("quit", _matches)) => {
                write!(std::io::stdout(), "Exiting ...").map_err(|e| e.to_string())?;
                std::io::stdout().flush().map_err(|e| e.to_string())?;
                return Ok(true);
            }
            Some((name, _matches)) => unimplemented!("{name}"),
            None => unreachable!("subcommand required"),
        }

        Ok(false)
    }

    fn cli() -> Command {
        // strip out usage
        const PARSER_TEMPLATE: &str = "\
            {all-args}
        ";
        // strip out name/version
        const APPLET_TEMPLATE: &str = "\
            {about-with-newline}\n\
            {usage-heading}\n    {usage}\n\
            \n\
            {all-args}{after-help}\
        ";

        Command::new("repl")
            .multicall(true)
            .arg_required_else_help(true)
            .subcommand_required(true)
            .subcommand_value_name("APPLET")
            .subcommand_help_heading("APPLETS")
            .help_template(PARSER_TEMPLATE)
            .subcommand(
                Command::new("ping")
                    .about("Get a response")
                    .help_template(APPLET_TEMPLATE),
            )
            .subcommand(
                Command::new("quit")
                    .alias("exit")
                    .about("Quit the REPL")
                    .help_template(APPLET_TEMPLATE),
            )
    }

    fn readline() -> Result<String, String> {
        write!(std::io::stdout(), "$ ").map_err(|e| e.to_string())?;
        std::io::stdout().flush().map_err(|e| e.to_string())?;
        let mut buffer = String::new();
        std::io::stdin()
            .read_line(&mut buffer)
            .map_err(|e| e.to_string())?;
        Ok(buffer)
    }