Skip to content

Commit

Permalink
docs: Add REPL example
Browse files Browse the repository at this point in the history
This is to help in cases like clap-rs#3668 and clap-rs#3673
  • Loading branch information
epage committed May 2, 2022
1 parent ff53b08 commit ec4735a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Expand Up @@ -143,6 +143,7 @@ rustversion = "1"
trycmd = { version = "0.13", default-features = false, features = ["color-auto", "diff", "examples"] }
humantime = "2"
snapbox = "0.2.9"
shlex = "1.1.0"

[[example]]
name = "demo"
Expand Down Expand Up @@ -182,6 +183,11 @@ name = "hostname"
path = "examples/multicall-hostname.rs"
required-features = ["unstable-multicall"]

[[example]]
name = "repl"
path = "examples/repl.rs"
required-features = ["unstable-multicall"]

[[example]]
name = "01_quick"
path = "examples/tutorial_builder/01_quick.rs"
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Expand Up @@ -26,6 +26,9 @@
- hostname: [builder](multicall-hostname.md)
- Topics:
- Subcommands
- repl: [builder](repl.rs)
- Topics:
- Read-Eval-Print Loops / Custom command lines

## Contributing

Expand Down
3 changes: 1 addition & 2 deletions examples/multicall-hostname.rs
Expand Up @@ -4,14 +4,13 @@ use clap::Command;

fn main() {
let cmd = Command::new(env!("CARGO_CRATE_NAME"))
.multicall(true)
.arg_required_else_help(true)
.subcommand_value_name("APPLET")
.subcommand_help_heading("APPLETS")
.subcommand(Command::new("hostname").about("show hostname part of FQDN"))
.subcommand(Command::new("dnsdomainname").about("show domain name part of FQDN"));

let cmd = cmd.multicall(true);

match cmd.get_matches().subcommand_name() {
Some("hostname") => println!("www"),
Some("dnsdomainname") => println!("example.com"),
Expand Down
95 changes: 95 additions & 0 deletions examples/repl.rs
@@ -0,0 +1,95 @@
// Note: this requires the `unstable-multicall` feature

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<'static> {
// 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)
.disable_help_flag(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)
}

0 comments on commit ec4735a

Please sign in to comment.