Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and versions are tracked in the repo-root `VERSION` file.

### Fixed

- Enforced required repeatable positional tails based on values consumed by the
repeatable itself, independent of earlier fixed positional arguments.
- Made table-driven CLI declaration transactional so any late semantic error
restores an existing model exactly or removes a partial new model.
- Rejected unreachable CLI command routes and ancestor/child option collisions,
Expand Down
5 changes: 3 additions & 2 deletions lib/bash/cli/lib_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ __base_bash_libs_cli_apply_defaults_and_validate__() {
}

__base_bash_libs_cli_apply_positionals__() {
local model="$1" path="$2" value name index repeatable required default
local model="$1" path="$2" value name index repeatable required default repeat_start

__base_bash_libs_cli_collect_positionals__ "$model" "$path"
if ((${#__base_bash_libs_cli_positional_names[@]} == 0)); then
Expand All @@ -1191,12 +1191,13 @@ __base_bash_libs_cli_apply_positionals__() {
required="$(__base_bash_libs_cli_positional_meta__ "$model" "$path" "$name" required)"
default="$(__base_bash_libs_cli_positional_meta__ "$model" "$path" "$name" default)"
if [[ "$repeatable" =~ ^(1|true|yes)$ ]]; then
repeat_start="$index"
while ((index < ${#BASE_BASH_LIBS_CLI_RESULT_POSITIONALS[@]})); do
value="${BASE_BASH_LIBS_CLI_RESULT_POSITIONALS[index]}"
__base_bash_libs_cli_validate_value__ "$model" "$path" positional "$name" "$value" || return $?
((index++))
done
if ((index == 0)) && [[ "$required" =~ ^(1|true|yes)$ ]]; then
if ((index == repeat_start)) && [[ "$required" =~ ^(1|true|yes)$ ]]; then
__base_bash_libs_cli_error__ "required positional '$name' was not provided."
return $?
fi
Expand Down
40 changes: 40 additions & 0 deletions lib/bash/cli/tests/lib_cli.bats
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,46 @@ EOF
[ "${BASE_BASH_LIBS_CLI_RESULT_POSITIONALS[1]}" = "--looks-like-option" ]
}

@test "required repeatable positionals count only their own consumed values" {
base_cli_model_init direct_repeat name=direct-repeat
base_cli_command direct_repeat run "Run"
base_cli_positional direct_repeat run target required=true
base_cli_positional direct_repeat run files required=true repeatable=true

bats_run base_cli_parse direct_repeat -- run target-only
[ "$status" -eq 2 ]
[[ "$output" == *"required positional 'files' was not provided"* ]]
base_cli_parse direct_repeat -- run target one
base_cli_parse direct_repeat -- run target one two

base_cli_model_init root_repeat name=root-repeat
base_cli_positional root_repeat '' items required=true repeatable=true
bats_run base_cli_parse root_repeat --
[ "$status" -eq 2 ]
base_cli_parse root_repeat -- one
base_cli_parse root_repeat -- one two

base_cli_model_init optional_repeat name=optional-repeat
base_cli_command optional_repeat run "Run"
base_cli_positional optional_repeat run target required=true
base_cli_positional optional_repeat run files repeatable=true
base_cli_parse optional_repeat -- run target-only
}

@test "quick declarations enforce required repeatable positional tails" {
base_cli_declare table_repeat \
'model|name=table-repeat' \
'command|path=run|description=Run' \
'positional|path=run|name=target|required=true' \
'positional|path=run|name=files|required=true|repeatable=true'

bats_run base_cli_parse table_repeat -- run target-only
[ "$status" -eq 2 ]
[[ "$output" == *"required positional 'files' was not provided"* ]]
base_cli_parse table_repeat -- run target one
base_cli_parse table_repeat -- run target one two
}

@test "run invokes handlers but help and version do not" {
local handler_calls=0
demo_handler() {
Expand Down
Loading