From bfc2e2c66b2537d0d78ec7211e15137dda85fde3 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Tue, 12 Aug 2025 13:29:55 -0400 Subject: [PATCH] cli: Resolve clap/deprecated warnings Signed-off-by: Lann Martin --- Cargo.toml | 3 +- crates/runtime-factors/src/lib.rs | 4 +- crates/trigger/src/cli.rs | 3 +- src/clap_markdown/mod.rs | 76 ++++++++++++------------------- src/commands/build.rs | 2 +- src/commands/cloud.rs | 24 +++++----- src/commands/external.rs | 24 +++++----- src/commands/new.rs | 8 +--- src/commands/registry.rs | 2 +- src/commands/templates.rs | 2 +- src/commands/up.rs | 2 +- 11 files changed, 65 insertions(+), 85 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index feaa76d388..996494ad92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ rust-version = "1.86" anyhow = { workspace = true } async-trait = { workspace = true } bytes = { workspace = true } -clap = { workspace = true, features = ["derive", "env"] } +# 'deprecated' enables deprecation warnings +clap = { workspace = true, features = ["deprecated", "derive", "env"] } clearscreen = "4" comfy-table = "7" command-group = "2" diff --git a/crates/runtime-factors/src/lib.rs b/crates/runtime-factors/src/lib.rs index 45110cbc18..c0b8baf4d4 100644 --- a/crates/runtime-factors/src/lib.rs +++ b/crates/runtime-factors/src/lib.rs @@ -96,7 +96,7 @@ pub struct TriggerAppArgs { /// Set a key/value pair (key=value) in the application's /// default store. Any existing value will be overwritten. /// Can be used multiple times. - #[clap(long = "key-value", parse(try_from_str = parse_kv))] + #[clap(long = "key-value", value_parser = parse_kv)] pub key_values: Vec<(String, String)>, /// Run a SQLite statement such as a migration against the default database. @@ -116,7 +116,7 @@ pub struct TriggerAppArgs { /// /// This option may be repeated. If the same key is specified multiple times /// the last value will be used. - #[clap(long, multiple = true, value_parser = clap::value_parser!(VariableSource), + #[clap(long, value_parser = clap::value_parser!(VariableSource), value_name = "KEY=VALUE | @FILE.json | @FILE.toml")] pub variable: Vec, diff --git a/crates/trigger/src/cli.rs b/crates/trigger/src/cli.rs index d2cfab22f6..2f41ba1b72 100644 --- a/crates/trigger/src/cli.rs +++ b/crates/trigger/src/cli.rs @@ -41,7 +41,7 @@ pub const SPIN_WORKING_DIR: &str = "SPIN_WORKING_DIR"; /// A command that runs a TriggerExecutor. #[derive(Parser, Debug)] #[clap( - usage = "spin [COMMAND] [OPTIONS]", + override_usage = "spin [COMMAND] [OPTIONS]", next_help_heading = help_heading::() )] pub struct FactorsTriggerCommand, B: RuntimeFactorsBuilder> { @@ -89,7 +89,6 @@ pub struct FactorsTriggerCommand, B: RuntimeFactorsBuilde #[clap( name = FOLLOW_LOG_OPT, long = "follow", - multiple_occurrences = true, )] pub follow_components: Vec, diff --git a/src/clap_markdown/mod.rs b/src/clap_markdown/mod.rs index a83cc00a65..ddf2609887 100644 --- a/src/clap_markdown/mod.rs +++ b/src/clap_markdown/mod.rs @@ -3,7 +3,7 @@ //! the upstream crate because it is Clap 4 only. When we move to Clap 4 //! we should shift to using the original crate (and upstream changes such //! as alphabetisation). See licences in this directory and NOTICES.md. -//! +//! //! Autogenerate Markdown documentation for clap command-line tools //! //! See [**Examples**][Examples] for examples of the content `clap-markdown` @@ -79,9 +79,7 @@ pub fn help_markdown() -> String { } /// Format the help information for `command` as Markdown, with custom options. -pub fn help_markdown_custom( - options: &MarkdownOptions, -) -> String { +pub fn help_markdown_custom(options: &MarkdownOptions) -> String { let command = C::command(); return help_markdown_command_custom(&command, options); @@ -93,10 +91,7 @@ pub fn help_markdown_command(command: &clap::Command) -> String { } /// Format the help information for `command` as Markdown, with custom options. -pub fn help_markdown_command_custom( - command: &clap::Command, - options: &MarkdownOptions, -) -> String { +pub fn help_markdown_command_custom(command: &clap::Command, options: &MarkdownOptions) -> String { let mut buffer = String::with_capacity(100); write_help_markdown(&mut buffer, &command, options); @@ -121,11 +116,7 @@ pub fn print_help_markdown() { println!("{}", buffer); } -fn write_help_markdown( - buffer: &mut String, - command: &clap::Command, - options: &MarkdownOptions, -) { +fn write_help_markdown(buffer: &mut String, command: &clap::Command, options: &MarkdownOptions) { //---------------------------------- // Write the document title //---------------------------------- @@ -142,7 +133,8 @@ fn write_help_markdown( buffer, "This document contains the help content for the `{}` command-line program.\n", title_name - ).unwrap(); + ) + .unwrap(); //---------------------------------- // Write the table of contents @@ -155,8 +147,7 @@ fn write_help_markdown( if options.show_table_of_contents { writeln!(buffer, "**Command Overview:**\n").unwrap(); - build_table_of_contents_markdown(buffer, Vec::new(), command, 0) - .unwrap(); + build_table_of_contents_markdown(buffer, Vec::new(), command, 0).unwrap(); write!(buffer, "\n").unwrap(); } @@ -171,13 +162,17 @@ fn write_help_markdown( // Write the footer //----------------- if options.show_footer { - write!(buffer, r#"
+ write!( + buffer, + r#"
This document was generated automatically by clap-markdown. -"#).unwrap(); +"# + ) + .unwrap(); } } @@ -215,12 +210,7 @@ fn build_table_of_contents_markdown( //---------------------------------- for subcommand in command.get_subcommands().sorted_by_key(|c| c.get_name()) { - build_table_of_contents_markdown( - buffer, - command_path.clone(), - subcommand, - depth + 1, - )?; + build_table_of_contents_markdown(buffer, command_path.clone(), subcommand, depth + 1)?; } Ok(()) @@ -418,12 +408,7 @@ fn build_command_markdown( write!(buffer, "\n\n")?; for subcommand in command.get_subcommands().sorted_by_key(|c| c.get_name()) { - build_command_markdown( - buffer, - command_path.clone(), - subcommand, - depth + 1, - )?; + build_command_markdown(buffer, command_path.clone(), subcommand, depth + 1)?; } Ok(()) @@ -447,9 +432,7 @@ fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { let value_name: String = match arg.get_value_names() { // TODO: What if multiple names are provided? Some([name, ..]) => name.to_string(), - Some([]) => unreachable!( - "clap Arg::get_value_names() returned Some(..) of empty list" - ), + Some([]) => unreachable!("clap Arg::get_value_names() returned Some(..) of empty list"), None => arg.get_id().to_string().to_ascii_uppercase(), }; @@ -460,26 +443,29 @@ fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { } else { write!(buffer, "`-{short}`, `--{long}`")? } - }, + } (Some(short), None) => { if arg.get_action().takes_values() { write!(buffer, "`-{short} <{value_name}>`")? } else { write!(buffer, "`-{short}`")? } - }, + } (None, Some(long)) => { if arg.get_action().takes_values() { write!(buffer, "`--{} <{value_name}>`", long)? } else { write!(buffer, "`--{}`", long)? } - }, + } (None, None) => { - debug_assert!(arg.is_positional(), "unexpected non-positional Arg with neither short nor long name: {arg:?}"); + debug_assert!( + arg.is_positional(), + "unexpected non-positional Arg with neither short nor long name: {arg:?}" + ); write!(buffer, "`<{value_name}>`",)?; - }, + } } if let Some(help) = arg.get_long_help() { @@ -517,19 +503,17 @@ fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { //-------------------- let possible_values = arg - .get_possible_values() - .unwrap_or_default() + .get_value_parser() + .possible_values() .into_iter() + .flatten() .filter(|pv| !pv.is_hide_set()) .collect::>(); // Print possible values for options that take a value, but not for flags // that can only be either present or absent and do not take a value. - if !possible_values.is_empty() - && !matches!(arg.get_action(), clap::ArgAction::SetTrue) - { - let any_have_help: bool = - possible_values.iter().any(|pv| pv.get_help().is_some()); + if !possible_values.is_empty() && !matches!(arg.get_action(), clap::ArgAction::SetTrue) { + let any_have_help: bool = possible_values.iter().any(|pv| pv.get_help().is_some()); if any_have_help { // If any of the possible values have help text, print them @@ -548,7 +532,7 @@ fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { .map(|pv| match pv.get_help() { Some(help) => { format!(" - `{}`:\n {}\n", pv.get_name(), help) - }, + } None => format!(" - `{}`\n", pv.get_name()), }) .collect::>() diff --git a/src/commands/build.rs b/src/commands/build.rs index 16270640e9..44a06072c0 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -26,7 +26,7 @@ pub struct BuildCommand { pub app_source: Option, /// Component ID to build. This can be specified multiple times. The default is all components. - #[clap(short = 'c', long, multiple = true)] + #[clap(short = 'c', long)] pub component_id: Vec, /// Run the application after building. diff --git a/src/commands/cloud.rs b/src/commands/cloud.rs index d60ce5237e..570eb327ff 100644 --- a/src/commands/cloud.rs +++ b/src/commands/cloud.rs @@ -35,24 +35,24 @@ const DEFAULT_DEPLOY_PLUGIN: &str = "cloud"; const DEPLOY_PLUGIN_ENV: &str = "SPIN_DEPLOY_PLUGIN"; impl DeployCommand { - pub async fn run(self, app: clap::App<'_>) -> Result<()> { - const CMD: &str = "deploy"; + pub async fn run(self, cmd: clap::Command<'_>) -> Result<()> { + const SUBCMD: &str = "deploy"; - let deploy_plugin = deployment_plugin(CMD)?; - let mut cmd = vec![deploy_plugin, CMD.to_string()]; - cmd.append(&mut self.args.clone()); - execute_external_subcommand(cmd, app).await + let deploy_plugin = deployment_plugin(SUBCMD)?; + let mut subcmd = vec![deploy_plugin, SUBCMD.to_string()]; + subcmd.append(&mut self.args.clone()); + execute_external_subcommand(subcmd, cmd).await } } impl LoginCommand { - pub async fn run(self, app: clap::App<'_>) -> Result<()> { - const CMD: &str = "login"; + pub async fn run(self, cmd: clap::Command<'_>) -> Result<()> { + const SUBCMD: &str = "login"; - let deploy_plugin = deployment_plugin(CMD)?; - let mut cmd = vec![deploy_plugin, CMD.to_string()]; - cmd.append(&mut self.args.clone()); - execute_external_subcommand(cmd, app).await + let deploy_plugin = deployment_plugin(SUBCMD)?; + let mut subcmd = vec![deploy_plugin, SUBCMD.to_string()]; + subcmd.append(&mut self.args.clone()); + execute_external_subcommand(subcmd, cmd).await } } diff --git a/src/commands/external.rs b/src/commands/external.rs index 49909a57e6..40823e2ffe 100644 --- a/src/commands/external.rs +++ b/src/commands/external.rs @@ -53,15 +53,15 @@ pub fn predefined_externals() -> Vec<(String, String)> { /// indicate the plugin to execute. Passes all subsequent arguments on to the /// subprocess. pub async fn execute_external_subcommand( - cmd: Vec, - app: clap::App<'_>, + subcmd: Vec, + cmd: clap::Command<'_>, ) -> anyhow::Result<()> { - let (plugin_name, args, override_compatibility_check) = parse_subcommand(cmd)?; + let (plugin_name, args, override_compatibility_check) = parse_subcommand(subcmd)?; let plugin_store = PluginStore::try_default()?; let plugin_version = ensure_plugin_available( &plugin_name, &plugin_store, - app, + cmd, override_compatibility_check, ) .await?; @@ -115,7 +115,7 @@ fn set_kill_on_ctrl_c(child: &tokio::process::Child) { async fn ensure_plugin_available( plugin_name: &str, plugin_store: &PluginStore, - app: clap::App<'_>, + cmd: clap::Command<'_>, override_compatibility_check: bool, ) -> anyhow::Result> { let plugin_version = match plugin_store.read_plugin_manifest(plugin_name) { @@ -130,7 +130,7 @@ async fn ensure_plugin_available( Some(manifest.version().to_owned()) } Err(PluginError::NotFound(e)) => { - consider_install(plugin_name, plugin_store, app, &e).await? + consider_install(plugin_name, plugin_store, cmd, &e).await? } Err(e) => return Err(e.into()), }; @@ -140,7 +140,7 @@ async fn ensure_plugin_available( async fn consider_install( plugin_name: &str, plugin_store: &PluginStore, - app: clap::App<'_>, + cmd: clap::Command<'_>, e: &spin_plugins::error::NotFoundError, ) -> anyhow::Result> { if predefined_externals() @@ -176,7 +176,7 @@ async fn consider_install( tracing::debug!("Tried to resolve {plugin_name} to plugin, got {e}"); terminal::error!("'{plugin_name}' is not a known Spin command. See spin --help.\n"); - print_similar_commands(app, plugin_name); + print_similar_commands(cmd, plugin_name); process::exit(2); } @@ -280,8 +280,8 @@ async fn report_badger_result(badger: tokio::task::JoinHandle) { } } -fn print_similar_commands(app: clap::App, plugin_name: &str) { - let similar = similar_commands(app, plugin_name); +fn print_similar_commands(cmd: clap::Command, plugin_name: &str) { + let similar = similar_commands(cmd, plugin_name); match similar.len() { 0 => (), 1 => eprintln!("The most similar command is:"), @@ -295,8 +295,8 @@ fn print_similar_commands(app: clap::App, plugin_name: &str) { } } -fn similar_commands(app: clap::App, target: &str) -> Vec { - app.get_subcommands() +fn similar_commands(cmd: clap::Command, target: &str) -> Vec { + cmd.get_subcommands() .filter_map(|sc| { let actual_name = undecorate(sc.get_name()); if levenshtein::levenshtein(&actual_name, target) <= 2 { diff --git a/src/commands/new.rs b/src/commands/new.rs index a99e939ff0..8e70650205 100644 --- a/src/commands/new.rs +++ b/src/commands/new.rs @@ -33,11 +33,7 @@ pub struct TemplateNewCommandCore { pub template_id: Option, /// Filter templates to select by tags. - #[clap( - long = "tag", - multiple_occurrences = true, - conflicts_with = "template-id" - )] + #[clap(long = "tag", conflicts_with = "template-id")] pub tags: Vec, /// The directory in which to create the new application or component. @@ -50,7 +46,7 @@ pub struct TemplateNewCommandCore { pub init: bool, /// Parameter values to be passed to the template (in name=value format). - #[clap(short = 'v', long = "value", multiple_occurrences = true)] + #[clap(short = 'v', long = "value")] pub values: Vec, /// A TOML file which contains parameter values in name = "value" format. diff --git a/src/commands/registry.rs b/src/commands/registry.rs index 39a9cc4800..db064f5b07 100644 --- a/src/commands/registry.rs +++ b/src/commands/registry.rs @@ -73,7 +73,7 @@ pub struct Push { /// Specifies the OCI image manifest annotations (in key=value format). /// Any existing value will be overwritten. Can be used multiple times. - #[clap(long = "annotation", parse(try_from_str = parse_kv))] + #[clap(long = "annotation", value_parser = parse_kv)] pub annotations: Vec<(String, String)>, } diff --git a/src/commands/templates.rs b/src/commands/templates.rs index eb35bf923d..6d936c7383 100644 --- a/src/commands/templates.rs +++ b/src/commands/templates.rs @@ -475,7 +475,7 @@ impl Uninstall { #[derive(Parser, Debug)] pub struct List { /// Filter templates matching all provided tags. - #[clap(long = "tag", multiple_occurrences = true)] + #[clap(long = "tag")] pub tags: Vec, /// The format in which to list the templates. diff --git a/src/commands/up.rs b/src/commands/up.rs index 9e11fb58cf..d50352b229 100644 --- a/src/commands/up.rs +++ b/src/commands/up.rs @@ -89,7 +89,7 @@ pub struct UpCommand { pub insecure: bool, /// Pass an environment variable (key=value) to all components of the application. - #[clap(short = 'e', long = "env", parse(try_from_str = parse_env_var))] + #[clap(short = 'e', long = "env", value_parser = parse_env_var)] pub env: Vec<(String, String)>, /// Temporary directory for the static assets of the components.