Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into prefix-attr
Browse files Browse the repository at this point in the history
  • Loading branch information
wfraser committed Nov 23, 2022
2 parents de5da9a + f5dcfc5 commit 4ccc123
Show file tree
Hide file tree
Showing 18 changed files with 140 additions and 29 deletions.
37 changes: 36 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 5.0.0 - ReleaseDate

### Breaking Changes

- Made `ArgPredicate` `non_exhaustive`

<!-- next-header -->
## [Unreleased] - ReleaseDate

## [4.0.26] - 2022-11-16

### Fixes

- *(error)* Fix typos in `ContextKind::as_str`

## [4.0.25] - 2022-11-15

### Features

- *(error)* Report available subcommands when required subcommand is missing

## [4.0.24] - 2022-11-14

### Fixes

- Avoid panic when printing an argument that isn't built

## [4.0.23] - 2022-11-11

### Fixes

- Don't panic on reporting invalid-long errors when followed by invalid UTF8
- *(help)* Clarified argument to `help` subcommand

## [4.0.22] - 2022-11-07

### Fixes
Expand Down Expand Up @@ -3991,7 +4022,11 @@ Minimum version of Rust is now v1.13.0 (Stable)
* **arg** allow lifetimes other than 'static in arguments ([9e8c1fb9](https://github.com/clap-rs/clap/commit/9e8c1fb9406f8448873ca58bab07fe905f1551e5))

