Commit f32feec7 authored by Emeric Verschuur's avatar Emeric Verschuur

Add enumeration type

parent 062518cc
......@@ -41,11 +41,12 @@ Declare each options and associated settings like name, arguments, etc. with ```
bashopts_declare option list:
- ```-n <val>```: Option/variable name (all option value will be stored into $<variable name>)
- ```-v <val>```: Default value
- ```-e <val>```: Bash expression: like default but this expression is computed on time and can contain variables and other bash expression
- ```-x <val>```: Bash expression: like default but this expression is computed on time and can contain variables and other bash expression
- ```-o <val>```: Short option name (one character, e.g.: 'h' for -h argument)
- ```-l <val>```: Long option (several characters, e.g.: 'my-opt' for --my-opt argument)
- ```-d <val>```: Option description
- ```-t <val>```: Value type: string, number, boolean (default)
- ```-t <val>```: Value type: string, enumeration, number, boolean (default)
- ```-e <val>```: Enum element: restrict possible values with a list of '-e <element>' options (you have to set one '-e <val>' by elements)
- ```-m <val>```: Method: set (DEFAULT: simple value where $<variable name> will be a simple value), add (list with several values where $<variable name> will be a bash array)
- ```-k <val>```: Custom check method (bash function like bashopts_check_number in bashopts.sh file)
- ```-r```: Value required
......
......@@ -106,7 +106,7 @@ bashopts_check_number() {
echo $1
return 0
fi
bashopts_log E "'$1' is not a valid number"
bashopts_log E "Option $op: '$1' is not a valid number"
return 1
}
......@@ -122,7 +122,7 @@ bashopts_check_boolean() {
return 0
;;
*)
bashopts_log E "'$1' is not a valid boolean value"
bashopts_log E "Option $op: '$1' is not a valid boolean value"
return 1
;;
esac
......@@ -134,6 +134,21 @@ bashopts_check_string() {
return 0
}
# check and format a enumeration value
bashopts_check_enumeration() {
local line expr values
while read -r line; do
expr="^($line)\$"
if [[ "$1" =~ $expr ]]; then
echo "${line##*|}"
return 0
fi
values+=("'${line##*|}'")
done <<< "${2:-${bashopts_optprop_enum_values[$op]}}"
bashopts_log E "Option $op: Invalid value '$1' (accepted values are: ${values[*]})"
return 1
}
# check nothing
bashopts_check_nothing() {
echo "$1"
......@@ -141,7 +156,7 @@ bashopts_check_nothing() {
}
# declare the options property arrays
for f in name default expression short_opt long_opt description type method check setting interactive req_value; do
for f in name default expression short_opt long_opt description type enum_values method check setting interactive req_value; do
eval declare -x -A bashopts_optprop_$f
done
......@@ -208,16 +223,17 @@ bashopts_setup() {
# STEP 2: add options
bashopts_declare() {
local arg arglist options
if ! arglist=$(getopt -o "n:v:e:o:l:d:t:m:k:rsi" -n "$0 " -- "$@"); then
local arg arglist options options_enum_values
if ! arglist=$(getopt -o "n:v:x:o:l:d:t:e:m:k:rsi" -n "$0 " -- "$@"); then
bashopts_log C "Usage bashopts_declare:" \
" -n <val> Name" \
" -v <val> Default value" \
" -e <val> Bash expression: like default but this expression is computed and can contain variables and other bash expression" \
" -x <val> Bash expression: like default but this expression is computed and can contain variables and other bash expression" \
" -o <val> Short option" \
" -l <val> Long option" \
" -d <val> Description" \
" -t <val> Value type: string (default), number, boolean" \
" -t <val> Value type: string, enumeration, number, boolean (default)" \
" -e <val> Enum element: restrict accepted values with a list of '-e <element>' options (you have to set one '-e <val>' by elements)" \
" -m <val> Method: set (DEFAULT: simple value), add (list with several values)" \
" -k <val> Custom check method (bash function)" \
" -r Value required" \
......@@ -232,14 +248,15 @@ bashopts_declare() {
shift
case "$arg" in
-n) options[name]=$(bashopts_check_opt_name $1 || exit 1); shift;;
-v) options[default]=$1; shift;;
-e) options[expression]=$1; shift;;
-o) options[short_opt]=$1; shift;;
-l) options[long_opt]=$1; shift;;
-d) options[description]=$1; shift;;
-t) options[type]=$1; shift;;
-m) options[method]=$1; shift;;
-k) options[check]=$1; shift;;
-v) options[default]=$1; shift;;
-x) options[expression]=$1; shift;;
-o) options[short_opt]=$1; shift;;
-l) options[long_opt]=$1; shift;;
-d) options[description]=$1; shift;;
-t) options[type]=$1; shift;;
-e) options_enum_values+=($1); shift;;
-m) options[method]=$1; shift;;
-k) options[check]=$1; shift;;
-s) options[setting]="true";;
-i) options[interactive]="true";;
-r) options[req_value]="true";;
......@@ -252,20 +269,32 @@ bashopts_declare() {
bashopts_log C "bashopts_declare: -r and -v options cannot be activated at the same time"
fi
# format the type and check/format the default value
case "${options[type]}" in
''|bool|boolean)
case "${options[type],,}" in
''|b|bool|boolean)
options[type]="boolean"
;;
str|string)
e|enum|enumeration)
options[type]="enumeration"
if [ ${#options_enum_values[@]} -lt 2 ]; then
bashopts_log C "bashopts_declare: ${options[name]} enumeration need at least two elements (two '-e <val>' calls at least)"
fi
options[enum_values]="$(IFS=$'\n'; echo "${options_enum_values[*]}")"
;;
s|str|string)
options[type]="string"
;;
num|number)
n|num|number)
options[type]="number"
;;
*)
bashopts_log C "Invalid type ${options[type]}"
;;
esac
# Check for incompatibility with old version (-e opt moved to -x)
if [ "${options[type]}" != "enumeration" ] && [ ${#options_enum_values[@]} -gt 0 ]; then
bashopts_log C "bashopts_declare: The former '-e' option is now moved to '-x'" \
" => the new '-e' is reserved for enumeration elements"
fi
# Setup check value method
if ! [[ -v options[check] ]]; then
options[check]="bashopts_check_${options[type]}"
......@@ -277,7 +306,7 @@ bashopts_declare() {
options[method]="set"
if [ "${options[type]}" != "string" ] || [[ -v options[default] ]]; then
# Check the default value format
options[default]="$(${options[check]} "${options[default]}")" \
options[default]="$(${options[check]} "${options[default]}" "${options[enum_values]}")" \
|| bashopts_log C "Invalid default value for ${options[name]} option"
fi
;;
......@@ -320,6 +349,28 @@ bashopts_declare() {
bashopts_optlist+=(${options[name]})
}
bashopts_get_valid_value_list() {
local op
case "$1" in
-*)
op=${bashopts_arg2op[$1]}
;;
*)
op=$1
;;
esac
case "${bashopts_optprop_type[$op]}" in
boolean)
echo -e "true\nfalse"
;;
enumeration)
while read -r line; do
echo "\"${line##*|}\""
done <<< "${bashopts_optprop_enum_values[$op]}"
;;
esac
}
# maximum of two values
bashopts_math_max() {
echo $(($1>$2?$1:$2))
......@@ -371,7 +422,7 @@ bashopts_dump_value() {
# display the formated help
bashopts_diplay_help() {
local elts optargs_max_len=8 val dval ncol
local elts optargs_max_len=8 val ncol line
declare -A optargs
ncol=$(tput cols || true) 2> /dev/null
# compute the good arguments comumn size
......@@ -398,25 +449,33 @@ bashopts_diplay_help() {
echo
echo "OPTIONS:"
for op in "${bashopts_optlist[@]}"; do
elts=""
dval=""
if ! [[ $op =~ ^__.*__$ ]]; then
# display additional information the each properties
# discarding special options like --help
if [[ -v bashopts_optprop_expression[$op] ]]; then
elts="- [\$$op] (type:${bashopts_optprop_type[$op]}, default:\"${bashopts_optprop_expression[$op]//\"/\\\"}\")"
elif [[ -v bashopts_optprop_default[$op] ]]; then
if [ "${bashopts_optprop_type[$op]}" == "string" ]; then
elts="- [\$$op] (type:${bashopts_optprop_type[$op]}, default:\"${bashopts_optprop_default[$op]//\"/\\\"}\")"
# display arguments, value if available, description, and additional info if available
printf " %-${optargs_max_len}s %s\n" "${optargs[$op]}" "${bashopts_optprop_description[$op]} $(
if ! [[ $op =~ ^__.*__$ ]]; then
# display additional information the each properties
# discarding special options like --help
echo -n "- [\$$op] (type:${bashopts_optprop_type[$op]}"
if [[ -v bashopts_optprop_expression[$op] ]]; then
echo -n ", default:\"${bashopts_optprop_expression[$op]//\"/\\\"}\""
elif [[ -v bashopts_optprop_default[$op] ]]; then
if [[ "${bashopts_optprop_type[$op]}" =~ ^(string|enumeration)$ ]]; then
echo -n ", default:\"${bashopts_optprop_default[$op]//\"/\\\"}\""
else
echo -n ", default:${bashopts_optprop_default[$op]}"
fi
else
elts="- [\$$op] (type:${bashopts_optprop_type[$op]}, default:${bashopts_optprop_default[$op]})"
elts=")"
fi
if [ "${bashopts_optprop_type[$op]}" == "enumeration" ]; then
echo -n ", accepted values:$(
while read -r line; do
echo -n " '${line##*|}'"
done <<< "${bashopts_optprop_enum_values[$op]}"
)"
fi
else
elts="- [\$$op] (type:${bashopts_optprop_type[$op]})"
echo -n ")"
fi
fi
# display arguments, value if available, description, and additional info if available
printf " %-${optargs_max_len}s %s\n" "${optargs[$op]}" "${bashopts_optprop_description[$op]} $elts"
)"
done
test "$1" != "-e" || exit $2
}
......@@ -615,7 +674,7 @@ bashopts_process_option() {
break
fi
done
elif [ "$val_req" == "true" ]; then
elif [ "$val_req" == "true" ] && [ "$__BASHOPTS_DISPLAY_HELP__" != "true" ]; then
bashopts_log E "At least one value required"
if [ "$BASHOPTS_INTERACTIVE" != "true" ]; then
bashopts_log C "Non interactive mode: Exit due to one or more error"
......@@ -632,7 +691,19 @@ bashopts_process_option() {
if [ "$BASHOPTS_INTERACTIVE" == "true" ]; then
# interactive edition
while true; do
echo "* ${bashopts_optprop_description[$op]}"
# Display the property description
echo "* ${bashopts_optprop_description[$op]}$(
# Add possible value list for enumeration type
if [ "${bashopts_optprop_type[$op]}" == "enumeration" ]; then
echo -n " (accepted values:$(
while read -r line; do
echo -n " '${line##*|}'"
done <<< "${bashopts_optprop_enum_values[$op]}"
)"
echo -n ")"
fi
)"
# Add info for array properties
if [ "${bashopts_optprop_method[$op]}" == "add" ]; then
echo " -> List property format: 'single val.' or BASH array '(v1 v2 v3)' or JSON array '[v1, v2, v3]'"
fi
......@@ -660,6 +731,8 @@ bashopts_process_option() {
# non array/normal value
tval=$ival
fi
elif [ "${bashopts_optprop_method[$op]}" == "add" ] && ! [[ -v tval ]]; then
tval=()
fi
# check format
if [ "${#tval[@]}" -eq 0 ] && [ "$val_req" == "true" ]; then
......
......@@ -10,11 +10,12 @@ trap 'bashopts_exit_handle' ERR
bashopts_setup -n "example.sh" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"
# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\"\$first_name \$last_name\""
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress" -i
bashopts_declare -n FIRST_NAME -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n LAST_NAME -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n DISPLAY_NAME -l display-name -t string -d "Display name" -x "\"\$FIRST_NAME \$LAST_NAME\""
bashopts_declare -n AGE -l number -d "Age" -t number
bashopts_declare -n MEANS_OF_TRANSPORTATION -l transport -t enum -e 'c|car' -e 'w|walk' -e 'b|bike' -e 't|transport' -v car -d "Means of transportation" -i
bashopts_declare -n EMAIL_LIST -t string -m add -l email -d "Email adress" -i
# Parse arguments
bashopts_parse_args "$@"
......@@ -24,10 +25,11 @@ bashopts_process_opts
# Display the values
echo
echo "First name: $first_name"
echo "Last name: $last_name"
echo "Display name: $display_name"
echo "Age: $age"
for email in "${email_list[@]}"; do
echo "First name: $FIRST_NAME"
echo "Last name: $LAST_NAME"
echo "Display name: $DISPLAY_NAME"
echo "Means of transportation: $MEANS_OF_TRANSPORTATION"
echo "Age: $AGE"
for email in "${EMAIL_LIST[@]}"; do
echo "Email: $email"
done
......@@ -52,7 +52,7 @@ _test_case_2() {
bashopts_setup -n "$0" -d "Test case $0" -s /tmp/bashopts_testrc
bashopts_declare -n base_path -l base -o b -d "Base path" -t string -v "/opt/test" -i -s
bashopts_declare -n config_path -l config -o c -d "Configuration path" -t string -e "\$base_path/myconf.cfg" -s
bashopts_declare -n config_path -l config -o c -d "Configuration path" -t string -x "\$base_path/myconf.cfg" -s
bashopts_parse_args --base /tmp -n
bashopts_process_opts
......@@ -108,7 +108,7 @@ _test_case_5() {
_test_case_6() {
bashopts_setup -n "$0" -d "Test case $0"
bashopts_declare -n computed_value -e '$(( 2 + 3 * 4 ))' -t string
bashopts_declare -n computed_value -x '$(( 2 + 3 * 4 ))' -t string
bashopts_parse_args
bashopts_process_opts
......@@ -132,7 +132,7 @@ _test_case_7() {
_test_case_8() {
bashopts_setup -n "$0" -d "Test case $0"
bashopts_declare -n array_value -l value -m add -d "Array value" -t string -e '("value 1" "value 2")'
bashopts_declare -n array_value -l value -m add -d "Array value" -t string -x '("value 1" "value 2")'
bashopts_parse_args
bashopts_process_opts
......@@ -224,7 +224,7 @@ _test_case_15() {
rm -f /tmp/bashopts_testrc
bashopts_setup -n "$0" -d "Test case $0" -s /tmp/bashopts_testrc
bashopts_declare -n array_value -l value -m add -d "Array value" -s -i -t string -e '("value 1" "value 2")'
bashopts_declare -n array_value -l value -m add -d "Array value" -s -i -t string -x '("value 1" "value 2")'
bashopts_parse_args --value ""
bashopts_process_opts > /dev/null <<< '( "value 1" "value 3" )'
......@@ -243,7 +243,7 @@ _test_case_16() {
rm -f /tmp/bashopts_testrc
bashopts_setup -n "$0" -d "Test case $0" -s /tmp/bashopts_testrc
bashopts_declare -n array_value -l value -m add -d "Array value" -s -i -t string -e '("value 1" "value 2")'
bashopts_declare -n array_value -l value -m add -d "Array value" -s -i -t string -x '("value 1" "value 2")'
bashopts_parse_args --value ""
bashopts_process_opts > /dev/null <<< '( "value 1" "value 2" )'
......@@ -277,11 +277,11 @@ _test_case_18() {
bashopts_parse_args
req_test_eq "$(bashopts_process_opts 2>&1 >/dev/null <<< "tru" \
| grep '^\[ERR')" "[ERRO] 'tru' is not a valid boolean value"
| grep '^\[ERR')" "[ERRO] Option a_boolean_value: 'tru' is not a valid boolean value"
req_test_eq "$(bashopts_process_opts 2>&1 >/dev/null <<< "fa" \
| grep '^\[ERR')" "[ERRO] 'fa' is not a valid boolean value"
| grep '^\[ERR')" "[ERRO] Option a_boolean_value: 'fa' is not a valid boolean value"
req_test_eq "$(bashopts_process_opts 2>&1 >/dev/null <<< "5" \
| grep '^\[ERR')" "[ERRO] '5' is not a valid boolean value"
| grep '^\[ERR')" "[ERRO] Option a_boolean_value: '5' is not a valid boolean value"
}
_test_case_19() {
......@@ -391,6 +391,22 @@ _test_case_27() {
"[CRIT] bashopts_declare: -r and -v options cannot be activated at the same time"
}
_test_case_28() {
bashopts_setup -n "$0" -d "Test case $0"
bashopts_declare -n value -l value -m add -d "list of enum" -t enum -e '1|one' -e '2|two' -e '3|tree'
req_test_eq "$(bashopts_parse_args --value "4" -n 2>&1 >/dev/null \
| grep '^\[ERR')" "[ERRO] Option value: Invalid value '4' (accepted values are: 'one' 'two' 'tree')"
bashopts_parse_args --value "1" --value "two" -n
bashopts_process_opts
req_test_eq "${#value[@]}" "2"
req_test_eq "${value[0]}" "one"
req_test_eq "${value[1]}" "two"
req_test_eq "$(bashopts_get_valid_value_list --value | tr '\n' '#')" '"one"#"two"#"tree"#'
}
_test() {
bashopts_setup -n "$0" -d "Test case $0"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment