Skip to content

Commit

Permalink
fix: cmd take option as positional if no option defined
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden committed May 19, 2023
1 parent 08fb4ce commit 3bb33f6
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 22 deletions.
10 changes: 6 additions & 4 deletions src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,12 @@ impl Command {
}
}

pub(crate) fn without_params_or_subcommands(&self) -> bool {
self.flag_option_params.is_empty()
&& self.positional_params.is_empty()
&& self.subcommands.is_empty()
pub(crate) fn no_flags_options_subcommands(&self) -> bool {
self.flag_option_params.is_empty() && self.subcommands.is_empty()
}

pub(crate) fn no_params_subcommands(&self) -> bool {
self.no_flags_options_subcommands() && self.positional_params.is_empty()
}

pub(crate) fn get_cmd_fn(&self, cmd_paths: &[&str]) -> Option<String> {
Expand Down
29 changes: 15 additions & 14 deletions src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct Matcher<'a, 'b> {
cmds: Vec<(&'b str, &'a Command, String)>,
flag_option_args: Vec<Vec<FlagOptionArg<'a, 'b>>>,
positional_args: Vec<&'b str>,
dashdash: Option<usize>,
dashdash: Vec<usize>,
arg_comp: ArgComp,
choices_fns: HashSet<&'a str>,
choices_values: HashMap<&'a str, Vec<String>>,
Expand Down Expand Up @@ -54,7 +54,7 @@ impl<'a, 'b> Matcher<'a, 'b> {
let mut arg_index = 1;
let mut flag_option_args = vec![vec![]];
let mut positional_args = vec![];
let mut dashdash = None;
let mut dashdash = vec![];
let mut arg_comp = ArgComp::Any;
let mut choices_fns = HashSet::new();
let args_len = args.len();
Expand All @@ -68,15 +68,14 @@ impl<'a, 'b> Matcher<'a, 'b> {
while arg_index < args_len {
let cmd = cmds[cmd_level].1;
let arg = args[arg_index].as_str();
if dashdash.is_some()
|| (cmd.without_params_or_subcommands()
&& !["-h", "-help", "--help"].contains(&arg))
if arg == "--" {
dashdash.push(positional_args.len());
} else if !dashdash.is_empty()
|| (cmd.no_flags_options_subcommands() && !["-h", "-help", "--help"].contains(&arg))
{
positional_args.push(arg);
} else if arg.starts_with('-') {
if arg == "--" {
dashdash = Some(positional_args.len());
} else if let Some((k, v)) = arg.split_once('=') {
if let Some((k, v)) = arg.split_once('=') {
let param = cmd.find_flag_option(k);
if arg_index == args_len - 1 {
if let Some(param) = param {
Expand Down Expand Up @@ -183,8 +182,7 @@ impl<'a, 'b> Matcher<'a, 'b> {
return vec![ArgcValue::Error(self.stringify_match_error(&err))];
}
let (cmd, cmd_paths) = self.get_cmd_and_paths(self.cmds.len() - 1);
let mut output = if cmd.without_params_or_subcommands() && !self.positional_args.is_empty()
{
let mut output = if cmd.no_params_subcommands() && !self.positional_args.is_empty() {
vec![ArgcValue::ExtraPositionalMultiple(
self.positional_args.iter().map(|v| v.to_string()).collect(),
)]
Expand All @@ -199,8 +197,11 @@ impl<'a, 'b> Matcher<'a, 'b> {

pub fn to_arg_values_for_choice_fn(&self) -> Vec<ArgcValue> {
let mut output: Vec<ArgcValue> = self.to_arg_values_base();
if let Some(v) = self.dashdash {
output.push(ArgcValue::Single("_dashdash".into(), v.to_string()));
if !self.dashdash.is_empty() {
output.push(ArgcValue::Multiple(
"_dashdash".into(),
self.dashdash.iter().map(|v| v.to_string()).collect(),
));
}
output.push(ArgcValue::Multiple(
"_args".into(),
Expand Down Expand Up @@ -248,7 +249,7 @@ impl<'a, 'b> Matcher<'a, 'b> {
}
ArgComp::Any => {
let cmd = self.cmds[self.cmds.len() - 1].1;
let mut output = if self.dashdash.is_some() {
let mut output = if !self.dashdash.is_empty() {
vec![]
} else {
self.comp_flag_options()
Expand Down Expand Up @@ -479,7 +480,7 @@ impl<'a, 'b> Matcher<'a, 'b> {
while param_index < params_len && arg_index < args_len {
let param = &cmd.positional_params[param_index];
if param.multiple {
let dashdash_idx = self.dashdash.unwrap_or_default();
let dashdash_idx = self.dashdash.first().cloned().unwrap_or_default();
let takes = if param_index == 0
&& dashdash_idx > 0
&& params_len == 2
Expand Down
1 change: 1 addition & 0 deletions tests/snapshots/integration__compgen__compgen.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cmd_option_formats Options all kind of formats
cmd_option_quotes Option value quoted
cmd_option_notations Option with value notations
cmd_flag_formats All kind of flags
cmd_positional Positional all required
cmd_positional_only Positional one required
cmd_positional_many Positional all required
cmd_positional_requires Positional all required
Expand Down
1 change: 1 addition & 0 deletions tests/snapshots/integration__compgen__compgen_help.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cmd_option_formats Options all kind of formats
cmd_option_quotes Option value quoted
cmd_option_notations Option with value notations
cmd_flag_formats All kind of flags
cmd_positional Positional all required
cmd_positional_only Positional one required
cmd_positional_many Positional all required
cmd_positional_requires Positional all required
Expand Down
1 change: 1 addition & 0 deletions tests/snapshots/integration__compgen__compgen_help2.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cmd_option_formats Options all kind of formats
cmd_option_quotes Option value quoted
cmd_option_notations Option with value notations
cmd_flag_formats All kind of flags
cmd_positional Positional all required
cmd_positional_only Positional one required
cmd_positional_many Positional all required
cmd_positional_requires Positional all required
Expand Down
33 changes: 33 additions & 0 deletions tests/snapshots/integration__export__export.snap
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,39 @@ expression: output
"aliases": [],
"subcommands": []
},
{
"describe": "Positional all required",
"name": "cmd_positional",
"author": null,
"version": null,
"options": [],
"positionals": [
{
"name": "name",
"describe": "A optional arg 1",
"choices": null,
"choices_fn": null,
"multiple": false,
"required": false,
"default": null,
"default_fn": null,
"value_name": null
},
{
"name": "values",
"describe": "A optional arg 2, multiple",
"choices": null,
"choices_fn": null,
"multiple": true,
"required": false,
"default": null,
"default_fn": null,
"value_name": null
}
],
"aliases": [],
"subcommands": []
},
{
"describe": "Positional one required",
"name": "cmd_positional_only",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OUTPUT
argc_flag1=1
argc_opt1=4
argc_arg1=( abc )
argc__dashdash=0
argc__dashdash=( 0 )
argc__args=( abc )
argc__line='cmd_preferred -f -o 4 -- abc'
argc__words=( spec cmd_preferred -f -o 4 -- abc )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OUTPUT
argc_flag1=1
argc_opt1=4
argc_arg1=( abc )
argc__dashdash=1
argc__dashdash=( 1 )
argc__args=( abc )
argc__line='cmd_preferred -f -o 4 abc --'
argc__words=( spec cmd_preferred -f -o 4 abc -- )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OUTPUT
argc_flag1=1
argc_opt1=4
argc_arg1=( abc def )
argc__dashdash=1
argc__dashdash=( 1 )
argc__args=( abc def )
argc__line='cmd_preferred -f -o 4 abc -- def'
argc__words=( spec cmd_preferred -f -o 4 abc -- def )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: tests/spec_test.rs
expression: output
---
RUN
spec cmd_positional xyz -a --abc

OUTPUT
argc_name=xyz
argc_values=( -a --abc )
argc__args=( xyz -a --abc )
argc__fn=cmd_positional
cmd_positional xyz -a --abc

1 change: 1 addition & 0 deletions tests/snapshots/integration__spec_test__spec_help.snap
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ COMMANDS:
cmd_option_quotes Option value quoted
cmd_option_notations Option with value notations
cmd_flag_formats All kind of flags
cmd_positional Positional all required
cmd_positional_only Positional one required
cmd_positional_many Positional all required
cmd_positional_requires Positional all required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ COMMANDS:
cmd_option_quotes Option value quoted
cmd_option_notations Option with value notations
cmd_flag_formats All kind of flags
cmd_positional Positional all required
cmd_positional_only Positional one required
cmd_positional_many Positional all required
cmd_positional_requires Positional all required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ spec help cmd_prefered
OUTPUT
cat >&2 <<-'EOF'
error: invalid value 'cmd_prefered' for '<command>'
[possible values: cmd_preferred, cmd_omitted, cmd_flag_names, cmd_no_long_flags, cmd_option_names, cmd_no_long_options, cmd_option_formats, cmd_option_quotes, cmd_option_notations, cmd_flag_formats, cmd_positional_only, cmd_positional_many, cmd_positional_requires, cmd_positional_with_choices, cmd_positional_with_default, cmd_positional_with_default_fn, cmd_positional_with_choices_and_default, cmd_positional_with_choices_fn, cmd_positional_with_choices_fn2, cmd_positional_with_choices_and_required, cmd_positional_with_choices_fn_and_required, cmd_without_any_arg, cmd_alias, a, alias, cmd_with_hyphens, cmd_nested_command, cmd_nested_command2, cmd_notation_values, cmd_dynamic_compgen, cmd_combine_shorts, cmd_single_dash, cmd_two_multiple_positionals]
[possible values: cmd_preferred, cmd_omitted, cmd_flag_names, cmd_no_long_flags, cmd_option_names, cmd_no_long_options, cmd_option_formats, cmd_option_quotes, cmd_option_notations, cmd_flag_formats, cmd_positional, cmd_positional_only, cmd_positional_many, cmd_positional_requires, cmd_positional_with_choices, cmd_positional_with_default, cmd_positional_with_default_fn, cmd_positional_with_choices_and_default, cmd_positional_with_choices_fn, cmd_positional_with_choices_fn2, cmd_positional_with_choices_and_required, cmd_positional_with_choices_fn_and_required, cmd_without_any_arg, cmd_alias, a, alias, cmd_with_hyphens, cmd_nested_command, cmd_nested_command2, cmd_notation_values, cmd_dynamic_compgen, cmd_combine_shorts, cmd_single_dash, cmd_two_multiple_positionals]

For more information, try '--help'.

Expand Down
7 changes: 7 additions & 0 deletions tests/spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ cmd_flag_formats() {
print_argc_vars
}

# @cmd Positional all required
# @arg name A optional arg 1
# @arg values* A optional arg 2, multiple
cmd_positional() {
print_argc_vars
}

# @cmd Positional one required
# @arg arg1! <DIR> A required arg1
cmd_positional_only() {
Expand Down
5 changes: 5 additions & 0 deletions tests/spec_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ fn test_spec_cmd_single_dash() {
snapshot_spec!(&["spec", "cmd_single_dash", "-flag1", "-opt1", "abc"]);
}

#[test]
fn test_spec_cmd_positional_option_as_positional() {
snapshot_spec!(&["spec", "cmd_positional", "xyz", "-a", "--abc"]);
}

#[test]
fn test_spec_cmd_positional_with_choices_fn() {
snapshot_spec!(&["spec", "cmd_positional_with_choices_fn", "xyz"]);
Expand Down

0 comments on commit 3bb33f6

Please sign in to comment.