<!-- next-url -->
[Unreleased]: https://github.com/clap-rs/clap/compare/v4.0.22...HEAD
[Unreleased]: https://github.com/clap-rs/clap/compare/v4.0.26...HEAD
[4.0.26]: https://github.com/clap-rs/clap/compare/v4.0.25...v4.0.26
[4.0.25]: https://github.com/clap-rs/clap/compare/v4.0.24...v4.0.25
[4.0.24]: https://github.com/clap-rs/clap/compare/v4.0.23...v4.0.24
[4.0.23]: https://github.com/clap-rs/clap/compare/v4.0.22...v4.0.23
[4.0.22]: https://github.com/clap-rs/clap/compare/v4.0.21...v4.0.22
[4.0.21]: https://github.com/clap-rs/clap/compare/v4.0.20...v4.0.21
[4.0.20]: https://github.com/clap-rs/clap/compare/v4.0.19...v4.0.20
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [

[package]
name = "clap"
version = "4.0.22"
version = "4.0.26"
description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
repository = "https://github.com/clap-rs/clap"
categories = ["command-line-interface"]
Expand Down
2 changes: 1 addition & 1 deletion clap_complete/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ where
///
/// # Examples
///
/// Assuming a separate `cli.rs` like the [example above](generate_to()),
/// Assuming a separate `cli.rs` like the [`generate_to` example](generate_to()),
/// we can let users generate a completion script using a command:
///
/// ```ignore
Expand Down
2 changes: 1 addition & 1 deletion src/builder/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4183,7 +4183,7 @@ impl Arg {
fn render_arg_val(&self, required: bool) -> String {
let mut rendered = String::new();

let num_vals = self.get_num_args().expect(INTERNAL_ERROR_MSG);
let num_vals = self.get_num_args().unwrap_or_else(|| 1.into());

let mut val_names = if self.val_names.is_empty() {
vec![self.id.as_internal_str().to_owned()]
Expand Down
1 change: 1 addition & 0 deletions src/builder/arg_predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::builder::OsStr;
///
/// These do not apply to [`ValueSource::DefaultValue`][crate::parser::ValueSource::DefaultValue]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "unstable-v5", non_exhaustive)]
pub enum ArgPredicate {
/// Is the argument present?
IsPresent,
Expand Down
4 changes: 3 additions & 1 deletion src/builder/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,7 @@ impl Command {
/// ```
#[inline]
#[must_use]
#[cfg(any(not(feature = "unstable-v5"), feature = "wrap_help"))]
pub fn term_width(mut self, width: usize) -> Self {
self.term_w = Some(width);
self
Expand All @@ -1100,6 +1101,7 @@ impl Command {
/// ```
#[inline]
#[must_use]
#[cfg(any(not(feature = "unstable-v5"), feature = "wrap_help"))]
pub fn max_term_width(mut self, w: usize) -> Self {
self.max_w = Some(w);
self
Expand Down Expand Up @@ -4287,7 +4289,7 @@ impl Command {
.action(ArgAction::Append)
.num_args(..)
.value_name("COMMAND")
.help("The subcommand whose help message to display"),
.help("Print help for the subcommand(s)"),
)
};
self._propagate_subcommand(&mut help_subcmd);
Expand Down
5 changes: 4 additions & 1 deletion src/error/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum ContextKind {
InvalidArg,
/// Existing arguments
PriorArg,
/// Accepted subcommands
ValidSubcommand,
/// Accepted values
ValidValue,
/// Rejected values
Expand Down Expand Up @@ -44,7 +46,8 @@ impl ContextKind {
Self::InvalidSubcommand => Some("Invalid Subcommand"),
Self::InvalidArg => Some("Invalid Argument"),
Self::PriorArg => Some("Prior Argument"),
Self::ValidValue => Some("Value Value"),
Self::ValidSubcommand => Some("Valid Subcommand"),
Self::ValidValue => Some("Valid Value"),
Self::InvalidValue => Some("Invalid Value"),
Self::ActualNumValues => Some("Actual Number of Values"),
Self::ExpectedNumValues => Some("Expected Number of Values"),
Expand Down
18 changes: 18 additions & 0 deletions src/error/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,24 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) ->
styled.none("'");
styled.warning(invalid_sub);
styled.none("' requires a subcommand but one was not provided");

let possible_values = error.get(ContextKind::ValidSubcommand);
if let Some(ContextValue::Strings(possible_values)) = possible_values {
if !possible_values.is_empty() {
styled.none("\n");
styled.none(TAB);
styled.none("[subcommands: ");
if let Some((last, elements)) = possible_values.split_last() {
for v in elements {
styled.good(escape(v));
styled.none(", ");
}
styled.good(escape(last));
}
styled.none("]");
}
}

true
} else {
false
Expand Down
14 changes: 9 additions & 5 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,17 +504,21 @@ impl<F: ErrorFormatter> Error<F> {

pub(crate) fn missing_subcommand(
cmd: &Command,
name: String,
parent: String,
available: Vec<String>,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);

#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([(
ContextKind::InvalidSubcommand,
ContextValue::String(name),
)]);
err = err.extend_context_unchecked([
(ContextKind::InvalidSubcommand, ContextValue::String(parent)),
(
ContextKind::ValidSubcommand,
ContextValue::Strings(available),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,3 @@ mod util;

const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
report at https://github.com/clap-rs/clap/issues";
const INVALID_UTF8: &str = "unexpected invalid UTF-8 code point";
2 changes: 1 addition & 1 deletion src/parser/features/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ where
/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub(crate) fn did_you_mean_flag<'a, 'help, I, T>(
arg: &str,
remaining_args: &[&str],
remaining_args: &[&std::ffi::OsStr],
longs: I,
subcommands: impl IntoIterator<Item = &'a mut Command>,
) -> Option<(String, Option<String>)>
Expand Down
10 changes: 4 additions & 6 deletions src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::parser::{ArgMatcher, SubCommand};
use crate::parser::{Validator, ValueSource};
use crate::util::Id;
use crate::ArgAction;
use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8};
use crate::INTERNAL_ERROR_MSG;

pub(crate) struct Parser<'cmd> {
cmd: &'cmd mut Command,
Expand Down Expand Up @@ -171,10 +171,8 @@ impl<'cmd> Parser<'cmd> {
}
ParseResult::NoMatchingArg { arg } => {
let _ = self.resolve_pending(matcher);
let remaining_args: Vec<_> = raw_args
.remaining(&mut args_cursor)
.map(|x| x.to_str().expect(INVALID_UTF8))
.collect();
let remaining_args: Vec<_> =
raw_args.remaining(&mut args_cursor).collect();
return Err(self.did_you_mean_error(
&arg,
matcher,
Expand Down Expand Up @@ -1523,7 +1521,7 @@ impl<'cmd> Parser<'cmd> {
&mut self,
arg: &str,
matcher: &mut ArgMatcher,
remaining_args: &[&str],
remaining_args: &[&OsStr],
trailing_values: bool,
) -> ClapError {
debug!("Parser::did_you_mean_error: arg={}", arg);
Expand Down
4 changes: 4 additions & 0 deletions src/parser/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ impl<'cmd> Validator<'cmd> {
return Err(Error::missing_subcommand(
self.cmd,
bn.to_string(),
self.cmd
.all_subcommand_names()
.map(|s| s.to_owned())
.collect::<Vec<_>>(),
Usage::new(self.cmd)
.required(&self.required)
.create_usage_with_title(&[]),
Expand Down
18 changes: 18 additions & 0 deletions tests/builder/app_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ fn sub_command_required() {
assert_eq!(err.kind(), ErrorKind::MissingSubcommand);
}

#[test]
#[cfg(feature = "error-context")]
fn sub_command_required_error() {
static ERROR: &str = "\
error: 'sc_required' requires a subcommand but one was not provided
[subcommands: sub1, help]
Usage: sc_required <COMMAND>
For more information try '--help'
";

let cmd = Command::new("sc_required")
.subcommand_required(true)
.subcommand(Command::new("sub1"));
utils::assert_output(cmd, "sc_required", ERROR, true);
}

#[test]
fn arg_required_else_help() {
let result = Command::new("arg_required")
Expand Down
10 changes: 5 additions & 5 deletions tests/builder/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1989,7 +1989,7 @@ Print this message or the help of the given subcommand(s)
Usage: myapp help [COMMAND]...
Arguments:
[COMMAND]... The subcommand whose help message to display
[COMMAND]... Print help for the subcommand(s)
";

let cmd = Command::new("myapp")
Expand All @@ -2006,7 +2006,7 @@ Print this message or the help of the given subcommand(s)
Usage: myapp subcmd help [COMMAND]...
Arguments:
[COMMAND]... The subcommand whose help message to display
[COMMAND]... Print help for the subcommand(s)
";

let cmd = Command::new("myapp")
Expand Down Expand Up @@ -2054,7 +2054,7 @@ Print this message or the help of the given subcommand(s)
Usage: myapp help [COMMAND]...
Arguments:
[COMMAND]... The subcommand whose help message to display
[COMMAND]... Print help for the subcommand(s)
";

let cmd = Command::new("myapp")
Expand Down Expand Up @@ -2561,7 +2561,7 @@ Print this message or the help of the given subcommand(s)
Usage: example help [COMMAND]...
Arguments:
[COMMAND]... The subcommand whose help message to display
[COMMAND]... Print help for the subcommand(s)
",
false,
);
Expand Down Expand Up @@ -2603,7 +2603,7 @@ Print this message or the help of the given subcommand(s)
Usage: example help [COMMAND]...
Arguments:
[COMMAND]... The subcommand whose help message to display
[COMMAND]... Print help for the subcommand(s)
",
false,
);
Expand Down
7 changes: 7 additions & 0 deletions tests/builder/multiple_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1570,3 +1570,10 @@ fn issue_2229() {
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::WrongNumberOfValues);
}

#[test]
#[should_panic = "Argument 'pos` is positional, it must take a value"]
fn disallow_positionals_without_values() {
let cmd = Command::new("test").arg(Arg::new("pos").num_args(0));
cmd.debug_assert();
}
22 changes: 22 additions & 0 deletions tests/builder/utf8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,28 @@ fn invalid_utf8_strict_option_long_equals() {
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_invalid_short() {
let m = Command::new("bad_utf8").try_get_matches_from(vec![
OsString::from(""),
OsString::from("-a"),
OsString::from_vec(vec![0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}

#[test]
fn invalid_utf8_strict_invalid_long() {
let m = Command::new("bad_utf8").try_get_matches_from(vec![
OsString::from(""),
OsString::from("--arg"),
OsString::from_vec(vec![0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}

#[test]
fn invalid_utf8_positional() {
let r = Command::new("bad_utf8")
Expand Down

0 comments on commit 4ccc123

Please sign in to comment.