diff --git a/Cargo.lock b/Cargo.lock index 1974ad0..4b6643f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,15 @@ dependencies = [ "log", ] +[[package]] +name = "clap_complete" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.1.8" @@ -295,6 +304,7 @@ dependencies = [ "chrono", "clap", "clap-verbosity-flag", + "clap_complete", "convert_case", "dialoguer", "dotenvy", diff --git a/curlz/Cargo.toml b/curlz/Cargo.toml index 8f7c479..2309477 100644 --- a/curlz/Cargo.toml +++ b/curlz/Cargo.toml @@ -11,7 +11,7 @@ include = ["src/**/*", "LICENSE", "*.md"] env_logger = "0.10" log = "0.4" clap = { version = "4.1", features = ["derive", "std", "cargo", "usage", "help"] } -#clap_complete = "4.1" +clap_complete = "4.1" clap-verbosity-flag = "2.0" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" diff --git a/curlz/src/curlz/cli/execute.rs b/curlz/src/curlz/cli/execute.rs index 8010aad..bc5009c 100644 --- a/curlz/src/curlz/cli/execute.rs +++ b/curlz/src/curlz/cli/execute.rs @@ -1,19 +1,26 @@ use super::sub_commands::*; -use clap::Parser; +use clap::{Command, CommandFactory, Parser}; +use clap_complete::{generate, generator::Generator, Shell}; use clap_verbosity_flag::{InfoLevel, Verbosity}; use env_logger::Target; #[derive(Clone, Debug, Parser)] #[command(author, version, about, long_about = None )] -#[clap(subcommand_required = true, arg_required_else_help = true)] +#[command(arg_required_else_help = true)] #[command(propagate_version = true)] #[command(name = "curlz")] pub struct Cli { + /// increased verbosity will show more debug output of curlz itself #[command(flatten)] verbose: Verbosity, + + /// Generate a SHELL completion script and print to stdout + #[clap(long, short, value_name = "SHELL")] + pub completions: Option, + #[command(subcommand)] - pub command: SubCommands, + pub cmd: Option, } pub fn execute() -> crate::Result<()> { @@ -23,12 +30,26 @@ pub fn execute() -> crate::Result<()> { .target(Target::Stderr) .init(); - match args.command { - SubCommands::Request(ref r) => r.execute(), - SubCommands::Bookmark(_b) => { - todo!() + if let Some(shell) = args.completions { + let mut cmd = Cli::command(); + print_completions(shell, &mut cmd); + std::process::exit(0); + } + + if let Some(cmd) = &args.cmd { + match cmd { + SubCommands::Request(ref r) => r.execute(), + SubCommands::Bookmark(_b) => { + todo!() + } + #[cfg(feature = "x-http-lang")] + SubCommands::HttpFile(ref hf) => hf.execute(), } - #[cfg(feature = "x-http-lang")] - SubCommands::HttpFile(ref hf) => hf.execute(), + } else { + Ok(()) } } + +fn print_completions(gen: G, cmd: &mut Command) { + generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); +} diff --git a/curlz/tests/basics.rs b/curlz/tests/basics.rs index 42c2d2f..db690cd 100644 --- a/curlz/tests/basics.rs +++ b/curlz/tests/basics.rs @@ -9,13 +9,27 @@ mod testlib; #[test] fn should_show_usage_when_no_args_passed() { #[cfg(windows)] - let pattern = predicate::str::contains("Usage: curlz.exe [OPTIONS] "); + let pattern = predicate::str::contains("Usage: curlz.exe [OPTIONS] [COMMAND]"); #[cfg(not(windows))] - let pattern = predicate::str::contains("Usage: curlz [OPTIONS] "); + let pattern = predicate::str::contains("Usage: curlz [OPTIONS] [COMMAND]"); binary().assert().failure().stderr(pattern); } +#[test] +fn should_show_completions() { + #[cfg(windows)] + let pattern = predicate::str::contains("#compdef curlz"); + #[cfg(not(windows))] + let pattern = predicate::str::contains("#compdef curlz"); + + binary() + .args(["--completions", "zsh"]) + .assert() + .success() + .stdout(pattern); +} + #[tokio::test] async fn should_send_as_get() { CurlzTestSuite::new()