diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 406b528079c..f7ee2427fa7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -167,7 +167,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.90" # STABLE + toolchain: "1.91" # STABLE - uses: Swatinem/rust-cache@v2 - name: UI Tests run: make test-ui-${{ matrix.features }} @@ -210,7 +210,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.90" # STABLE + toolchain: "1.91" # STABLE - uses: Swatinem/rust-cache@v2 - name: Check documentation env: @@ -225,7 +225,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.90" # STABLE + toolchain: "1.91" # STABLE components: rustfmt - uses: Swatinem/rust-cache@v2 - name: Check formatting @@ -239,7 +239,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.90" # STABLE + toolchain: "1.91" # STABLE components: clippy - uses: Swatinem/rust-cache@v2 - name: Lint (ultra-minimal) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 1c06b6e58c0..c203003e970 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.x' - uses: pre-commit/action@v3.0.1 diff --git a/Cargo.lock b/Cargo.lock index 9c48a66c2f7..e0871b158dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -498,7 +498,7 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.59" +version = "4.5.61" dependencies = [ "automod", "clap 4.5.51", @@ -513,7 +513,7 @@ dependencies = [ [[package]] name = "clap_complete_nushell" -version = "4.5.9" +version = "4.5.10" dependencies = [ "clap 4.5.51", "clap_complete", diff --git a/Makefile b/Makefile index 56e525477a3..c3560a40f1b 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ ifneq (${TOOLCHAIN_TARGET},) ARGS+=--target ${TOOLCHAIN_TARGET} endif -STABLE?=1.90 +STABLE?=1.91 _FEATURES = minimal default wasm full debug release _FEATURES_minimal = --no-default-features --features "std" diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs index edba6a48eb0..7df14fcc337 100644 --- a/clap_builder/src/builder/arg.rs +++ b/clap_builder/src/builder/arg.rs @@ -492,7 +492,7 @@ impl Arg { /// ///
/// - /// **NOTE:** When utilized with [`Arg::num_args(1..)`], only the **last** positional argument + /// **NOTE:** When utilized with [`Arg::num_args(1..)`][Arg::num_args], only the **last** positional argument /// may be defined as having a variable number of arguments (i.e. with the highest index) /// ///
@@ -1359,7 +1359,7 @@ impl Arg { /// ///
/// - /// **NOTE:** implicitly sets [`Arg::action(ArgAction::Set)`]. + /// **NOTE:** implicitly sets [`Arg::action(ArgAction::Set)`][ArgAction::Set]. /// ///
/// @@ -1487,7 +1487,7 @@ impl Arg { /// /// **WARNING:** Prior arguments with `allow_hyphen_values(true)` get precedence over known /// flags but known flags get precedence over the next possible positional argument with - /// `allow_hyphen_values(true)`. When combined with [`Arg::num_args(..)`], + /// `allow_hyphen_values(true)`. When combined with [`Arg::num_args(..)`][Arg::num_args], /// [`Arg::value_terminator`] is one way to ensure processing stops. /// /// diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs index 70861ee491a..c65c67a02de 100644 --- a/clap_builder/src/builder/command.rs +++ b/clap_builder/src/builder/command.rs @@ -27,6 +27,7 @@ use crate::output::fmt::Stream; use crate::output::{fmt::Colorizer, write_help, Usage}; use crate::parser::{ArgMatcher, ArgMatches, Parser}; use crate::util::ChildGraph; +use crate::util::FlatSet; use crate::util::{color::ColorChoice, Id}; use crate::{Error, INTERNAL_ERROR_MSG}; @@ -104,6 +105,7 @@ pub struct Command { current_disp_ord: Option, subcommand_value_name: Option, subcommand_heading: Option, + help_heading: Option>, external_value_parser: Option, long_help_exists: bool, deferred: Option Command>, @@ -3701,6 +3703,82 @@ impl Command { self.subcommand_heading = heading.into_resettable().into_option(); self } + + /// Set a custom help heading + /// + /// To place the `help` subcommand under a custom heading amend the default + /// heading using [`Command::subcommand_help_heading`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .version("2.6") + /// .subcommand( + /// Command::new("show") + /// .about("Help for show") + /// ) + /// .print_help() + /// # ; + /// ``` + /// + /// will produce + /// + /// ```text + /// myprog + /// + /// Usage: myprog [COMMAND] + /// + /// Commands: + /// help Print this message or the help of the given subcommand(s) + /// show Help for show + /// + /// Options: + /// -h, --help Print help + /// -V, --version Print version + /// ``` + /// + /// but usage of `help_heading` + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .version("2.6") + /// .subcommand( + /// Command::new("show") + /// .about("Help for show") + /// .help_heading("Custom heading"), + /// ) + /// .print_help() + /// # ; + /// ``` + /// + /// will produce + /// + /// ```text + /// myprog + /// + /// Usage: myprog [COMMAND] + /// + /// Commands: + /// help Print this message or the help of the given subcommand(s) + /// + /// Custom heading: + /// show Help for show + /// + /// Options: + /// -h, --help Print help + /// -V, --version Print version + /// ``` + #[inline] + #[must_use] + pub fn help_heading(mut self, heading: impl IntoResettable) -> Self { + self.help_heading = Some(heading.into_resettable().into_option()); + self + } } /// # Reflection @@ -3940,6 +4018,15 @@ impl Command { self.subcommand_heading.as_deref() } + /// Get the help heading specified for this command, if any + #[inline] + pub fn get_help_heading(&self) -> Option<&str> { + self.help_heading + .as_ref() + .map(|s| s.as_deref()) + .unwrap_or_default() + } + /// Returns the subcommand value name. #[inline] pub fn get_subcommand_value_name(&self) -> Option<&str> { @@ -4341,6 +4428,13 @@ impl Command { } } + #[allow(dead_code)] // overcome clippy minimal report that it is unused. + pub(crate) fn get_subcommand_custom_help_headings(&self) -> FlatSet<&str> { + self.get_subcommands() + .filter_map(|sc| sc.get_help_heading()) + .collect::>() + } + fn _do_parse( &mut self, raw_args: &mut clap_lex::RawArgs, @@ -5218,6 +5312,7 @@ impl Default for Command { current_disp_ord: Some(0), subcommand_value_name: Default::default(), subcommand_heading: Default::default(), + help_heading: Default::default(), external_value_parser: Default::default(), long_help_exists: false, deferred: None, diff --git a/clap_builder/src/error/kind.rs b/clap_builder/src/error/kind.rs index 298a4fd3277..7c487350eb6 100644 --- a/clap_builder/src/error/kind.rs +++ b/clap_builder/src/error/kind.rs @@ -224,7 +224,7 @@ pub enum ErrorKind { /// Occurs when the user provides a value containing invalid UTF-8. /// /// To allow arbitrary data - /// - Set [`Arg::value_parser(value_parser!(OsString))`] for argument values + /// - Set [`Arg::value_parser(value_parser!(OsString))`][crate::Arg::value_parser] for argument values /// - Set [`Command::external_subcommand_value_parser`] for external-subcommand /// values /// diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs index a0bc70fee1e..5542f6231b9 100644 --- a/clap_builder/src/output/help_template.rs +++ b/clap_builder/src/output/help_template.rs @@ -389,7 +389,7 @@ impl HelpTemplate<'_, '_> { .collect::>(); let subcmds = self.cmd.has_visible_subcommands(); - let custom_headings = self + let custom_arg_headings = self .cmd .get_arguments() .filter_map(|arg| arg.get_help_heading()) @@ -434,8 +434,8 @@ impl HelpTemplate<'_, '_> { let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",); self.write_args(&non_pos, "Options", option_sort_key); } - if !custom_headings.is_empty() { - for heading in custom_headings { + if !custom_arg_headings.is_empty() { + for heading in custom_arg_headings { let args = self .cmd .get_arguments() @@ -879,8 +879,6 @@ impl HelpTemplate<'_, '_> { cmd.get_name(), *first ); - use std::fmt::Write as _; - let header = &self.styles.get_header(); let mut ord_v = BTreeMap::new(); for subcommand in cmd @@ -892,44 +890,95 @@ impl HelpTemplate<'_, '_> { subcommand, ); } - for (_, subcommand) in ord_v { - if !*first { - self.writer.push_str("\n\n"); - } - *first = false; - let heading = subcommand.get_usage_name_fallback(); - let about = subcommand - .get_about() - .or_else(|| subcommand.get_long_about()) - .unwrap_or_default(); + let custom_sc_headings = self.cmd.get_subcommand_custom_help_headings(); - let _ = write!(self.writer, "{header}{heading}:{header:#}",); - if !about.is_empty() { - let _ = write!(self.writer, "\n{about}",); - } + // Commands under default heading + self.write_flat_subcommands_under_heading(&ord_v, None, first, false); - let args = subcommand - .get_arguments() - .filter(|arg| should_show_arg(self.use_long, arg) && !arg.is_global_set()) - .collect::>(); - if !args.is_empty() { - self.writer.push_str("\n"); - } + // Commands under custom headings + for heading in custom_sc_headings { + self.write_flat_subcommands_under_heading(&ord_v, Some(heading), first, false); + } - let mut sub_help = HelpTemplate { - writer: self.writer, - cmd: subcommand, - styles: self.styles, - usage: self.usage, - next_line_help: self.next_line_help, - term_w: self.term_w, - use_long: self.use_long, - }; - sub_help.write_args(&args, heading, option_sort_key); - if subcommand.is_flatten_help_set() { - sub_help.write_flat_subcommands(subcommand, first); - } + // Help command + self.write_flat_subcommands_under_heading(&ord_v, None, first, true); + } + + fn write_flat_subcommands_under_heading( + &mut self, + ord_v: &BTreeMap<(usize, &str), &Command>, + heading: Option<&str>, + first: &mut bool, + include_help: bool, + ) { + debug!("help_template::write subcommand under heading: `{heading:?}`"); + // If a custom heading is set ignore the include help flag + let bt: Vec<(&(usize, &str), &&Command)> = if heading.is_some() { + ord_v + .iter() + .filter(|item| item.1.get_help_heading() == heading) + .collect() + } else { + ord_v + .iter() + .filter(|item| item.1.get_help_heading() == heading) + .filter(|item| { + if include_help { + item.1.get_name() == "help" + } else { + item.1.get_name() != "help" + } + }) + .collect() + }; + + for (_, subcommand) in bt { + self.write_flat_subcommand(subcommand, first); + } + } + + fn write_flat_subcommand(&mut self, subcommand: &Command, first: &mut bool) { + use std::fmt::Write as _; + let header = &self.styles.get_header(); + + if !*first { + self.writer.push_str("\n\n"); + } + + *first = false; + + let heading = subcommand.get_usage_name_fallback(); + let about = subcommand + .get_about() + .or_else(|| subcommand.get_long_about()) + .unwrap_or_default(); + + let _ = write!(self.writer, "{header}{heading}:{header:#}",); + if !about.is_empty() { + let _ = write!(self.writer, "\n{about}",); + } + + let args = subcommand + .get_arguments() + .filter(|arg| should_show_arg(self.use_long, arg) && !arg.is_global_set()) + .collect::>(); + if !args.is_empty() { + self.writer.push_str("\n"); + } + + let mut sub_help = HelpTemplate { + writer: self.writer, + cmd: subcommand, + styles: self.styles, + usage: self.usage, + next_line_help: self.next_line_help, + term_w: self.term_w, + use_long: self.use_long, + }; + sub_help.write_args(&args, heading, option_sort_key); + if subcommand.is_flatten_help_set() { + sub_help.write_flat_subcommands(subcommand, first); } } @@ -963,7 +1012,39 @@ impl HelpTemplate<'_, '_> { let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest); - for (i, (sc_str, sc)) in ord_v.into_iter().enumerate() { + let custom_sc_headings = self.cmd.get_subcommand_custom_help_headings(); + + // User commands without heading + self.write_subcommands_under_heading(&ord_v, None, next_line_help, longest); + + // User commands with heading + for heading in custom_sc_headings { + self.write_subcommands_under_heading(&ord_v, Some(heading), next_line_help, longest); + } + } + + fn write_subcommands_under_heading( + &mut self, + ord_v: &BTreeMap<(usize, StyledStr), &Command>, + heading: Option<&str>, + next_line_help: bool, + longest: usize, + ) { + debug!("help_template::write subcommand under heading: `{heading:?}`"); + use std::fmt::Write as _; + let header = &self.styles.get_header(); + + if let Some(heading) = heading { + self.writer.push_str("\n\n"); + let _ = write!(self.writer, "{header}{heading}:{header:#}\n",); + } + + for (i, (sc_str, sc)) in ord_v + .clone() + .into_iter() + .filter(|item| item.1.get_help_heading() == heading) + .enumerate() + { if 0 < i { self.writer.push_str("\n"); } diff --git a/clap_builder/src/output/usage.rs b/clap_builder/src/output/usage.rs index 7c4ddc5ffca..db341c12f00 100644 --- a/clap_builder/src/output/usage.rs +++ b/clap_builder/src/output/usage.rs @@ -113,12 +113,49 @@ impl Usage<'_> { } let mut cmd = self.cmd.clone(); cmd.build(); - for (i, sub) in cmd + + let mut first = true; + // Get the custom headings + let custom_sc_headings = cmd.get_subcommand_custom_help_headings(); + + // Write commands without headings + for sub in cmd .get_subcommands() .filter(|c| !c.is_hide_set()) - .enumerate() + .filter(|c| c.get_help_heading().is_none()) + .filter(|c| c.get_name() != "help") { - if i != 0 { + if sub.get_name() == "help" { + continue; + } + if !first { + styled.trim_end(); + let _ = write!(styled, "{USAGE_SEP}"); + } + first = false; + Usage::new(sub).write_usage_no_title(styled, &[]); + } + + // Write commands with headings + for heading in custom_sc_headings { + for sub in cmd + .get_subcommands() + .filter(|c| !c.is_hide_set()) + .filter(|c| c.get_help_heading() == Some(heading)) + .filter(|c| c.get_name() != "help") + { + if !first { + styled.trim_end(); + let _ = write!(styled, "{USAGE_SEP}"); + } + first = false; + Usage::new(sub).write_usage_no_title(styled, &[]); + } + } + + // Write help command last (regardless of custom headings) + if let Some(sub) = cmd.find_subcommand("help") { + if !first { styled.trim_end(); let _ = write!(styled, "{USAGE_SEP}"); } diff --git a/clap_builder/src/util/color.rs b/clap_builder/src/util/color.rs index c1978a38400..cd02b9494c1 100644 --- a/clap_builder/src/util/color.rs +++ b/clap_builder/src/util/color.rs @@ -2,7 +2,7 @@ use crate::builder::PossibleValue; use crate::derive::ValueEnum; /// Represents the color preferences for program output -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] pub enum ColorChoice { /// Enables colored output only when the output is going to a terminal or TTY. /// @@ -23,6 +23,7 @@ pub enum ColorChoice { /// .get_matches(); /// # } /// ``` + #[default] Auto, /// Enables colored output regardless of whether or not the output is going to a terminal/TTY. @@ -65,12 +66,6 @@ impl ColorChoice { } } -impl Default for ColorChoice { - fn default() -> Self { - Self::Auto - } -} - impl std::fmt::Display for ColorChoice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.to_possible_value() diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index 0d440e341d5..31fcc7d9006 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +## [4.5.61] - 2025-11-19 + +## [4.5.60] - 2025-10-29 + +### Fixes + +- *(dynamic/powershell)* don't repeat previous words when completing + ## [4.5.59] - 2025-10-13 ## [4.5.58] - 2025-09-16 @@ -602,7 +610,9 @@ MSRV changed to 1.64.0 ## [3.0.1] - 2022-01-03 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.59...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.61...HEAD +[4.5.61]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.60...clap_complete-v4.5.61 +[4.5.60]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.59...clap_complete-v4.5.60 [4.5.59]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.58...clap_complete-v4.5.59 [4.5.58]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.57...clap_complete-v4.5.58 [4.5.57]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.57 diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 5b0731a86fa..1b53c3db907 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete" -version = "4.5.59" +version = "4.5.61" description = "Generate shell completion scripts for your clap::Command" categories = ["command-line-interface"] keywords = [ diff --git a/clap_complete/README.md b/clap_complete/README.md index 48f7a169d0a..3fc3e640021 100644 --- a/clap_complete/README.md +++ b/clap_complete/README.md @@ -5,16 +5,16 @@ [![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) [![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_complete) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/clap_complete/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/clap_complete/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/README.md#sponsors) ## About diff --git a/clap_complete_nushell/CHANGELOG.md b/clap_complete_nushell/CHANGELOG.md index f04f121dfad..c65da047ff0 100644 --- a/clap_complete_nushell/CHANGELOG.md +++ b/clap_complete_nushell/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +## [4.5.10] - 2025-10-31 + +### Fixes + +- Fix the order of positional arguments + ## [4.5.9] - 2025-10-13 ## [4.5.8] - 2025-07-09 @@ -60,7 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [4.3.0] - 2023-05-24 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.9...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.10...HEAD +[4.5.10]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.9...clap_complete_nushell-v4.5.10 [4.5.9]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.8...clap_complete_nushell-v4.5.9 [4.5.8]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.7...clap_complete_nushell-v4.5.8 [4.5.7]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.6...clap_complete_nushell-v4.5.7 diff --git a/clap_complete_nushell/Cargo.toml b/clap_complete_nushell/Cargo.toml index 790d48359a2..2cf7a7dbb52 100644 --- a/clap_complete_nushell/Cargo.toml +++ b/clap_complete_nushell/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete_nushell" -version = "4.5.9" +version = "4.5.10" description = "A generator library used with clap for Nushell completion scripts" categories = ["command-line-interface"] keywords = [ diff --git a/clap_complete_nushell/src/lib.rs b/clap_complete_nushell/src/lib.rs index d0c08540460..9939bf417a9 100644 --- a/clap_complete_nushell/src/lib.rs +++ b/clap_complete_nushell/src/lib.rs @@ -219,7 +219,16 @@ fn generate_completion(completions: &mut String, cmd: &Command, is_subcommand: b completions.push_str(format!(" export extern {name} [\n").as_str()); } - for arg in cmd.get_arguments() { + let flags: Vec<_> = cmd.get_arguments().filter(|a| !a.is_positional()).collect(); + let mut positionals: Vec<_> = cmd.get_positionals().collect(); + + positionals.sort_by_key(|arg| arg.get_index()); + + for arg in flags { + append_argument(arg, name, completions); + } + + for arg in positionals { append_argument(arg, name, completions); } diff --git a/clap_complete_nushell/tests/common.rs b/clap_complete_nushell/tests/common.rs index 0c38f0d0c4a..4d5bd02f215 100644 --- a/clap_complete_nushell/tests/common.rs +++ b/clap_complete_nushell/tests/common.rs @@ -168,6 +168,29 @@ pub(crate) fn sub_subcommands_command(name: &'static str) -> Command { ) } +pub(crate) fn positional_index_command(name: &'static str) -> Command { + Command::new(name) + .version("3.0") + .about("Tests positional argument index ordering") + .arg( + Arg::new("flag") + .short('f') + .long("flag") + .action(ArgAction::SetTrue) + .help("some flag"), + ) + .arg(Arg::new("third").index(3).help("third positional")) + .arg(Arg::new("first").index(1).help("first positional")) + .arg( + Arg::new("option") + .short('o') + .long("option") + .action(ArgAction::Set) + .help("some option"), + ) + .arg(Arg::new("second").index(2).help("second positional")) +} + pub(crate) fn value_hint_command(name: &'static str) -> Command { Command::new(name) .arg( diff --git a/clap_complete_nushell/tests/nushell.rs b/clap_complete_nushell/tests/nushell.rs index f3eacd85c0e..a05d2d482d6 100644 --- a/clap_complete_nushell/tests/nushell.rs +++ b/clap_complete_nushell/tests/nushell.rs @@ -83,3 +83,15 @@ fn value_hint() { name, ); } + +#[test] +fn positional_index() { + let name = "my-app"; + let cmd = common::positional_index_command(name); + common::assert_matches( + snapbox::file!["snapshots/positional_index.nu"], + clap_complete_nushell::Nushell, + cmd, + name, + ); +} diff --git a/clap_complete_nushell/tests/snapshots/aliases.nu b/clap_complete_nushell/tests/snapshots/aliases.nu index 8263e60315e..67a70d6ff85 100644 --- a/clap_complete_nushell/tests/snapshots/aliases.nu +++ b/clap_complete_nushell/tests/snapshots/aliases.nu @@ -8,9 +8,9 @@ module completions { --option(-o): string # cmd option --opt: string # cmd option -O: string # cmd option - positional?: string --help(-h) # Print help --version(-V) # Print version + positional?: string ] } diff --git a/clap_complete_nushell/tests/snapshots/feature_sample.nu b/clap_complete_nushell/tests/snapshots/feature_sample.nu index eb081b9b4de..912ac28aa78 100644 --- a/clap_complete_nushell/tests/snapshots/feature_sample.nu +++ b/clap_complete_nushell/tests/snapshots/feature_sample.nu @@ -6,13 +6,13 @@ module completions { # Tests completions export extern my-app [ - file?: path # some input file --config(-c) # some config file with another line --conf # some config file with another line -C # some config file with another line - choice?: string@"nu-complete my-app choice" --help(-h) # Print help --version(-V) # Print version + file?: path # some input file + choice?: string@"nu-complete my-app choice" ] # tests things diff --git a/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu b/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu index ac1e050f667..98e5762e4d8 100644 --- a/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu +++ b/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu @@ -111,10 +111,10 @@ module completions { --delim: string --tuple: string --require-eq: string - ...term: string --global # everywhere --help(-h) # Print help --version(-V) # Print version + ...term: string ] export extern "test pacman" [ @@ -150,11 +150,11 @@ module completions { ] export extern "test last" [ - first?: string - free?: string --global # everywhere --help(-h) # Print help --version(-V) # Print version + first?: string + free?: string ] export extern "test alias" [ @@ -164,10 +164,10 @@ module completions { --option(-o): string # cmd option --opt: string # cmd option -O: string # cmd option - positional?: string --global # everywhere --help(-h) # Print help --version(-V) # Print version + positional?: string ] def "nu-complete test hint choice" [] { @@ -184,7 +184,6 @@ module completions { --exe(-e): path --cmd-name: string --cmd(-c): string - command_with_args?: string --user(-u): string --host(-H): string --url: string @@ -192,6 +191,7 @@ module completions { --global # everywhere --help(-h) # Print help --version(-V) # Print version + command_with_args?: string ] # Print this message or the help of the given subcommand(s) diff --git a/clap_complete_nushell/tests/snapshots/positional_index.nu b/clap_complete_nushell/tests/snapshots/positional_index.nu new file mode 100644 index 00000000000..126a92fae99 --- /dev/null +++ b/clap_complete_nushell/tests/snapshots/positional_index.nu @@ -0,0 +1,16 @@ +module completions { + + # Tests positional argument index ordering + export extern my-app [ + --flag(-f) # some flag + --option(-o): string # some option + --help(-h) # Print help + --version(-V) # Print version + first?: string # first positional + second?: string # second positional + third?: string # third positional + ] + +} + +export use completions * diff --git a/clap_complete_nushell/tests/snapshots/special_commands.nu b/clap_complete_nushell/tests/snapshots/special_commands.nu index 0551531b3f7..6b6541d0786 100644 --- a/clap_complete_nushell/tests/snapshots/special_commands.nu +++ b/clap_complete_nushell/tests/snapshots/special_commands.nu @@ -6,13 +6,13 @@ module completions { # Tests completions export extern my-app [ - file?: path # some input file --config(-c) # some config file with another line --conf # some config file with another line -C # some config file with another line - choice?: string@"nu-complete my-app choice" --help(-h) # Print help --version(-V) # Print version + file?: path # some input file + choice?: string@"nu-complete my-app choice" ] # tests things @@ -25,9 +25,9 @@ module completions { # tests other things export extern "my-app some_cmd" [ --config: string # the other case to test - ...path: string --help(-h) # Print help --version(-V) # Print version + ...path: string ] export extern "my-app some-cmd-with-hyphens" [ diff --git a/clap_complete_nushell/tests/snapshots/sub_subcommands.nu b/clap_complete_nushell/tests/snapshots/sub_subcommands.nu index 2f56ca6d46c..a17626202c9 100644 --- a/clap_complete_nushell/tests/snapshots/sub_subcommands.nu +++ b/clap_complete_nushell/tests/snapshots/sub_subcommands.nu @@ -6,13 +6,13 @@ module completions { # Tests completions export extern my-app [ - file?: path # some input file --config(-c) # some config file with another line --conf # some config file with another line -C # some config file with another line - choice?: string@"nu-complete my-app choice" --help(-h) # Print help --version(-V) # Print version + file?: path # some input file + choice?: string@"nu-complete my-app choice" ] # tests things diff --git a/clap_complete_nushell/tests/snapshots/value_hint.nu b/clap_complete_nushell/tests/snapshots/value_hint.nu index f27849a5463..f3d2a470596 100644 --- a/clap_complete_nushell/tests/snapshots/value_hint.nu +++ b/clap_complete_nushell/tests/snapshots/value_hint.nu @@ -14,12 +14,12 @@ module completions { --exe(-e): path --cmd-name: string --cmd(-c): string - command_with_args?: string --user(-u): string --host(-H): string --url: string --email: string --help(-h) # Print help + command_with_args?: string ] } diff --git a/examples/tutorial_builder/04_04_custom.rs b/examples/tutorial_builder/04_04_custom.rs index 840b3aa4f71..98019dc71fe 100644 --- a/examples/tutorial_builder/04_04_custom.rs +++ b/examples/tutorial_builder/04_04_custom.rs @@ -37,7 +37,7 @@ fn main() { ) .exit(); } - ver.to_string() + ver.clone() } else { // Increment the one requested (in a real program, we'd reset the lower numbers) let (maj, min, pat) = ( diff --git a/tests/builder/help_heading.rs b/tests/builder/help_heading.rs new file mode 100644 index 00000000000..68e4a1f5de6 --- /dev/null +++ b/tests/builder/help_heading.rs @@ -0,0 +1,2849 @@ +#![cfg(feature = "help")] + +use clap::{Arg, Command}; + +use super::utils; + +fn setup() -> Command { + Command::new("test") + .author("Kevin K.") + .about("tests stuff") + .version("1.3") +} + +fn empty_args() -> impl IntoIterator { + std::iter::empty() +} + +#[test] +fn test_multiple_commands_mixed_standard_and_custom_headers() { + static VISIBLE_ALIAS_HELP: &str = "\ +Usage: clap-test [COMMAND] + +Help Section: + help Print this message or the help of the given subcommand(s) + +Commands: + def_cmd1 First command under default command heading + def_cmd4 Fourth command under default command heading + def_cmd3 Third command under default command heading + def_cmd2 Second command under default command heading + +First Custom: + ch1_cmd1 First command under first custom command heading + ch1_cmd2 Second command under first custom command heading + ch1_cmd4 Fourth command under first custom command heading + ch1_cmd3 Third command under first custom command heading + +Second Custom: + ch2_cmd3 Third command under second custom command heading + ch2_cmd4 Fourth command under second custom command heading + ch2_cmd2 Second command under second custom command heading + ch2_cmd1 First command under second custom command heading + +Options: + -h, --help Print help + -V, --version Print version +"; + + let cmd = Command::new("clap-test") + .version("2.6") + .subcommand_help_heading("Help Section") + .subcommand( + Command::new("def_cmd1") + .about("First command under default command heading") + .help_heading("Commands"), + ) + .subcommand( + Command::new("def_cmd4") + .about("Fourth command under default command heading") + .help_heading("Commands"), + ) + .subcommand( + Command::new("def_cmd3") + .about("Third command under default command heading") + .help_heading("Commands"), + ) + .subcommand( + Command::new("def_cmd2") + .about("Second command under default command heading") + .help_heading("Commands"), + ) + .subcommand( + Command::new("ch1_cmd1") + .about("First command under first custom command heading") + .help_heading("First Custom"), + ) + .subcommand( + Command::new("ch2_cmd3") + .about("Third command under second custom command heading") + .help_heading("Second Custom"), + ) + .subcommand( + Command::new("ch1_cmd2") + .about("Second command under first custom command heading") + .help_heading("First Custom"), + ) + .subcommand( + Command::new("ch1_cmd4") + .about("Fourth command under first custom command heading") + .help_heading("First Custom"), + ) + .subcommand( + Command::new("ch2_cmd4") + .about("Fourth command under second custom command heading") + .help_heading("Second Custom"), + ) + .subcommand( + Command::new("ch2_cmd2") + .about("Second command under second custom command heading") + .help_heading("Second Custom"), + ) + .subcommand( + Command::new("ch2_cmd1") + .about("First command under second custom command heading") + .help_heading("Second Custom"), + ) + .subcommand( + Command::new("ch1_cmd3") + .about("Third command under first custom command heading") + .help_heading("First Custom"), + ); + + utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false); + } + +#[test] +fn test_multiple_commands_mixed_headings_flatten() { + static VISIBLE_ALIAS_HELP: &str = "\ +Usage: clap-test + clap-test def_cmd1 + clap-test def_cmd2 + clap-test cust_cmd1 + clap-test cust_cmd2 --child + clap-test other_cmd1 + clap-test other_cmd2 --child + clap-test help [COMMAND]... + +Options: + -h, --help Print help + -V, --version Print version + +clap-test def_cmd1: +Def_cmd1 under default command heading + -h, --help Print help + +clap-test def_cmd2: +Def_cmd2 under default command heading + -h, --help Print help + +clap-test cust_cmd1: +Cust_cmd1 under custom command heading + -h, --help Print help + +clap-test cust_cmd2: +Cust_cmd2 under custom command heading + --child child help + -h, --help Print help + +clap-test other_cmd1: +Other_cmd1 under other command heading + -h, --help Print help + +clap-test other_cmd2: +Other_cmd2 under other command heading + --child + -h, --help Print help + +clap-test help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) +"; + + let cmd = Command::new("clap-test") + .version("2.6") + .flatten_help(true) + .subcommand( + Command::new("def_cmd1") + .about("Def_cmd1 under default command heading") + ) + .subcommand( + Command::new("cust_cmd1") + .about("Cust_cmd1 under custom command heading") + .help_heading("Custom Heading"), + ) + .subcommand( + Command::new("other_cmd1") + .about("Other_cmd1 under other command heading") + .help_heading("Other Heading"), + ) + .subcommand( + Command::new("other_cmd2") + .about("Other_cmd2 under other command heading") + .help_heading("Other Heading") + .arg(Arg::new("child").long("child").required(true)), + ) + .subcommand( + Command::new("def_cmd2") + .about("Def_cmd2 under default command heading") + ) + .subcommand( + Command::new("cust_cmd2") + .about("Cust_cmd2 under custom command heading") + .help_heading("Custom Heading") + .arg(Arg::new("child").long("child").required(true).help("child help")), + ); + + utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false); +} + + +#[test] +fn test_help_header_hide_commands_header() { + static VISIBLE_ALIAS_HELP: &str = "\ +Usage: clap-test [COMMAND] + +Test commands: + test Some help + +Options: + -h, --help Print help + -V, --version Print version +"; + + let cmd = Command::new("clap-test") + .version("2.6") + .disable_help_subcommand(true) + .subcommand_help_heading("Test commands") + .subcommand(Command::new("test").about("Some help")); + utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false); +} + + + +mod adjusted_help{ + + +// This file is created from a copy of help.rs +// Any test not containing a subcommand is discarded +// Any test with subcommand has help_heading added + +use clap::{arg, error::ErrorKind, Arg, ArgAction, Command}; +use snapbox::assert_data_eq; +use snapbox::str; + +use super::utils; +use super::setup; +use super::empty_args; + + +#[test] +fn help_subcommand() { + let m = setup() + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("tests things") + .arg(arg!(-v --verbose "with verbosity")), + ) + .try_get_matches_from(vec!["myprog", "help"]); + + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp); +} + +#[test] +#[cfg(feature = "error-context")] +fn help_multi_subcommand_error() { + let cmd = Command::new("ctest").subcommand( + Command::new("subcmd").subcommand( + Command::new("multi") + .help_heading("Custom Heading") + .about("tests subcommands") + .author("Kevin K. ") + .version("0.1") + .arg(arg!( + -f --flag "tests flags" + )) + .arg( + arg!( + -o --option "tests options" + ) + .required(false) + .num_args(1..) + .action(ArgAction::Append), + ), + ), + ); + let err = cmd + .try_get_matches_from(["ctest", "help", "subcmd", "multi", "foo"]) + .unwrap_err(); + + assert_data_eq!( + err.to_string(), + str![[r#" +error: unrecognized subcommand 'foo' + +Usage: ctest subcmd multi [OPTIONS] + +For more information, try '--help'. + +"#]] + ); +} + +#[test] +fn subcommand_short_help() { + let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "subcmd", "-h"]); + + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp); +} + +#[test] +fn subcommand_long_help() { + let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "subcmd", "--help"]); + + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp); +} + +#[test] +fn subcommand_help_rev() { + let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "help", "subcmd"]); + + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp); +} + +// Updated to include the custom heading in the expected output +#[test] +fn complex_help_output() { + let expected = str![[r#" +clap-test v1.4.8 +Kevin K. +tests clap library + +Usage: clap-test [OPTIONS] [positional] [positional2] [positional3]... [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + subcmd tests subcommands + +Arguments: + [positional] tests positionals + [positional2] tests positionals with exclusions + [positional3]... tests specific values [possible values: vi, emacs] + +Options: + -o, --option ... tests options + -f, --flag... tests flags + -F tests flags with exclusions + --long-option-2 tests long options with exclusions + -O, --option3 specific vals [possible values: fast, slow] + --multvals Tests multiple values, not mult occs + --multvalsmo Tests multiple values, and mult occs + --minvals2 ... Tests 2 min vals + --maxvals3 ... Tests 3 max vals + --optvaleq[=] Tests optional value, require = sign + --optvalnoeq [] Tests optional value + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(utils::complex_app_with_help_heading(), "clap-test --help", expected, false); +} + +#[test] +fn multi_level_sc_help() { + let cmd = Command::new("ctest").subcommand( + Command::new("subcmd") + .help_heading("First Heading") + .subcommand( + Command::new("multi") + .about("tests subcommands") + .help_heading("Second Heading") + .author("Kevin K. ") + .version("0.1") + .arg(arg!( + -f --flag "tests flags" + )) + .arg( + arg!( + -o --option "tests options" + ) + .required(false) + .num_args(1..) + .action(ArgAction::Append), + ), + ), + ); + + let expected = str![[r#" +tests subcommands + +Usage: ctest subcmd multi [OPTIONS] + +Options: + -f, --flag tests flags + -o, --option ... tests options + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "ctest help subcmd multi", expected, false); +} + +#[test] +fn no_wrap_default_help() { + let cmd = Command::new("ctest").version("1.0").term_width(0); + + let expected = str![[r#" +Usage: ctest + +Options: + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "ctest --help", expected, false); +} + + +#[test] +fn try_help_subcommand_default() { + let cmd = Command::new("ctest") + .version("1.0") + .subcommand(Command::new("foo").help_heading("Custom Heading")) + .term_width(0); + + let expected = str![[r#" +error: unrecognized subcommand 'bar' + +Usage: ctest [COMMAND] + +For more information, try '--help'. + +"#]]; + utils::assert_output(cmd, "ctest bar", expected, true); +} + +#[test] +fn try_help_subcommand_custom_flag() { + let cmd = Command::new("ctest") + .version("1.0") + .disable_help_flag(true) + .arg( + Arg::new("help") + .long("help") + .short('h') + .action(ArgAction::Help) + .global(true), + ) + .subcommand(Command::new("foo").help_heading("Custom Heading")) + .term_width(0); + + let expected = str![[r#" +error: unrecognized subcommand 'bar' + +Usage: ctest [COMMAND] + +For more information, try '--help'. + +"#]]; + utils::assert_output(cmd, "ctest bar", expected, true); +} + +#[test] +fn try_help_subcommand_custom_flag_no_action() { + let cmd = Command::new("ctest") + .version("1.0") + .disable_help_flag(true) + // Note `ArgAction::Help` is excluded + .arg(Arg::new("help").long("help").global(true)) + .subcommand(Command::new("foo").help_heading("Custom Heading")) + .term_width(0); + + let expected = str![[r#" +error: unrecognized subcommand 'bar' + +Usage: ctest [COMMAND] + +For more information, try 'help'. + +"#]]; + utils::assert_output(cmd, "ctest bar", expected, true); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn wrapped_help() { + let cmd = Command::new("test") + .term_width(67) + .arg( + Arg::new("all") + .short('a') + .long("all") + .action(ArgAction::SetTrue) + .help("Also do versioning for private crates (will not be published)"), + ) + .arg( + Arg::new("exact") + .long("exact") + .action(ArgAction::SetTrue) + .help("Specify inter dependency version numbers exactly with `=`"), + ) + .arg( + Arg::new("no_git_commit") + .long("no-git-commit") + .action(ArgAction::SetTrue) + .help("Do not commit version changes"), + ) + .arg( + Arg::new("no_git_push") + .long("no-git-push") + .action(ArgAction::SetTrue) + .help("Do not push generated commit and tags to git remote"), + ) + .subcommand( + Command::new("sub1") + .help_heading("Custom Heading") + .about("One two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen") + ); + + let expected = str![[r#" +Usage: test [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + sub1 One two three four five six seven eight nine ten eleven + twelve thirteen fourteen fifteen + +Options: + -a, --all Also do versioning for private crates (will + not be published) + --exact Specify inter dependency version numbers + exactly with `=` + --no-git-commit Do not commit version changes + --no-git-push Do not push generated commit and tags to git + remote + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "test --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn unwrapped_help() { + let cmd = Command::new("test") + .term_width(68) + .arg( + Arg::new("all") + .short('a') + .long("all") + .action(ArgAction::SetTrue) + .help("Also do versioning for private crates (will not be published)"), + ) + .arg( + Arg::new("exact") + .long("exact") + .action(ArgAction::SetTrue) + .help("Specify inter dependency version numbers exactly with `=`"), + ) + .arg( + Arg::new("no_git_commit") + .long("no-git-commit") + .action(ArgAction::SetTrue) + .help("Do not commit version changes"), + ) + .arg( + Arg::new("no_git_push") + .long("no-git-push") + .action(ArgAction::SetTrue) + .help("Do not push generated commit and tags to git remote"), + ) + .subcommand( + Command::new("sub1") + .help_heading("Custom Heading") + .about("One two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen") + ); + + let expected = str![[r#" +Usage: test [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + sub1 One two three four five six seven eight nine ten eleven + twelve thirteen fourteen fifteen + +Options: + -a, --all Also do versioning for private crates (will + not be published) + --exact Specify inter dependency version numbers + exactly with `=` + --no-git-commit Do not commit version changes + --no-git-push Do not push generated commit and tags to git + remote + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "test --help", expected, false); +} + +#[test] +fn complex_subcommand_help_output() { + let a = utils::complex_app_with_help_heading(); + + let expected = str![[r#" +clap-test-subcmd 0.1 +Kevin K. +tests subcommands + +Usage: clap-test subcmd [OPTIONS] [scpositional] + +Arguments: + [scpositional] tests positionals + +Options: + -o, --option ... tests options + -f, --flag... tests flags + -s, --subcmdarg tests other args + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(a, "clap-test subcmd --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[cfg(feature = "wrap_help")] +fn setup_aliases_with_help_heading() -> Command { + Command::new("ctest") + .version("0.1") + .arg( + Arg::new("dest") + .short('d') + .long("destination") + .value_name("FILE") + .help("File to save into") + .long_help("The Filepath to save into the result") + .short_alias('q') + .short_aliases(['w', 'e']) + .alias("arg-alias") + .aliases(["do-stuff", "do-tests"]) + .visible_short_alias('t') + .visible_short_aliases(['i', 'o']) + .visible_alias("file") + .visible_aliases(["into", "to"]) + .action(ArgAction::Set), + ) + .subcommand( + Command::new("rev") + .help_heading("Custom Heading") + .short_flag('r') + .long_flag("inplace") + .about("In place") + .long_about("Change mode to work in place on source") + .alias("subc-alias") + .aliases(["subc-do-stuff", "subc-do-tests"]) + .short_flag_alias('j') + .short_flag_aliases(['k', 'l']) + .long_flag_alias("subc-long-flag-alias") + .long_flag_aliases(["subc-long-do-stuff", "subc-long-do-tests"]) + .visible_alias("source") + .visible_aliases(["from", "onsource"]) + .visible_short_flag_alias('s') + .visible_short_flag_aliases(['f', 'g']) + .visible_long_flag_alias("origin") + .visible_long_flag_aliases(["path", "tryfrom"]) + .arg( + Arg::new("input") + .value_name("INPUT") + .help("The source file"), + ), + ) +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn visible_aliases_with_short_help() { + let app = setup_aliases_with_help_heading().term_width(80); + + let expected = str![[r#" +Usage: ctest [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + rev, -r, --inplace In place [aliases: -s, -f, -g, --origin, --path, + --tryfrom, source, from, onsource] + +Options: + -d, --destination File to save into [aliases: -t, -i, -o, --file, + --into, --to] + -h, --help Print help (see more with '--help') + -V, --version Print version + +"#]]; + utils::assert_output(app, "ctest -h", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn visible_aliases_with_long_help() { + let app = setup_aliases_with_help_heading().term_width(80); + + let expected = str![[r#" +Usage: ctest [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + rev, -r, --inplace In place [aliases: -s, -f, -g, --origin, --path, + --tryfrom, source, from, onsource] + +Options: + -d, --destination + The Filepath to save into the result + + [aliases: -t, -i, -o, --file, --into, --to] + + -h, --help + Print help (see a summary with '-h') + + -V, --version + Print version + +"#]]; + utils::assert_output(app, "ctest --help", expected, false); +} + +#[test] +#[cfg(feature = "wrap_help")] +fn dont_wrap_urls() { + let cmd = Command::new("Example") + .term_width(30) + .subcommand(Command::new("update") + .help_heading("Custom Heading") + .arg( + Arg::new("force-non-host") + .help("Install toolchains that require an emulator. See https://github.com/rust-lang/rustup/wiki/Non-host-toolchains") + .long("force-non-host") + .action(ArgAction::SetTrue)) + ); + + let expected = str![[r#" +Usage: Example update [OPTIONS] + +Options: + --force-non-host + Install toolchains + that require an + emulator. See + https://github.com/rust-lang/rustup/wiki/Non-host-toolchains + -h, --help + Print help + +"#]]; + utils::assert_output(cmd, "Example update --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn sc_negates_reqs() { + let cmd = Command::new("prog") + .version("1.0") + .subcommand_negates_reqs(true) + .arg(arg!(-o --opt "tests options").required(true)) + .arg(Arg::new("PATH").help("help")) + .subcommand(Command::new("test").help_heading("Custom Heading")); + + let expected = str![[r#" +Usage: prog --opt [PATH] + prog [PATH] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + test + +Arguments: + [PATH] help + +Options: + -o, --opt tests options + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "prog --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn args_negate_sc() { + let cmd = Command::new("prog") + .version("1.0") + .args_conflicts_with_subcommands(true) + .arg(arg!(-f --flag "testing flags")) + .arg(arg!(-o --opt "tests options")) + .arg(Arg::new("PATH").help("help")) + .subcommand(Command::new("test").help_heading("Custom Heading")); + + let expected = str![[r#" +Usage: prog [OPTIONS] [PATH] + prog + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + test + +Arguments: + [PATH] help + +Options: + -f, --flag testing flags + -o, --opt tests options + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "prog --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn dont_strip_padding_issue_5083() { + let cmd = Command::new("test") + .help_template("{subcommands}") + .subcommands([ + Command::new("one"), + Command::new("two").help_heading("Custom Heading"), + Command::new("three"), + ]); + + let expected = str![[r#" + one + three + help Print this message or the help of the given subcommand(s) + +Custom Heading: + two + +"#]]; + utils::assert_output(cmd, "test --help", expected, false); +} + + +// Updated to include the custom heading in the expected output +#[test] +fn last_arg_mult_usage_req_with_sc() { + let cmd = Command::new("last") + .version("0.1") + .subcommand_negates_reqs(true) + .arg(Arg::new("TARGET").required(true).help("some")) + .arg(Arg::new("CORPUS").help("some")) + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .num_args(1..) + .last(true) + .required(true) + .help("some"), + ) + .subcommand(Command::new("test").help_heading("Custom Heading").about("some")); + + let expected = str![[r#" +Usage: last [CORPUS] -- ... + last [TARGET] [CORPUS] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + test some + +Arguments: + some + [CORPUS] some + ... some + +Options: + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "last --help", expected, false); +} + +#[test] +fn last_arg_mult_usage_with_sc() { + let cmd = Command::new("last") + .version("0.1") + .args_conflicts_with_subcommands(true) + .arg(Arg::new("TARGET").required(true).help("some")) + .arg(Arg::new("CORPUS").help("some")) + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .num_args(1..) + .last(true) + .help("some"), + ) + .subcommand(Command::new("test").help_heading("Custom Heading").about("some")); + + let expected = str![[r#" +Usage: last [CORPUS] [-- ...] + last + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + test some + +Arguments: + some + [CORPUS] some + [ARGS]... some + +Options: + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "last --help", expected, false); +} + +fn issue_1112_setup_help_heading() -> Command { + Command::new("test") + .version("1.3") + .disable_help_flag(true) + .arg( + Arg::new("help1") + .long("help") + .short('h') + .help("some help") + .action(ArgAction::SetTrue), + ) + .subcommand( + Command::new("foo").arg( + Arg::new("help1") + .help_heading("Custom Heading") + .long("help") + .short('h') + .help("some help") + .action(ArgAction::SetTrue), + ), + ) +} + +#[test] +fn prefer_user_help_long_1112() { + let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "--help"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert!(*m.get_one::("help1").expect("defaulted by clap")); +} + +#[test] +fn prefer_user_help_short_1112() { + let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "-h"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert!(*m.get_one::("help1").expect("defaulted by clap")); +} + +#[test] +fn prefer_user_subcmd_help_long_1112() { + let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "foo", "--help"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert!(*m + .subcommand_matches("foo") + .unwrap() + .get_one::("help1") + .expect("defaulted by clap")); +} + +#[test] +fn prefer_user_subcmd_help_short_1112() { + let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "foo", "-h"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert!(m + .subcommand_matches("foo") + .unwrap() + .get_one::("help1") + .expect("defaulted by clap")); +} + + +#[test] +#[should_panic = "List of such arguments: delete"] +fn help_required_globally() { + Command::new("myapp") + .help_expected(true) + .arg(Arg::new("foo").help("It does foo stuff")) + .subcommand( + Command::new("bar").help_heading("Custom Heading") + .arg(Arg::new("create").help("creates bar")) + .arg(Arg::new("delete")), + ) + .try_get_matches_from(empty_args()) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Command::help_expected is enabled for the Command"] +fn help_required_globally_but_not_given_for_subcommand() { + Command::new("myapp") + .help_expected(true) + .arg(Arg::new("foo").help("It does foo stuff")) + .subcommand( + Command::new("bar").help_heading("Custom Heading") + .arg(Arg::new("create").help("creates bar")) + .arg(Arg::new("delete")), + ) + .try_get_matches_from(empty_args()) + .unwrap(); +} + +#[test] +fn help_required_and_given_for_subcommand() { + Command::new("myapp") + .help_expected(true) + .arg(Arg::new("foo").help("It does foo stuff")) + .subcommand( + Command::new("bar").help_heading("Custom Heading") + .arg(Arg::new("create").help("creates bar")) + .arg(Arg::new("delete").help("deletes bar")), + ) + .try_get_matches_from(empty_args()) + .unwrap(); +} + +#[test] +fn help_subcmd_help() { + let cmd = Command::new("myapp") + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand(Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Print this message or the help of the given subcommand(s) + +Usage: myapp help [COMMAND]... + +Arguments: + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd.clone(), "myapp help help", expected, false); +} + +#[test] +fn subcmd_help_subcmd_help() { + let cmd = Command::new("myapp") + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand( + Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Print this message or the help of the given subcommand(s) + +Usage: myapp subcmd help [COMMAND]... + +Arguments: + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd.clone(), "myapp subcmd help help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn global_args_should_show_on_toplevel_help_message() { + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand(Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Usage: myapp [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +First Heading: + subcmd + +Options: + -g, --some-global + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "myapp help", expected, false); +} + +#[test] +fn global_args_should_not_show_on_help_message_for_help_help() { + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand(Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Print this message or the help of the given subcommand(s) + +Usage: myapp help [COMMAND]... + +Arguments: + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "myapp help help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn global_args_should_show_on_help_message_for_subcommand() { + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand(Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Usage: myapp subcmd [OPTIONS] [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Second Heading: + multi + +Options: + -g, --some-global + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "myapp help subcmd", expected, false); +} + +#[test] +fn global_args_should_show_on_help_message_for_nested_subcommand() { + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd") + .help_heading("First Heading") + .subcommand(Command::new("multi") + .help_heading("Second Heading") + .version("1.0"))); + + let expected = str![[r#" +Usage: myapp subcmd multi [OPTIONS] + +Options: + -g, --some-global + -h, --help Print help + -V, --version Print version + +"#]]; + utils::assert_output(cmd, "myapp help subcmd multi", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +fn prefer_about_over_long_about_in_subcommands_list() { + let cmd = Command::new("about-in-subcommands-list").subcommand( + Command::new("sub") + .help_heading("Custom Heading") + .long_about("long about sub") + .about("short about sub"), + ); + + let expected = str![[r#" +Usage: about-in-subcommands-list [COMMAND] + +Commands: + help Print this message or the help of the given subcommand(s) + +Custom Heading: + sub short about sub + +Options: + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "about-in-subcommands-list --help", expected, false); +} + +#[test] +fn disabled_help_flag() { + let res = Command::new("foo") + .subcommand(Command::new("sub") + .help_heading("Custom Heading")) + .disable_help_flag(true) + .try_get_matches_from("foo a".split(' ')); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidSubcommand); +} + +#[test] +fn disabled_help_flag_and_subcommand() { + let res = Command::new("foo") + .subcommand(Command::new("sub") + .help_heading("Custom Heading")) + .disable_help_flag(true) + .disable_help_subcommand(true) + .try_get_matches_from("foo help".split(' ')); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidSubcommand); + assert!( + err.to_string().ends_with('\n'), + "Errors should have a trailing newline, got {:?}", + err.to_string() + ); +} + +#[test] +fn override_help_subcommand() { + let cmd = Command::new("bar") + .subcommand(Command::new("help") + .help_heading("Help Heading") + .arg(Arg::new("arg").action(ArgAction::Set))) + .subcommand(Command::new("not_help") + .help_heading("Not Help") + .arg(Arg::new("arg").action(ArgAction::Set))) + .disable_help_subcommand(true); + let matches = cmd.try_get_matches_from(["bar", "help", "foo"]).unwrap(); + assert_eq!( + matches + .subcommand_matches("help") + .unwrap() + .get_one::("arg") + .map(|v| v.as_str()), + Some("foo") + ); +} + +#[test] +fn override_help_flag_using_long() { + let cmd = Command::new("foo") + .subcommand(Command::new("help") + .help_heading("Custom Heading") + .long_flag("help")) + .disable_help_flag(true) + .disable_help_subcommand(true); + let matches = cmd.try_get_matches_from(["foo", "--help"]).unwrap(); + assert!(matches.subcommand_matches("help").is_some()); +} + +#[test] +fn override_help_flag_using_short() { + let cmd = Command::new("foo") + .disable_help_flag(true) + .disable_help_subcommand(true) + .subcommand(Command::new("help") + .help_heading("Custom Heading") + .short_flag('h')); + let matches = cmd.try_get_matches_from(["foo", "-h"]).unwrap(); + assert!(matches.subcommand_matches("help").is_some()); +} + +#[test] +fn subcommand_help_doesnt_have_useless_help_flag() { + // The main care-about is that the docs and behavior match. Since the `help` subcommand + // currently ignores the `--help` flag, the output shouldn't have it. + let cmd = Command::new("example") + .subcommand(Command::new("test") + .help_heading("Custom Heading") + .about("Subcommand")); + + let expected = str![[r#" +Print this message or the help of the given subcommand(s) + +Usage: example help [COMMAND]... + +Arguments: + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "example help help", expected, false); +} + +#[test] +fn disable_help_flag_affects_help_subcommand() { + let mut cmd = Command::new("test_app") + .disable_help_flag(true) + .subcommand(Command::new("test") + .help_heading("Custom Heading") + .about("Subcommand")); + cmd.build(); + + let args = cmd + .find_subcommand("help") + .unwrap() + .get_arguments() + .map(|a| a.get_id().as_str()) + .collect::>(); + assert!( + !args.contains(&"help"), + "`help` should not be present: {args:?}" + ); +} + +#[test] +fn dont_propagate_version_to_help_subcommand() { + let cmd = Command::new("example") + .version("1.0") + .propagate_version(true) + .subcommand(Command::new("subcommand") + .help_heading("Custom Heading")); + + let expected = str![[r#" +Print this message or the help of the given subcommand(s) + +Usage: example help [COMMAND]... + +Arguments: + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd.clone(), "example help help", expected, false); + + cmd.debug_assert(); +} + + +#[test] +fn parent_cmd_req_in_usage_with_help_flag() { + let cmd = Command::new("parent") + .version("0.1") + .arg(Arg::new("TARGET").required(true).help("some")) + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .required(true) + .help("some"), + ) + .subcommand(Command::new("test") + .help_heading("Custom Heading") + .about("some")); + + let expected = str![[r#" +some + +Usage: parent test + +Options: + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "parent test --help", expected, false); +} + +#[test] +fn parent_cmd_req_in_usage_with_help_subcommand() { + let cmd = Command::new("parent") + .version("0.1") + .arg(Arg::new("TARGET").required(true).help("some")) + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .required(true) + .help("some"), + ) + .subcommand(Command::new("test") + .help_heading("Custom Heading") + .about("some")); + + let expected = str![[r#" +some + +Usage: parent test + +Options: + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "parent help test", expected, false); +} + +#[test] +fn parent_cmd_req_in_usage_with_render_help() { + let mut cmd = Command::new("parent") + .version("0.1") + .arg(Arg::new("TARGET").required(true).help("some")) + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .required(true) + .help("some"), + ) + .subcommand(Command::new("test") + .help_heading("Custom Heading") + .about("some")); + cmd.build(); + let subcmd = cmd.find_subcommand_mut("test").unwrap(); + + let help = subcmd.render_help().to_string(); + assert_data_eq!( + help, + str![[r#" +some + +Usage: parent test + +Options: + -h, --help Print help + +"#]] + ); +} + +#[test] +fn parent_cmd_req_ignored_when_negates_reqs() { + let cmd = Command::new("ctest") + .arg(arg!()) + .subcommand_negates_reqs(true) + .subcommand(Command::new("subcmd") + .help_heading("Custom Heading")); + + let expected = str![[r#" +Usage: ctest subcmd + +Options: + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "ctest subcmd --help", expected, false); +} + +#[test] +fn parent_cmd_req_ignored_when_conflicts() { + let cmd = Command::new("ctest") + .arg(arg!()) + .args_conflicts_with_subcommands(true) + .subcommand(Command::new("subcmd") + .help_heading("Custom Heading")); + + let expected = str![[r#" +Usage: ctest subcmd + +Options: + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "ctest subcmd --help", expected, false); +} + +#[test] +fn display_name_subcommand_default() { + let mut cmd = Command::new("parent") + .subcommand(Command::new("child") + .help_heading("Custom Heading") + .bin_name("child.exe")); + cmd.build(); + assert_eq!( + cmd.find_subcommand("child").unwrap().get_display_name(), + Some("parent-child") + ); +} + +#[test] +fn display_name_subcommand_explicit() { + let mut cmd = Command::new("parent").subcommand( + Command::new("child") + .help_heading("Custom Heading") + .bin_name("child.exe") + .display_name("child.display"), + ); + cmd.build(); + assert_eq!( + cmd.find_subcommand("child").unwrap().get_display_name(), + Some("child.display") + ); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_basic() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent test: +test command + --child + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_short_help() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg( + Arg::new("parent") + .long("parent") + .help("foo") + .long_help("bar"), + ) + .subcommand( + Command::new("test") + .about("test command") + .help_heading("Custom Heading") + .long_about("long some") + .arg(Arg::new("child").long("child").help("foo").long_help("bar")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent foo + -h, --help Print help (see more with '--help') + +parent test: +test command + --child foo + -h, --help Print help (see more with '--help') + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_long_help() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg( + Arg::new("parent") + .long("parent") + .help("foo") + .long_help("bar"), + ) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .long_about("long some") + .arg(Arg::new("child").long("child").help("foo").long_help("bar")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + bar + + -h, --help + Print help (see a summary with '-h') + +parent test: +test command + --child + bar + + -h, --help + Print help (see a summary with '-h') + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... + Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent --help", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_help_cmd() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg( + Arg::new("parent") + .long("parent") + .help("foo") + .long_help("bar"), + ) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .long_about("long some") + .arg(Arg::new("child").long("child").help("foo").long_help("bar")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + bar + + -h, --help + Print help (see a summary with '-h') + +parent test: +test command + --child + bar + + -h, --help + Print help (see a summary with '-h') + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... + Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent help", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_with_global() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent").global(true)) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent test: +test command + --child + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_arg_required() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent").required(true)) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child").required(true)), + ); + + let expected = str![[r#" +parent command + +Usage: parent --parent + parent --parent test --child + parent --parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent --parent test: +test command + --child + -h, --help Print help + +parent --parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_with_external_subcommand() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .allow_external_subcommands(true) + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent test: +test command + --child + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_with_subcommand_required() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .subcommand_required(true) + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child")), + ); + + let expected = str![[r#" +parent command + +Usage: parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent test: +test command + --child + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_with_args_conflicts_with_subcommands() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .subcommand_required(true) + .args_conflicts_with_subcommands(true) + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("test") + .help_heading("Custom Heading") + .about("test command") + .arg(Arg::new("child").long("child")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent test [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent test: +test command + --child + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +#[test] +fn flatten_single_hidden_command() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("child1") + .help_heading("Custom Heading") + .hide(true) + .about("child1 command") + .arg(Arg::new("child").long("child1")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + +Options: + --parent + -h, --help Print help + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_hidden_command() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("child1") + .help_heading("Custom Heading") + .about("child1 command") + .arg(Arg::new("child").long("child1")), + ) + .subcommand( + Command::new("child2") + .help_heading("Custom Heading") + .about("child2 command") + .arg(Arg::new("child").long("child2")), + ) + .subcommand( + Command::new("child3") + .help_heading("Custom Heading") + .hide(true) + .about("child3 command") + .arg(Arg::new("child").long("child3")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent child1 [OPTIONS] + parent child2 [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent child1: +child1 command + --child1 + -h, --help Print help + +parent child2: +child2 command + --child2 + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_recursive() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("child1") + .flatten_help(true) + .help_heading("Custom Heading") + .about("child1 command") + .arg(Arg::new("child").long("child1")) + .subcommand( + Command::new("grandchild1") + .flatten_help(true) + .help_heading("Custom Heading") + .about("grandchild1 command") + .arg(Arg::new("grandchild").long("grandchild1")) + .subcommand( + Command::new("greatgrandchild1") + .help_heading("Custom Heading") + .about("greatgrandchild1 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild1")), + ) + .subcommand( + Command::new("greatgrandchild2") + .help_heading("Custom Heading") + .about("greatgrandchild2 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild2")), + ) + .subcommand( + Command::new("greatgrandchild3") + .help_heading("Custom Heading") + .about("greatgrandchild3 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild3")), + ), + ) + .subcommand( + Command::new("grandchild2") + .help_heading("Custom Heading") + .about("grandchild2 command") + .arg(Arg::new("grandchild").long("grandchild2")), + ) + .subcommand( + Command::new("grandchild3") + .help_heading("Custom Heading") + .about("grandchild3 command") + .arg(Arg::new("grandchild").long("grandchild3")), + ), + ) + .subcommand( + Command::new("child2") + .help_heading("Custom Heading") + .about("child2 command") + .arg(Arg::new("child").long("child2")), + ) + .subcommand( + Command::new("child3") + .hide(true) + .help_heading("Custom Heading") + .about("child3 command") + .arg(Arg::new("child").long("child3")) + .subcommand( + Command::new("grandchild1") + .flatten_help(true) + .help_heading("Custom Heading") + .about("grandchild1 command") + .arg(Arg::new("grandchild").long("grandchild1")) + .subcommand( + Command::new("greatgrandchild1") + .help_heading("Custom Heading") + .about("greatgrandchild1 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild1")), + ) + .subcommand( + Command::new("greatgrandchild2") + .help_heading("Custom Heading") + .about("greatgrandchild2 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild2")), + ) + .subcommand( + Command::new("greatgrandchild3") + .help_heading("Custom Heading") + .about("greatgrandchild3 command") + .arg(Arg::new("greatgrandchild").long("greatgrandchild3")), + ), + ) + .subcommand( + Command::new("grandchild2") + .help_heading("Custom Heading") + .about("grandchild2 command") + .arg(Arg::new("grandchild").long("grandchild2")), + ) + .subcommand( + Command::new("grandchild3") + .help_heading("Custom Heading") + .about("grandchild3 command") + .arg(Arg::new("grandchild").long("grandchild3")), + ), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent child1 [OPTIONS] + parent child1 grandchild1 [OPTIONS] + parent child1 grandchild1 greatgrandchild1 [OPTIONS] + parent child1 grandchild1 greatgrandchild2 [OPTIONS] + parent child1 grandchild1 greatgrandchild3 [OPTIONS] + parent child1 grandchild1 help [COMMAND] + parent child1 grandchild2 [OPTIONS] + parent child1 grandchild3 [OPTIONS] + parent child1 help [COMMAND] + parent child2 [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent child1: +child1 command + --child1 + -h, --help Print help + +parent child1 grandchild1: +grandchild1 command + --grandchild1 + -h, --help Print help + +parent child1 grandchild1 greatgrandchild1: +greatgrandchild1 command + --greatgrandchild1 + -h, --help Print help + +parent child1 grandchild1 greatgrandchild2: +greatgrandchild2 command + --greatgrandchild2 + -h, --help Print help + +parent child1 grandchild1 greatgrandchild3: +greatgrandchild3 command + --greatgrandchild3 + -h, --help Print help + +parent child1 grandchild1 help: +Print this message or the help of the given subcommand(s) + +parent child1 grandchild2: +grandchild2 command + --grandchild2 + -h, --help Print help + +parent child1 grandchild3: +grandchild3 command + --grandchild3 + -h, --help Print help + +parent child1 help: +Print this message or the help of the given subcommand(s) + +parent child2: +child2 command + --child2 + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Parent Help - should it be the first (as normally under Command +// and not custom heading) or should it be the last as is expected here? +#[test] +fn flatten_not_recursive() { + let cmd = Command::new("parent") + .flatten_help(true) + .about("parent command") + .arg(Arg::new("parent").long("parent")) + .subcommand( + Command::new("child1") + .help_heading("Custom Heading") + .about("child1 command") + .arg(Arg::new("child").long("child1")) + .subcommand( + Command::new("grandchild1") + .help_heading("Custom Heading") + .about("grandchild1 command") + .arg(Arg::new("grandchild").long("grandchild1")), + ) + .subcommand( + Command::new("grandchild2") + .help_heading("Custom Heading") + .about("grandchild2 command") + .arg(Arg::new("grandchild").long("grandchild2")), + ) + .subcommand( + Command::new("grandchild3") + .help_heading("Custom Heading") + .about("grandchild3 command") + .arg(Arg::new("grandchild").long("grandchild3")), + ), + ) + .subcommand( + Command::new("child2") + .help_heading("Custom Heading") + .about("child2 command") + .arg(Arg::new("child").long("child2")), + ) + .subcommand( + Command::new("child3") + .help_heading("Custom Heading") + .about("child3 command") + .arg(Arg::new("child").long("child3")), + ); + + let expected = str![[r#" +parent command + +Usage: parent [OPTIONS] + parent child1 [OPTIONS] [COMMAND] + parent child2 [OPTIONS] + parent child3 [OPTIONS] + parent help [COMMAND]... + +Options: + --parent + -h, --help Print help + +parent child1: +child1 command + --child1 + -h, --help Print help + +parent child2: +child2 command + --child2 + -h, --help Print help + +parent child3: +child3 command + --child3 + -h, --help Print help + +parent help: +Print this message or the help of the given subcommand(s) + [COMMAND]... Print help for the subcommand(s) + +"#]]; + utils::assert_output(cmd, "parent -h", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn next_line_command_short() { + let value_name = "V"; + let text = "Hello"; + + let cmd = Command::new("test") + .term_width(120) + .next_line_help(true) + .args([ + Arg::new("default") + .long("default") + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_false") + .long("next_line_help_false") + .next_line_help(false) + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_true") + .long("next_line_help_true") + .next_line_help(true) + .value_name(value_name) + .help(text) + .long_help(text), + ]) + .subcommands([ + Command::new("default").about(text).long_about(text), + Command::new("next_line_help_false") + .help_heading("Custom Heading") + .next_line_help(false) + .about(text) + .long_about(text), + Command::new("next_line_help_true") + .help_heading("Custom Heading") + .next_line_help(true) + .about(text) + .long_about(text), + ]); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Hello + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Hello + next_line_help_true + Hello + +Options: + --default + Hello + --next_line_help_false + Hello + --next_line_help_true + Hello + -h, --help + Print help (see more with '--help') + +"#]]; + utils::assert_output(cmd.clone(), "myprog -h", expected, false); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Hello + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Hello + next_line_help_true + Hello + +Options: + --default + Hello + + --next_line_help_false + Hello + + --next_line_help_true + Hello + + -h, --help + Print help (see a summary with '-h') + +"#]]; + utils::assert_output(cmd, "myprog --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn next_line_arg_short() { + let value_name = "V"; + let text = "Hello"; + + let cmd = Command::new("test") + .term_width(120) + .next_line_help(true) + .args([ + Arg::new("default") + .long("default") + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_false") + .long("next_line_help_false") + .next_line_help(false) + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_true") + .long("next_line_help_true") + .next_line_help(true) + .value_name(value_name) + .help(text) + .long_help(text), + ]) + .subcommands([ + Command::new("default").about(text).long_about(text), + Command::new("next_line_help_false") + .help_heading("Custom Heading") + .next_line_help(false) + .about(text) + .long_about(text), + Command::new("next_line_help_true") + .help_heading("Custom Heading") + .next_line_help(true) + .about(text) + .long_about(text), + ]); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Hello + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Hello + next_line_help_true + Hello + +Options: + --default + Hello + --next_line_help_false + Hello + --next_line_help_true + Hello + -h, --help + Print help (see more with '--help') + +"#]]; + utils::assert_output(cmd.clone(), "myprog -h", expected, false); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Hello + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Hello + next_line_help_true + Hello + +Options: + --default + Hello + + --next_line_help_false + Hello + + --next_line_help_true + Hello + + -h, --help + Print help (see a summary with '-h') + +"#]]; + utils::assert_output(cmd, "myprog --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn next_line_command_wrapped() { + let value_name = "SOME_LONG_VALUE"; + let text = "Also do versioning for private crates (will not be published) + +Specify inter dependency version numbers exactly with `=` + +Do not commit version changes + +Do not push generated commit and tags to git remote +"; + + let cmd = Command::new("test") + .term_width(67) + .next_line_help(true) + .args([ + Arg::new("default") + .long("default") + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_false") + .long("next_line_help_false") + .next_line_help(false) + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_true") + .long("next_line_help_true") + .next_line_help(true) + .value_name(value_name) + .help(text) + .long_help(text), + ]) + .subcommands([ + Command::new("default").about(text).long_about(text), + Command::new("next_line_help_false") + .help_heading("Custom Heading") + .next_line_help(false) + .about(text) + .long_about(text), + Command::new("next_line_help_true") + .help_heading("Custom Heading") + .next_line_help(true) + .about(text) + .long_about(text), + ]); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + +Options: + --default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + --next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + --next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + -h, --help + Print help (see more with '--help') + +"#]]; + utils::assert_output(cmd.clone(), "myprog -h", expected, false); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + +Options: + --default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + --next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + --next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + -h, --help + Print help (see a summary with '-h') + +"#]]; + utils::assert_output(cmd, "myprog --help", expected, false); +} + +// Updated to include the custom heading in the expected output +#[test] +#[cfg(feature = "wrap_help")] +fn next_line_arg_wrapped() { + let value_name = "SOME_LONG_VALUE"; + let text = "Also do versioning for private crates (will not be published) + +Specify inter dependency version numbers exactly with `=` + +Do not commit version changes + +Do not push generated commit and tags to git remote +"; + + let cmd = Command::new("test") + .term_width(67) + .next_line_help(true) + .args([ + Arg::new("default") + .long("default") + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_false") + .long("next_line_help_false") + .next_line_help(false) + .value_name(value_name) + .help(text) + .long_help(text), + Arg::new("next_line_help_true") + .long("next_line_help_true") + .next_line_help(true) + .value_name(value_name) + .help(text) + .long_help(text), + ]) + .subcommands([ + Command::new("default").about(text).long_about(text), + Command::new("next_line_help_false") + .help_heading("Custom Heading") + .next_line_help(false) + .about(text) + .long_about(text), + Command::new("next_line_help_true") + .help_heading("Custom Heading") + .next_line_help(true) + .about(text) + .long_about(text), + ]); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + +Options: + --default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + --next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + --next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + -h, --help + Print help (see more with '--help') + +"#]]; + utils::assert_output(cmd.clone(), "myprog -h", expected, false); + + let expected = str![[r#" +Usage: myprog [OPTIONS] [COMMAND] + +Commands: + default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + help + Print this message or the help of the given subcommand(s) + +Custom Heading: + next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + +Options: + --default + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + --next_line_help_false + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + --next_line_help_true + Also do versioning for private crates (will not be + published) + + Specify inter dependency version numbers exactly with `=` + + Do not commit version changes + + Do not push generated commit and tags to git remote + + -h, --help + Print help (see a summary with '-h') + +"#]]; + utils::assert_output(cmd, "myprog --help", expected, false); +} + +} \ No newline at end of file diff --git a/tests/builder/utils.rs b/tests/builder/utils.rs index 05d95b4e51c..2e5ced5ae05 100644 --- a/tests/builder/utils.rs +++ b/tests/builder/utils.rs @@ -114,3 +114,61 @@ pub(crate) fn complex_app() -> Command { .arg(arg!([scpositional] "tests positionals")), ) } + +pub(crate) fn complex_app_with_help_heading() -> Command { + let opt3_vals = ["fast", "slow"]; + let pos3_vals = ["vi", "emacs"]; + + Command::new("clap-test") + .version("v1.4.8") + .about("tests clap library") + .author("Kevin K. ") + .help_template(FULL_TEMPLATE) + .arg( + arg!( + -o --option "tests options" + ) + .required(false) + .num_args(1..) + .action(ArgAction::Append), + ) + .arg(arg!([positional] "tests positionals")) + .arg( + arg!(-f --flag "tests flags") + .action(ArgAction::Count) + .global(true), + ) + .args([ + arg!(flag2: -F "tests flags with exclusions") + .conflicts_with("flag") + .requires("long-option-2") + .action(ArgAction::SetTrue), + arg!(--"long-option-2" "tests long options with exclusions") + .conflicts_with("option") + .requires("positional2"), + arg!([positional2] "tests positionals with exclusions"), + arg!(-O --option3 "specific vals").value_parser(opt3_vals), + arg!([positional3] ... "tests specific values").value_parser(pos3_vals), + arg!(--multvals "Tests multiple values, not mult occs") + .value_names(["one", "two"]), + arg!(--multvalsmo ... "Tests multiple values, and mult occs") + .value_names(["one", "two"]), + arg!(--minvals2 "Tests 2 min vals").num_args(2..), + arg!(--maxvals3 "Tests 3 max vals").num_args(1..=3), + arg!(--optvaleq "Tests optional value, require = sign") + .num_args(0..=1) + .require_equals(true), + arg!(--optvalnoeq "Tests optional value").num_args(0..=1), + ]) + .subcommand( + Command::new("subcmd") + .about("tests subcommands") + .help_heading("Custom Heading") + .version("0.1") + .author("Kevin K. ") + .help_template(FULL_TEMPLATE) + .arg(arg!(-o --option "tests options").num_args(1..)) + .arg(arg!(-s --subcmdarg "tests other args")) + .arg(arg!([scpositional] "tests positionals")), + ) +} \ No newline at end of file diff --git a/tests/derive/value_enum.rs b/tests/derive/value_enum.rs index e0ed09bc161..3024efd0481 100644 --- a/tests/derive/value_enum.rs +++ b/tests/derive/value_enum.rs @@ -41,16 +41,14 @@ fn basic() { #[test] fn default_value() { #[derive(clap::ValueEnum, PartialEq, Debug, Clone)] + #[derive(Default)] enum ArgChoice { Foo, + #[default] Bar, } - impl Default for ArgChoice { - fn default() -> Self { - Self::Bar - } - } + #[derive(Parser, PartialEq, Debug)] struct Opt { diff --git a/tests/derive_ui.rs b/tests/derive_ui.rs index 84abd3e5304..2df86284186 100644 --- a/tests/derive_ui.rs +++ b/tests/derive_ui.rs @@ -8,7 +8,7 @@ #![cfg(feature = "unstable-derive-ui-tests")] #[cfg(feature = "derive")] -#[rustversion::attr(not(stable(1.90)), ignore)] // STABLE +#[rustversion::attr(not(stable(1.91)), ignore)] // STABLE #[test] fn ui() { let t = trybuild::TestCases::new(); diff --git a/tests/derive_ui/enum_variant_not_args.stderr b/tests/derive_ui/enum_variant_not_args.stderr index 32ea375254a..2b9ea252c80 100644 --- a/tests/derive_ui/enum_variant_not_args.stderr +++ b/tests/derive_ui/enum_variant_not_args.stderr @@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: clap::Args` is not satisfied --> tests/derive_ui/enum_variant_not_args.rs:3:9 | 3 | Sub(SubCmd), - | ^^^^^^ the trait `clap::Args` is not implemented for `SubCmd` + | ^^^^^^ unsatisfied trait bound | +help: the trait `clap::Args` is not implemented for `SubCmd` + --> tests/derive_ui/enum_variant_not_args.rs:7:1 + | +7 | enum SubCmd {} + | ^^^^^^^^^^^ = help: the following other types implement trait `clap::Args`: () Box diff --git a/tests/derive_ui/flatten_enum_in_struct.stderr b/tests/derive_ui/flatten_enum_in_struct.stderr index 4ad37382ee8..e21deedcd72 100644 --- a/tests/derive_ui/flatten_enum_in_struct.stderr +++ b/tests/derive_ui/flatten_enum_in_struct.stderr @@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: clap::Args` is not satisfied --> tests/derive_ui/flatten_enum_in_struct.rs:4:10 | 4 | sub: SubCmd, - | ^^^^^^ the trait `clap::Args` is not implemented for `SubCmd` + | ^^^^^^ unsatisfied trait bound | +help: the trait `clap::Args` is not implemented for `SubCmd` + --> tests/derive_ui/flatten_enum_in_struct.rs:8:1 + | +8 | enum SubCmd {} + | ^^^^^^^^^^^ = help: the following other types implement trait `clap::Args`: () Box diff --git a/tests/derive_ui/flatten_struct_in_enum.stderr b/tests/derive_ui/flatten_struct_in_enum.stderr index ae36513ef31..5ff7d3bb039 100644 --- a/tests/derive_ui/flatten_struct_in_enum.stderr +++ b/tests/derive_ui/flatten_struct_in_enum.stderr @@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: Subcommand` is not satisfied --> tests/derive_ui/flatten_struct_in_enum.rs:4:9 | 4 | Sub(SubCmd), - | ^^^^^^ the trait `Subcommand` is not implemented for `SubCmd` + | ^^^^^^ unsatisfied trait bound | +help: the trait `Subcommand` is not implemented for `SubCmd` + --> tests/derive_ui/flatten_struct_in_enum.rs:8:1 + | +8 | struct SubCmd {} + | ^^^^^^^^^^^^^ = help: the following other types implement trait `Subcommand`: () Box diff --git a/tests/derive_ui/skip_without_default.stderr b/tests/derive_ui/skip_without_default.stderr index bddc122089d..1b0d684fac8 100644 --- a/tests/derive_ui/skip_without_default.stderr +++ b/tests/derive_ui/skip_without_default.stderr @@ -2,4 +2,10 @@ error[E0277]: the trait bound `Kind: Default` is not satisfied --> tests/derive_ui/skip_without_default.rs:22:11 | 22 | #[arg(skip)] - | ^^^^ the trait `Default` is not implemented for `Kind` + | ^^^^ unsatisfied trait bound + | +help: the trait `Default` is not implemented for `Kind` + --> tests/derive_ui/skip_without_default.rs:12:1 + | +12 | enum Kind { + | ^^^^^^^^^ diff --git a/tests/derive_ui/value_parser_unsupported.stderr b/tests/derive_ui/value_parser_unsupported.stderr index 72b128f641f..951bebcb6a7 100644 --- a/tests/derive_ui/value_parser_unsupported.stderr +++ b/tests/derive_ui/value_parser_unsupported.stderr @@ -37,6 +37,7 @@ note: the traits `From`, `FromStr`, `ValueEnum`, and `ValueParserFactory` must | 293 | pub trait ValueEnum: Sized + Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | --> $RUST/core/src/convert/mod.rs --> $RUST/core/src/str/traits.rs = note: this error originates in the macro `clap::value_parser` (in Nightly builds, run with -Z macro-backtrace for more info)