From 67e6bf8567411350bd8d419aa63403cddb7bf747 Mon Sep 17 00:00:00 2001 From: Nikolay Pronchev Date: Sat, 7 Sep 2024 06:26:29 +0000 Subject: [PATCH] Add positional arg var --- README.md | 2 ++ parser.sh | 105 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e6f0e12..2297f5b 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Configuration variables must be assigned before calling `_parse`. | _accept_options | `auto` | `any` — The script accepts any options.
`none` — The script does not accept any options.
`mapped_only` — The script accepts only mapped options.
`auto` — The script accepts only mapped options if at least one has been mapped. Otherwise, it accepts any options. | | _default_max_positional_args | | Maximum number of positional arguments. A different value can be set for each command (see [_map_command](#_map_command)). | | _default_min_positional_args | `0` | Minimum number of positional arguments. A different value can be set for each command (see [_map_command](#_map_command)). | +| _default_positional_arg_variable || Name of the variable that will store the single positional argument. Implicitly sets `_default_max_positional_args` and `_default_min_positional_args` to 1. Command-specific values for `max_args` and `arg_variable` disables this behavior. | | _mapping_key_value_delimiter | `=` | Key-value delimiter for options mapping (see [_map_option](#_map_option)). | | _mapping_values_delimiter | `,` | Values delimiter for options mapping (see [_map_option](#_map_option)). | | _option_duplicates_allowed | `false` | `true` — Multiple uses of options are allowed.
`false` — Multiple uses of options are not allowed.| @@ -131,6 +132,7 @@ Maps a command. Takes key-value pairs as arguments. Key and values list are sepa | description | Command description. Currently not used. | | min_args | Minimum number of positional arguments when using the command. | | max_args | Maximum number of positional arguments when using the command. | +| arg_variable | Name of the variable that will store the command's positional argument. Implicitly sets `min_args` and `max_args` to 1. | ```bash _map_command \ diff --git a/parser.sh b/parser.sh index 5c41f74..6dac446 100755 --- a/parser.sh +++ b/parser.sh @@ -4,6 +4,7 @@ _accept_command=auto # auto, none, any, mapped_only _accept_options=auto # auto, none, any, mapped_only _default_max_positional_args="" _default_min_positional_args=0 +_default_positional_arg_variable="" _mapping_key_value_delimiter="=" _mapping_values_delimiter="," _option_duplicates_allowed=true @@ -151,6 +152,33 @@ _mapped_options_only() { [ "$_mapped_options_count" -gt 0 ] } +_is_free_var_name() { + if [ -n "$_default_positional_arg_variable" ] && [ "$_default_positional_arg_variable" = "$1" ]; then + echo "variable is already used as default positional arg variable: $1." + return 1 + fi + + _i=1 + while [ "$_i" -le "$_mapped_options_count" ]; do + if [ "$1" = "$(_var_value "_options_${_i}_variable")" ]; then + echo "variable is already mapped for option #${_i}: $1." + return 1 + fi + + _i=$(_math "$_i + 1") + done + + _i=1 + while [ "$_i" -le "$_mapped_commands_count" ]; do + if [ "$1" = "$(_var_value "_commands_${_i}_arg_variable")" ]; then + echo "arg variable is already mapped for command #${_i}: $1." + return 1 + fi + + _i=$(_math "$_i + 1") + done +} + _map_command() { _command_index=$(_math "$_mapped_commands_count + 1") _mapping_command_prefix="_commands_${_command_index}" @@ -179,6 +207,10 @@ _map_command() { _validate_command_min_args "$_map_value" elif [ "$_map_key" = "name" ]; then _validate_command_name "$_map_value" + elif [ "$_map_key" = "arg_variable" ]; then + _validate_command_arg_variable "$_map_value" + _assign "${_mapping_command_prefix}_min_args" 1 + _assign "${_mapping_command_prefix}_max_args" 1 else _err "Invalid command #${_command_index} mapping key: $_map_key." fi @@ -233,6 +265,8 @@ _map_option() { _validate_option_aliases "$_map_value" elif [ "$_map_key" = "variable" ]; then _validate_option_variable "$_map_value" + _assign "${_mapping_option_prefix}_min_args" 1 + _assign "${_mapping_option_prefix}_max_args" 1 elif [ "$_map_key" = "description" ]; then _validate_option_description "$_map_value" elif [ "$_map_key" = "max_args" ]; then @@ -345,6 +379,31 @@ _validate_command_name() { done } +_validate_command_arg_variable() { + if [ -n "$(_var_value "${_mapping_command_prefix}_arg_variable")" ]; then + _err "Command #${_command_index} arg variable is already mapped." + fi + + _min_args=$(_var_value "${_mapping_command_prefix}_min_args") + _max_args=$(_var_value "${_mapping_command_prefix}_max_args") + + if { [ -n "$_min_args" ] && [ "$_min_args" -ne 1 ]; } || { [ -n "$_max_args" ] && [ "$_max_args" -ne 1 ]; }; then + _err "Command #${_command_index} arg variable can only be used with commands that requires single argument." + fi + + if [ -z "$1" ]; then + _err "Command #${_command_index} arg variable cannot be empty." + fi + + if ! _is_valid_var_name "$1"; then + _err "Command #${_command_index} arg variable is invalid: $1. Must be a valid variable name." + fi + + if ! _description=$(_is_free_var_name "$1"); then + _err "Command #${_command_index} ${_description}" + fi +} + _validate_option_aliases() { if [ -n "$(_var_value "${_mapping_option_prefix}_aliases")" ]; then _err "Option #${_option_index} aliases are already mapped." @@ -444,14 +503,9 @@ _validate_option_variable() { _err "Option #${_option_index} variable is invalid: $1. Must be a valid variable name." fi - _i=1 - while [ "$_i" -le "$_mapped_options_count" ]; do - if [ "$1" = "$(_var_value "_options_${_i}_variable")" ]; then - _err "Option #${_option_index} variable is already mapped for option #${_i}: $1." - fi - - _i=$(_math "$_i + 1") - done + if ! _description=$(_is_free_var_name "$1"); then + _err "Option #${_option_index} ${_description}" + fi } _validate_option_description() { @@ -477,10 +531,6 @@ _validate_option_max_args() { _err "Option #${_option_index} max args is invalid: $1. Must be a non-negative integer." fi - if [ -n "$(_var_value "${_mapping_option_prefix}_variable")" ] && [ "$1" -ne 1 ]; then - _err "Option #${_option_index} max args is invalid: $1. Implicitly set to 1 when option variable was set." - fi - if (_is "$(_var_value "${_mapping_option_prefix}_required")") && [ "$1" -eq 0 ]; then _err "Option #${_option_index} must be removed as constant. It is required without arguments." fi @@ -498,10 +548,6 @@ _validate_option_min_args() { if ! _is_int "$1"; then _err "Option #${_option_index} min args is invalid: $1. Must be a non-negative integer." fi - - if [ -n "$(_var_value "${_mapping_option_prefix}_variable")" ] && [ "$1" -ne 1 ]; then - _err "Option #${_option_index} min args is invalid: $1. Implicitly set to 1 when option variable was set." - fi } _validate_option_required() { @@ -640,25 +686,36 @@ _parse_positional_args() { _err "Positional arguments must be placed before options: $1." fi - if [ -z "$_command_index" ]; then - _max_args_count="$_default_max_positional_args" - else - _max_args_count=$(_var_value "_commands_${_command_index}_max_args") + if [ -n "$_command_index" ]; then + _max_args=$(_var_value "_commands_${_command_index}_max_args") + _arg_variable="$(_var_value "_commands_${_command_index}_arg_variable")" + fi + + if [ -z "$_max_args" ]; then + _max_args=$_default_max_positional_args + fi + + if [ -z "$_arg_variable" ]; then + _arg_variable="$_default_positional_arg_variable" fi while [ $# -gt 0 ]; do _positional_arg_index=$(_math "$_positional_args_count + 1") - if [ -n "$_max_args_count" ] && [ "$_positional_arg_index" -gt "$_max_args_count" ]; then + if [ -n "$_max_args" ] && [ "$_positional_arg_index" -gt "$_max_args" ]; then if [ -z "$_command_index" ]; then - _err "Maximum $_max_args_count positional argument(s) allowed." + _err "Maximum $_max_args positional argument(s) allowed." else - _err "Maximum $_max_args_count positional argument(s) allowed for command: $(_var_value "_commands_${_command_index}_name")." + _err "Maximum $_max_args positional argument(s) allowed for command: $(_var_value "_commands_${_command_index}_name")." fi fi _assign "_positional_args_${_positional_arg_index}" "$1" - _positional_args_count=$_positional_arg_index + _assign "_positional_args_count" "$_positional_arg_index" + + if [ -n "$_arg_variable" ]; then + _assign "$_arg_variable" "$1" + fi shift done