diff --git a/bin/awkLint b/bin/awkLint index 85f3425b..c0761e09 100755 --- a/bin/awkLint +++ b/bin/awkLint @@ -1236,7 +1236,7 @@ Result in checkstyle format.")" # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/buildBinFiles b/bin/buildBinFiles index 06af60d6..9a5784cd 100755 --- a/bin/buildBinFiles +++ b/bin/buildBinFiles @@ -1275,7 +1275,7 @@ INTERNAL TOOL")" # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/buildPushDockerImage b/bin/buildPushDockerImage index e628daa2..2f9812ea 100755 --- a/bin/buildPushDockerImage +++ b/bin/buildPushDockerImage @@ -1396,21 +1396,21 @@ INTERNAL # shellcheck disable=SC2054 helpArray=(vendor\ image\ to\ use:\ alpine\|ubuntu) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu' + echo " Default value: ubuntu" echo ' Possible values: alpine|ubuntu' echo -e " ${__HELP_OPTION_COLOR}--bash-version ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(version\ of\ bash\ to\ use:\ 4.4\|5.0\|5.1\|5.2) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 5.1' + echo " Default value: 5.1" echo ' Possible values: 4.4|5.0|5.1|5.2' echo -e " ${__HELP_OPTION_COLOR}--bash-base-image ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(bash\ bash\ image\ to\ use\ \(eg:\ ubuntu:20.04\,\ amd64/bash:4.4-alpine3.18\)) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu:20.04' + echo " Default value: ubuntu:20.04" echo -e " ${__HELP_OPTION_COLOR}--branch-name ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 @@ -1463,7 +1463,7 @@ INTERNAL # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/compile b/bin/compile index 0f3e15ad..8fdf6c08 100755 --- a/bin/compile +++ b/bin/compile @@ -159,17 +159,17 @@ declare -agx _COMPILE_FILE_ARGUMENTS=() # read command parameters # $@ is all command line parameters passed to the script. -# -o is for short options like -h -# -l is for long options with double dash like --help -# the comma separates different long options +# -o is for short compileOptions like -h +# -l is for long compileOptions with double dash like --help +# the comma separates different long compileOptions longOpts="help,src-dir:,template-dir:,bin-dir:,root-dir:,src-path:,bin-file:,keep-temp-files,verbose,vv,vvv" shortOpts="hks:t:b:r:f:v" -options=$(getopt -l "${longOpts}" -o "${shortOpts}" -a -- "${BASH_FRAMEWORK_ARGV[@]}" 2>/dev/null) || { +compileOptions=$(getopt -l "${longOpts}" -o "${shortOpts}" -a -- "${BASH_FRAMEWORK_ARGV[@]}" 2>/dev/null) || { showHelp - Log::fatal "invalid options specified" + Log::fatal "invalid compileOptions specified" } -eval set -- "${options}" +eval set -- "${compileOptions}" while true; do case $1 in -h | --help) @@ -251,7 +251,7 @@ done # add framework src dir by default srcDirs+=("${_COMPILE_SRC_DIR}") _COMPILE_FILE_ARGUMENTS+=(-s "${_COMPILE_SRC_DIR}") -# add temporary generated functions src dir(Options) +# add temporary generated functions src dir(compileOptions) mkdir -p "${TMPDIR}/src" || true srcDirs+=("${TMPDIR}/src") _COMPILE_FILE_ARGUMENTS+=(-s "${TMPDIR}/src") diff --git a/bin/definitionLint b/bin/definitionLint index cafbcd2f..ed80ba3a 100755 --- a/bin/definitionLint +++ b/bin/definitionLint @@ -1457,7 +1457,7 @@ definitionLintCommand() { # shellcheck disable=SC2054 helpArray=(define\ output\ format\ of\ this\ command) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: plain' + echo " Default value: plain" echo ' Possible values: plain|checkstyle' echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" @@ -1501,7 +1501,7 @@ definitionLintCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/doc b/bin/doc index d91a7fe3..3aaf85c9 100755 --- a/bin/doc +++ b/bin/doc @@ -1933,21 +1933,21 @@ docCommand() { # shellcheck disable=SC2054 helpArray=(vendor\ image\ to\ use:\ alpine\|ubuntu) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu' + echo " Default value: ubuntu" echo ' Possible values: alpine|ubuntu' echo -e " ${__HELP_OPTION_COLOR}--bash-version ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(version\ of\ bash\ to\ use:\ 4.4\|5.0\|5.1\|5.2) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 5.1' + echo " Default value: 5.1" echo ' Possible values: 4.4|5.0|5.1|5.2' echo -e " ${__HELP_OPTION_COLOR}--bash-base-image ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(bash\ bash\ image\ to\ use\ \(eg:\ ubuntu:20.04\,\ amd64/bash:4.4-alpine3.18\)) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu:20.04' + echo " Default value: ubuntu:20.04" echo -e " ${__HELP_OPTION_COLOR}--branch-name ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 @@ -2005,7 +2005,7 @@ docCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/dockerLint b/bin/dockerLint index 0eb45df9..4053bf23 100755 --- a/bin/dockerLint +++ b/bin/dockerLint @@ -1562,7 +1562,7 @@ dockerLintCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/findShebangFiles b/bin/findShebangFiles index 8bc6782d..469c96ee 100755 --- a/bin/findShebangFiles +++ b/bin/findShebangFiles @@ -1270,7 +1270,7 @@ findShebangFiles() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/frameworkLint b/bin/frameworkLint index 46fc49b8..ff185f42 100755 --- a/bin/frameworkLint +++ b/bin/frameworkLint @@ -1379,7 +1379,7 @@ frameworkLintCommand() { # shellcheck disable=SC2054 helpArray=(define\ output\ format\ of\ this\ command) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: plain' + echo " Default value: plain" echo ' Possible values: plain|checkstyle' echo -e " ${__HELP_OPTION_COLOR}--expected-warnings-count ${__HELP_NORMAL} {single}" local -a helpArray @@ -1428,7 +1428,7 @@ frameworkLintCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/megalinter b/bin/megalinter index 69496b18..ade3be9d 100755 --- a/bin/megalinter +++ b/bin/megalinter @@ -1497,7 +1497,7 @@ megalinterCommand() { # shellcheck disable=SC2054 helpArray=(define\ output\ format\ of\ this\ command) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: plain' + echo " Default value: plain" echo ' Possible values: plain|json' echo -e " ${__HELP_OPTION_COLOR}--fix${__HELP_NORMAL} {single}" local -a helpArray @@ -1519,7 +1519,7 @@ megalinterCommand() { # shellcheck disable=SC2054 helpArray=(Specify\ docker\ megalinter\ image\ name\ to\ use) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: oxsecurity/megalinter-terraform:v7.4.0' + echo " Default value: oxsecurity/megalinter-terraform:v7.4.0" echo -e " ${__HELP_OPTION_COLOR}--check-megalinter-version${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 @@ -1530,7 +1530,7 @@ megalinterCommand() { # shellcheck disable=SC2054 helpArray=(Specify\ megalinter\ config\ filename\ to\ use) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: .mega-linter.yml' + echo " Default value: .mega-linter.yml" echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" @@ -1573,7 +1573,7 @@ megalinterCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/plantuml b/bin/plantuml index b78baaec..1b047482 100755 --- a/bin/plantuml +++ b/bin/plantuml @@ -1334,7 +1334,7 @@ plantumlCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/runBuildContainer b/bin/runBuildContainer index b451141d..94008453 100755 --- a/bin/runBuildContainer +++ b/bin/runBuildContainer @@ -1530,21 +1530,21 @@ runBuildContainerCommand() { # shellcheck disable=SC2054 helpArray=(vendor\ image\ to\ use:\ alpine\|ubuntu) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu' + echo " Default value: ubuntu" echo ' Possible values: alpine|ubuntu' echo -e " ${__HELP_OPTION_COLOR}--bash-version ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(version\ of\ bash\ to\ use:\ 4.4\|5.0\|5.1\|5.2) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 5.1' + echo " Default value: 5.1" echo ' Possible values: 4.4|5.0|5.1|5.2' echo -e " ${__HELP_OPTION_COLOR}--bash-base-image ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(bash\ bash\ image\ to\ use\ \(eg:\ ubuntu:20.04\,\ amd64/bash:4.4-alpine3.18\)) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu:20.04' + echo " Default value: ubuntu:20.04" echo -e " ${__HELP_OPTION_COLOR}--branch-name ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 @@ -1602,7 +1602,7 @@ runBuildContainerCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/shellcheckLint b/bin/shellcheckLint index 8dfde0d0..5e4da78f 100755 --- a/bin/shellcheckLint +++ b/bin/shellcheckLint @@ -1635,7 +1635,7 @@ shellcheckLintCommand() { # shellcheck disable=SC2054 helpArray=(define\ output\ format\ of\ this\ command) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: tty' + echo " Default value: tty" echo ' Possible values: checkstyle|diff|gcc|json|json1|quiet|tty' echo -e " ${__HELP_OPTION_COLOR}--staged${__HELP_NORMAL} {single}" local -a helpArray @@ -1689,7 +1689,7 @@ shellcheckLintCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/bin/test b/bin/test index 1bde5341..d5206f6d 100755 --- a/bin/test +++ b/bin/test @@ -1707,21 +1707,21 @@ testCommand() { # shellcheck disable=SC2054 helpArray=(vendor\ image\ to\ use:\ alpine\|ubuntu) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu' + echo " Default value: ubuntu" echo ' Possible values: alpine|ubuntu' echo -e " ${__HELP_OPTION_COLOR}--bash-version ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(version\ of\ bash\ to\ use:\ 4.4\|5.0\|5.1\|5.2) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 5.1' + echo " Default value: 5.1" echo ' Possible values: 4.4|5.0|5.1|5.2' echo -e " ${__HELP_OPTION_COLOR}--bash-base-image ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 helpArray=(bash\ bash\ image\ to\ use\ \(eg:\ ubuntu:20.04\,\ amd64/bash:4.4-alpine3.18\)) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: ubuntu:20.04' + echo " Default value: ubuntu:20.04" echo -e " ${__HELP_OPTION_COLOR}--branch-name ${__HELP_NORMAL} {single}" local -a helpArray # shellcheck disable=SC2054 @@ -1779,7 +1779,7 @@ testCommand() { # shellcheck disable=SC2054 helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' + echo " Default value: default" echo ' Possible values: default|default-force|noColor' echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" local -a helpArray diff --git a/manualTests/Object::create.sh b/manualTests/Object::create.sh new file mode 100755 index 00000000..b1945642 --- /dev/null +++ b/manualTests/Object::create.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +rootDir="$(cd "$(readlink -e "${BASH_SOURCE[0]%/*}")/.." && pwd -P)" +export FRAMEWORK_ROOT_DIR="${rootDir}" +export _COMPILE_ROOT_DIR="${rootDir}" + +srcDir="$(cd "${rootDir}/src" && pwd -P)" + +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" +# shellcheck source=/src/Log/__all.sh +source "${srcDir}/Log/__all.sh" + +#set -x +set -o errexit +set -o pipefail +export TMPDIR="/tmp" + +Object::create zzzGroupGlobalOptionsFunction \ + --type group \ + --property-title "GLOBAL OPTIONS:" + +Object::create simpleObjectFunction \ + --type "Group" + +Object::create groupObjectFunction \ + --type "Group" \ + --property-title "title" \ + --property-help "help" + +BASH_FRAMEWORK_DISPLAY_LEVEL=__LEVEL_DEBUG + +Object::create optionFunction \ + --type "Option" \ + --property-variableName "varName" + +# shellcheck disable=SC2154 +Options2::validateOptionObject "${optionFunction}" diff --git a/manualTests/Object::setArray.sh b/manualTests/Object::setArray.sh new file mode 100755 index 00000000..bc5b6efa --- /dev/null +++ b/manualTests/Object::setArray.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +rootDir="$(cd "$(readlink -e "${BASH_SOURCE[0]%/*}")/.." && pwd -P)" +export FRAMEWORK_ROOT_DIR="${rootDir}" +export _COMPILE_ROOT_DIR="${rootDir}" + +srcDir="$(cd "${rootDir}/src" && pwd -P)" + +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=/src/Log/__all.sh +source "${srcDir}/Log/__all.sh" + +declare -a missingArrayTerminator=( + --type "missingArrayTerminatorType" + --array-list "elem1" "elem2" "elem3" + --property-property "propertyValue" +) +Object::setArray missingArrayTerminator --array-list "newElem1" "newElem2" + +declare -p missingArrayTerminator diff --git a/manualTests/Object::setProperty.sh b/manualTests/Object::setProperty.sh new file mode 100755 index 00000000..e527d550 --- /dev/null +++ b/manualTests/Object::setProperty.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +rootDir="$(cd "$(readlink -e "${BASH_SOURCE[0]%/*}")/.." && pwd -P)" +export FRAMEWORK_ROOT_DIR="${rootDir}" +export _COMPILE_ROOT_DIR="${rootDir}" + +srcDir="$(cd "${rootDir}/src" && pwd -P)" + +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=/src/Log/__all.sh +source "${srcDir}/Log/__all.sh" + +declare -a newPropertyObject=( + --type "simpleObjectType" + --property-property "propertyValue" +) +Object::setProperty newPropertyObject --property-newProperty "value" +declare -p newPropertyObject diff --git a/manualTests/Options2::renderHelpArg.sh b/manualTests/Options2::renderHelpArg.sh new file mode 100755 index 00000000..8928dce2 --- /dev/null +++ b/manualTests/Options2::renderHelpArg.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +rootDir="$(cd "$(readlink -e "${BASH_SOURCE[0]%/*}")/.." && pwd -P)" +export FRAMEWORK_ROOT_DIR="${rootDir}" +export _COMPILE_ROOT_DIR="${rootDir}" + +srcDir="$(cd "${rootDir}/src" && pwd -P)" + +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" +# shellcheck source=/src/Log/__all.sh +source "${srcDir}/Log/__all.sh" +# shellcheck source=/src/UI/theme.sh +source "${srcDir}/UI/theme.sh" +# shellcheck source=/src/Assert/tty.sh +source "${srcDir}/Assert/tty.sh" + +declare -a arg=( + --type "Arg" + --property-variableName "var" + --property-name "name" + --property-help "help" + --property-title "Global options" + --property-mandatory 1 + --property-max 2 +) +# shellcheck disable=SC2034 +declare -a group=( + --type "Group" + --property-title "Global options" + --property-help "help" +) +Options2::renderGroupHelp group +declare -a arg=( + --type "Arg" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-callback "François" +) +Options2::validateArgObject arg +UI::theme "default" +Options2::renderArgHelp arg 2>&1 diff --git a/manualTests/testCreateObject.sh b/manualTests/testCreateObject.sh new file mode 100755 index 00000000..5db579f0 --- /dev/null +++ b/manualTests/testCreateObject.sh @@ -0,0 +1,41 @@ +#!/bin/bash +FRAMEWORK_ROOT_DIR=. +source src/Log/__all.sh +source src/Crypto/uuidV4.sh +source src/Object/create.sh +source src/Options2/__all.sh + +declare myFunction +Object::create myFunction \ + --type "Command" \ + --property-name "François" +declare myFunction2 +Object::create myFunction2 \ + --array-list "a" \ + --type "Command2" \ + --property-name "François2" \ + --array-list "b" \ + --array-list "c" + +echo "--------------------------------------------" +${myFunction} getProperty --property-name +${myFunction2} getProperty --property-name +${myFunction} getProperty --property-name +${myFunction2} getProperty --property-list + +echo "------------------- setProperty -------------------------" +${myFunction} setProperty name "myFunctionFrançois3" +${myFunction} getProperty --property-name +${myFunction2} setProperty name "myFunction2François3" +${myFunction2} getProperty --property-name +echo -n "name2 " +${myFunction2} setProperty name2 "newProperty name2" + +${myFunction2} getProperty --property-name2 + +echo "--------------------------------------------" +Object::create myFunction2 \ + --function-name "argFunction2Overload" \ + --type "Command2Overload" \ + --property-name "François2Overload" +${myFunction2} getProperty --property-name diff --git a/src/Object/__all.sh b/src/Object/__all.sh new file mode 100755 index 00000000..9d5c869b --- /dev/null +++ b/src/Object/__all.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# shellcheck source=src/Object/create.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/create.sh" +# shellcheck source=src/Object/memberExists.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/memberExists.sh" +# shellcheck source=src/Object/getProperty.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/getProperty.sh" +# shellcheck source=src/Object/setProperty.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/setProperty.sh" +# shellcheck source=src/Object/getArray.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/getArray.sh" +# shellcheck source=src/Object/setArray.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/setArray.sh" +# shellcheck source=/src/Crypto/uuidV4.sh +source "${FRAMEWORK_ROOT_DIR}/src/Crypto/uuidV4.sh" diff --git a/src/Object/create.bats b/src/Object/create.bats new file mode 100755 index 00000000..c2101568 --- /dev/null +++ b/src/Object/create.bats @@ -0,0 +1,175 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::create::simpleObject { #@test + declare simpleObjectFunction + Object::create simpleObjectFunction \ + --type "simpleObjectType" \ + --property-property "propertyValue" + + run ${simpleObjectFunction} type + assert_output "simpleObjectType" + + run ${simpleObjectFunction} strict + assert_output "1" + + run ${simpleObjectFunction} functionName + [[ "${simpleObjectFunction}" =~ ^[0-9a-f]{32}$ ]] + + run ${simpleObjectFunction} getProperty property + assert_output "propertyValue" + + run ${simpleObjectFunction} invalidCommand + assert_output --partial "ERROR - invalid command invalidCommand" + assert_failure 1 + + run ${simpleObjectFunction} getProperty propertyUnknown + assert_output --partial "ERROR - unknown property propertyUnknown" + assert_failure 2 +} + +function Object::create::missingPositionalArg { #@test + declare missingPositionalArg + run Object::create \ + --type "simpleObjectType" \ + --propertyInvalid-property "propertyValue" 2>&1 + + assert_output --partial "local: \`--type': invalid variable name for name reference" + assert_failure 1 +} + +function Object::create::simpleObjectNonStrict { #@test + declare simpleObjectType + Object::create simpleObjectType \ + --strict 0 \ + --type "simpleObjectType" \ + --property-property "propertyValue" + + run ${simpleObjectType} strict + assert_output "0" + + run ${simpleObjectType} getProperty propertyUnknown + assert_output "" + assert_success +} + +function Object::create::invalidProperty { #@test + declare simpleObjectType + run Object::create simpleObjectType \ + --type "simpleObjectType" \ + --propertyInvalid-property "propertyValue" 2>&1 + + assert_output --partial "ERROR - invalid object property --propertyInvalid-property" + assert_failure 1 +} + +function Object::create::duplicatedProperty { #@test + declare duplicatedProperty + run Object::create duplicatedProperty \ + --type "simpleObjectType" \ + --property-property "propertyValue1" \ + --property-property "propertyValue2" 2>&1 + + assert_output --partial "ERROR - property property is provided more than one time" + assert_failure 1 +} + +function Object::create::invalidObjectType { #@test + declare invalidObjectType + run Object::create invalidObjectType \ + --type "invalidéObjectType" \ + --property-property "propertyValue" 2>&1 + + assert_output --partial "ERROR - invalid object type invalidéObjectType" + assert_failure 1 +} + +function Object::create::missingObjectType { #@test + run Object::create missingObjectType \ + --property-property "propertyValue" 2>&1 + + assert_output --partial "ERROR - missing object type" + assert_failure 1 +} + +function Object::create::unknownArray { #@test + declare unknownArray + Object::create unknownArray \ + --type "simpleObjectType" \ + --array-list "unknownArray" 2>&1 + + run ${unknownArray} getArray "unknownArray" + + assert_output --partial "ERROR - unknown array unknownArray" + assert_failure 2 +} + +function Object::create::propertyArrayOrdered { #@test + declare propertyArrayOrdered + Object::create propertyArrayOrdered \ + --type "simpleObjectType" \ + --property-property "propertyValue" \ + --array-list "value1" \ + --array-list "value2" + + run ${propertyArrayOrdered} type + assert_success + assert_output "simpleObjectType" + + run ${propertyArrayOrdered} functionName + [[ "${output}" =~ ^[0-9a-f]{32}$ ]] + + run ${propertyArrayOrdered} getProperty property + assert_success + assert_output "propertyValue" + + run ${propertyArrayOrdered} getArray list + assert_success + assert_lines_count 2 + assert_line --index 0 "value1" + assert_line --index 1 "value2" +} + +function Object::create::propertyArrayUnordered { #@test + declare propertyArrayUnordered + Object::create propertyArrayUnordered \ + --type "simpleObjectType" \ + --array-list "value1" \ + --array-list "value2" \ + --property-property "propertyValue" \ + --array-list "value3" + + run ${propertyArrayUnordered} type + assert_success + assert_output "simpleObjectType" + + run ${propertyArrayUnordered} functionName + [[ "${output}" =~ ^[0-9a-f]{32}$ ]] + + run ${propertyArrayUnordered} getProperty property + assert_success + assert_output "propertyValue" + + run ${propertyArrayUnordered} getArray list + assert_success + assert_lines_count 3 + assert_line --index 0 "value1" + assert_line --index 1 "value2" + assert_line --index 2 "value3" + + run ${propertyArrayUnordered} getMembers + assert_success + assert_lines_count 2 + assert_line --index 0 "list" + assert_line --index 1 "property" +} diff --git a/src/Object/create.sh b/src/Object/create.sh new file mode 100755 index 00000000..3c0911fd --- /dev/null +++ b/src/Object/create.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +# @description create a simili object based on function generation +# @exitcode 1 invalid command or invalid parameter +# @warning this function is using eval feature which can be dangerous +# with unchecked data +# @deprecated as this version is using eval, prefer to use +# Object::getProperty version +Object::create() { + local -n objectCreateVariable=$1 + shift || true + # shellcheck disable=SC2016 + createTemplateFunction() { + local type="$1" + local functionName="$2" + shift 2 || true + local -a properties=("$@") + + echo "${functionName}() {" + echo ' local command="$1"' + echo " local strict='${strict}'" + echo -n ' '; declare -p properties + echo ' local -i propertiesLength="${#properties[@]}"' + echo ' if [[ "${command}" = "type" ]]; then' + echo " echo '${type}'" + echo ' elif [[ "${command}" = "functionName" ]]; then' + echo " echo '${functionName}'" + echo ' elif [[ "${command}" = "strict" ]]; then' + echo " echo '${strict}'" + echo ' elif [[ "${command}" = "getProperty" ]]; then' + echo ' local -i i=0 || true' + echo ' local propertyName="$2"' + echo ' local propertyFound="0"' + echo ' while ((i < propertiesLength)); do' + echo ' if [[ "${properties[${i}]}" = "--property-${propertyName}" ]]; then' + echo ' echo "${properties[$((i+1))]}"' + echo ' return 0' + echo ' fi' + echo ' ((i=i+2))' + echo ' done' + echo ' if [[ "${strict}" = "1" && "${propertyFound}" = "0" ]]; then' + echo ' Log::displayError "unknown property ${propertyName}"' + echo ' return 2' + echo ' fi' + echo ' elif [[ "${command}" = "setProperty" ]]; then' + echo ' local i=0 || true' + echo ' local propertyName="$2"' + echo ' local propertyValue="$3"' + echo ' local -a newProperties=()' + echo ' local propertyFound="0"' + echo ' while ((i < propertiesLength)); do' + echo ' if [[ "${properties[${i}]}" = "--property-${propertyName}" ]]; then' + echo ' propertyFound="1"' + echo ' newProperties+=("${properties[${i}]}" "${propertyValue}")' + echo ' if ((i < propertiesLength-2)); then' + echo ' newProperties+=("${properties[@]:i+2}")' + echo ' fi' + echo ' break' + echo ' fi' + echo ' newProperties+=("${properties[${i}]}" "${properties[$((i + 1))]}")' + echo ' ((i=i+2))' + echo ' done' + echo ' if [[ "${propertyFound}" = "0" ]]; then' + echo ' newProperties+=("--property-${propertyName}" "${propertyValue}")' + echo ' fi' + echo " eval \"\$(createTemplateFunction \"${type}\" \"${functionName}\" \"\${newProperties[@]}\")\"" + echo ' elif [[ "${command}" = "getArray" ]]; then' + echo ' local -i i=0 || true' + echo ' local propertyName="$2"' + echo ' local propertyFound="0"' + echo ' while ((i < propertiesLength)); do' + echo ' if [[ "${properties[${i}]}" = "--array-${propertyName}" ]]; then' + echo ' propertyFound="1"' + echo ' echo "${properties[$((i+1))]}"' + echo ' fi' + echo ' ((i=i+2))' + echo ' done' + echo ' if [[ "${strict}" = "1" && "${propertyFound}" = "0" ]]; then' + echo ' Log::displayError "unknown array ${propertyName}"' + echo ' return 2' + echo ' fi' + echo ' elif [[ "${command}" = "setArray" ]]; then' + echo ' local -i i=0 || true' + echo ' local arrayName="$2"' + echo ' local -a arrayValues=("$@")' + echo ' local -a newProperties=()' + echo ' local propertyFound="0"' + echo ' while ((i < propertiesLength)); do' + echo ' if [[ "${properties[${i}]}" = "--array-${propertyName}" ]]; then' + echo ' propertyFound="1"' + echo ' newProperties+=("${properties[${i}]}" "${propertyValue}")' + echo ' if ((i < propertiesLength-2)); then' + echo ' newProperties+=("${properties[@]:i+2}");' + echo ' fi' + echo ' break' + echo ' fi' + echo ' newProperties+=("${properties[${i}]}" "${properties[$((i + 1))]}")' + echo ' ((i=i+2))' + echo ' done' + echo ' if [[ "${propertyFound}" = "0" ]]; then' + echo ' newProperties+=("--property-${propertyName}" "${propertyValue}")' + echo ' fi' + echo " eval \"\$(createTemplateFunction \"${type}\" \"${functionName}\" \"\${newProperties[@]}\")\"" + echo ' elif [[ "${command}" = "getMembers" ]]; then' + echo ' while ((i < propertiesLength)); do' + echo ' echo "${properties[${i}]}" | sed -E "s/--(property|array)-//"' + echo ' ((i=i+2))' + echo ' done | sort -u' + echo ' else' + echo ' Log::displayError "invalid command ${command}"' + echo ' return 1' + echo ' fi' + echo '}' + } + + local type functionName + local strict="1" + local -a properties=() + while (($#>0)); do + case "$1" in + --type) + shift || true + type="$1" + ;; + --strict) + # strict means that property non existence is failing with error + shift || true + strict="$1" + ;; + --property-*) + if Array::contains "$1" "${properties[@]}"; then + Log::displayError "property ${1#--property-} is provided more than one time" + return 1 + fi + properties+=("$1" "$2") + shift || true + ;; + --array-*) + properties+=("$1" "$2") + shift || true + ;; + *) + Log::displayError "invalid object property $1" + return 1 + esac + shift || true + done + + if [[ -z "${type}" ]]; then + Log::displayError "missing object type" + return 1 + fi + if ! Assert::posixFunctionName "${type}"; then + Log::displayError "invalid object type ${type}" + return 1 + fi + functionName="$(Crypto::uuidV4)" + functionName="${functionName//-/}" + + eval "$(createTemplateFunction "${type}" "${functionName}" "${properties[@]}")" + # shellcheck disable=SC2034 + objectCreateVariable=${functionName} +} diff --git a/src/Object/getArray.bats b/src/Object/getArray.bats new file mode 100755 index 00000000..52dc16e5 --- /dev/null +++ b/src/Object/getArray.bats @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::getArray::simpleObject { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-myList "elem1" + ) + run Object::getArray simpleObject --array-myList 1 + assert_output "elem1" + assert_success + + run Object::getArray simpleObject --array-myList 0 + assert_output "elem1" + assert_success +} + +function Object::getArray::multipleArrayValues { #@test + declare -a multipleArrayValues=( + --type "multipleArrayValuesType" + --array-myList "elem1" "elem2" -- + --property-type "type" + ) + run Object::getArray multipleArrayValues --array-myList 1 + assert_lines_count 2 + assert_line --index 0 "elem1" + assert_line --index 1 "elem2" + assert_success + + run Object::getArray multipleArrayValues --array-myList 0 + assert_lines_count 2 + assert_line --index 0 "elem1" + assert_line --index 1 "elem2" + assert_success +} + +function Object::getArray::unknownArray { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + run Object::getArray simpleObject --array-unknownArray 1 + assert_output "" + assert_failure 1 + + run Object::getArray simpleObject --array-unknownArray 0 + assert_output "" + assert_success +} + +function Object::getArray::customTerminator { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-myList "elem1" "elem2" "@@@" + --property-property "propertyValue" + ) + OBJECT_TEMPLATE_ARRAY_TERMINATOR="@@@" run Object::getArray simpleObject --array-myList 1 + assert_line --index 0 "elem1" + assert_line --index 1 "elem2" + assert_lines_count 2 + assert_success + + OBJECT_TEMPLATE_ARRAY_TERMINATOR="@@@" run Object::getArray simpleObject --array-myList 0 + assert_line --index 0 "elem1" + assert_line --index 1 "elem2" + assert_lines_count 2 + assert_success +} diff --git a/src/Object/getArray.sh b/src/Object/getArray.sh new file mode 100755 index 00000000..1db9181a --- /dev/null +++ b/src/Object/getArray.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# @description get array elements from object +# @arg $1 object_get_array_objectData:&String[] the object +# @arg $2 arrayName:String the name of the array property to search (eg: --array-property) +# @arg $3 strict:Boolean if !0 then return code 1 if array property not found +# @stdout the array property values if array property found(one value by line) +# @exitcode 1 if array property not found +Object::getArray() { + local -n object_get_array_objectData=$1 + local arrayName="${2:-}" + local strict="${3:-0}" + + local -i propertiesLength="${#object_get_array_objectData[@]}" + local -i i=0 || true + while ((i < propertiesLength)); do + if [[ "${object_get_array_objectData[${i}]}" = "${arrayName}" ]]; then + ((++i)) + # eat next elements until finding terminator + while ((i < propertiesLength)); do + if [[ "${object_get_array_objectData[${i}]}" = "${OBJECT_TEMPLATE_ARRAY_TERMINATOR:---}" ]]; then + return 0 + fi + echo "${object_get_array_objectData[${i}]}" + ((++i)) + done + return 0 + fi + ((++i)) + done + + if [[ "${strict}" != "0" ]]; then + return 1 + fi +} diff --git a/src/Object/getProperty.bats b/src/Object/getProperty.bats new file mode 100755 index 00000000..bfb7df19 --- /dev/null +++ b/src/Object/getProperty.bats @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::getProperty::simpleObject { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + run Object::getProperty simpleObject --property-property 1 + assert_output "propertyValue" + assert_success + + run Object::getProperty simpleObject --property-property 0 + assert_output "propertyValue" + assert_success +} + +function Object::getProperty::unknownProperty { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + run Object::getProperty simpleObject --property-unknownProperty 1 + assert_output "" + assert_failure 1 + + run Object::getProperty simpleObject --property-unknownProperty 0 + assert_output "" + assert_success +} + +function Object::getProperty::withArray { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-list "elem1" -- + --property-property "propertyValue" + ) + run Object::getProperty simpleObject --property-property 1 + assert_output "propertyValue" + assert_success + + run Object::getProperty simpleObject --property-property 0 + assert_output "propertyValue" + assert_success +} + +function Object::getProperty::property2 { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-list "elem1" -- + --property-property "propertyValue" + --property-property2 "propertyValue2" + ) + run Object::getProperty simpleObject --property-property2 1 + assert_output "propertyValue2" + assert_success + + run Object::getProperty simpleObject --property-property2 0 + assert_output "propertyValue2" + assert_success +} diff --git a/src/Object/getProperty.sh b/src/Object/getProperty.sh new file mode 100755 index 00000000..049dcd15 --- /dev/null +++ b/src/Object/getProperty.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# @description get property value from object +# @arg $1 object_get_property_objectData:&String[] the object +# @arg $2 propertyName:String the name of the property to search (eg: --property) +# @arg $3 strict:Boolean if !0 then return code 1 if property not found +# @stdout the property value if property found +# @exitcode 1 if property not found +Object::getProperty() { + local -n object_get_property_objectData=$1 + local propertyName="${2:-}" + local strict="${3:-0}" + + local -i propertiesLength="${#object_get_property_objectData[@]}" + local -i i=0 || true + local propertyFound="0" + while ((i < propertiesLength)); do + if [[ "${object_get_property_objectData[${i}]}" = "${propertyName}" ]]; then + echo "${object_get_property_objectData[$((i + 1))]}" + return 0 + fi + ((i = i + 1)) + done + if [[ "${strict}" != "0" && "${propertyFound}" = "0" ]]; then + return 1 + fi +} diff --git a/src/Object/memberExists.bats b/src/Object/memberExists.bats new file mode 100755 index 00000000..6302ae46 --- /dev/null +++ b/src/Object/memberExists.bats @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::memberExists::simpleObject { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + run Object::memberExists simpleObject --property-property + assert_output "" + assert_success +} + +function Object::memberExists::unknownProperty { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + run Object::memberExists simpleObject --property-unknownProperty + assert_output "" + assert_failure 1 +} + +function Object::memberExists::withArray { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-list "elem1" -- + --property-property "propertyValue" + ) + run Object::memberExists simpleObject --property-property + assert_output "" + assert_success + +} + +function Object::memberExists::property2 { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-list "elem1" -- + --property-property "propertyValue" + --property-property2 "propertyValue2" + ) + run Object::memberExists simpleObject --property-property2 + assert_output "" + assert_success +} diff --git a/src/Object/memberExists.sh b/src/Object/memberExists.sh new file mode 100755 index 00000000..59b3fc94 --- /dev/null +++ b/src/Object/memberExists.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# @description check if member(property or array property) exists on given object +# @arg $1 object_member_exists_objectData:&String[] the object +# @arg $2 memberName:String the name of the member to search (eg: --property) +# @exitcode 1 if member not found +Object::memberExists() { + local -n object_member_exists_objectData=$1 + local memberName="${2:-}" + + local -i propertiesLength="${#object_member_exists_objectData[@]}" + local -i i=0 || true + while ((i < propertiesLength)); do + if [[ "${object_member_exists_objectData[${i}]}" = "${memberName}" ]]; then + return 0 + fi + ((i = i + 1)) + done + return 1 +} diff --git a/src/Object/setArray.bats b/src/Object/setArray.bats new file mode 100755 index 00000000..c23d4e97 --- /dev/null +++ b/src/Object/setArray.bats @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::setArray::simpleObject { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --array-list "elem1" -- + --property-property "propertyValue" + ) + local status=0 + Object::setArray simpleObject --array-list "newElem1" "newElem2" || status=1 + [[ "${status}" = "0" ]] + run echo "${simpleObject[@]}" + assert_output "--type simpleObjectType --property-property propertyValue --array-list newElem1 newElem2 --" +} + +function Object::setArray::multipleElements { #@test + declare -a multipleElements=( + --type "multipleElementsType" + --array-list "elem1" "elem2" "elem3" -- + --property-property "propertyValue" + ) + local status=0 + Object::setArray multipleElements --array-list "newElem1" "newElem2" || status=1 + [[ "${status}" = "0" ]] + run echo "${multipleElements[@]}" + assert_output "--type multipleElementsType --property-property propertyValue --array-list newElem1 newElem2 --" +} + +function Object::setArray::missingArrayTerminator { #@test + declare -a missingArrayTerminator=( + --type "missingArrayTerminatorType" + --array-list "elem1" "elem2" "elem3" + --property-property "propertyValue" + ) + local status=0 + Object::setArray missingArrayTerminator --array-list "newElem1" "newElem2" || status=1 + [[ "${status}" = "0" ]] + run echo "${missingArrayTerminator[@]}" + assert_output "--type missingArrayTerminatorType --array-list newElem1 newElem2 --" +} + +function Object::setArray::customArrayTerminator { #@test + declare -a customArrayTerminator=( + --type "customArrayTerminatorType" + --array-list "elem1" "elem2" "elem3" "@@@" + --property-property "propertyValue" + ) + local status=0 + OBJECT_TEMPLATE_ARRAY_TERMINATOR="@@@" Object::setArray customArrayTerminator --array-list "newElem1" "newElem2" || status=1 + [[ "${status}" = "0" ]] + run echo "${customArrayTerminator[@]}" + assert_output "--type customArrayTerminatorType --property-property "propertyValue" --array-list newElem1 newElem2 @@@" +} + +function Object::setArray::newProperty { #@test + declare -a newPropertyObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + local status=0 + Object::setArray newPropertyObject --array-list "newElem1" "newElem2" || status=1 + [[ "${status}" = "0" ]] + run echo "${newPropertyObject[@]}" + assert_output "--type simpleObjectType --property-property propertyValue --array-list newElem1 newElem2 --" +} diff --git a/src/Object/setArray.sh b/src/Object/setArray.sh new file mode 100755 index 00000000..0224132a --- /dev/null +++ b/src/Object/setArray.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# @description create or overwrite array property on object +# @arg $1 object_set_array_objectData:&String[] the object +# @arg $2 arrayName:String the name of the array property to search (eg: --array-property) +# @arg $@ arrayValues:String[] array values to set +Object::setArray() { + local -n object_set_array_objectData=$1 + local arrayName="${2:-}" + shift 2 || true + local -a arrayValues=("$@") + + local -i propertiesLength="${#object_set_array_objectData[@]}" + local -a newProperties=() + + while ((i < propertiesLength)); do + if [[ "${object_set_array_objectData[${i}]}" = "${arrayName}" ]]; then + ((++i)) + # eat next elements until finding terminator + while ((i < propertiesLength)); do + if [[ "${object_set_array_objectData[${i}]}" = "${OBJECT_TEMPLATE_ARRAY_TERMINATOR:---}" ]]; then + ((++i)) + break + fi + ((++i)) + done + break + fi + newProperties+=("${object_set_array_objectData[${i}]}") + ((++i)) + done + + # copy rest of the properties if any + while ((i < propertiesLength)); do + newProperties+=("${object_set_array_objectData[${i}]}") + ((++i)) + done + + # finally set the array + newProperties+=("${arrayName}" "${arrayValues[@]}" "${OBJECT_TEMPLATE_ARRAY_TERMINATOR:---}") + object_set_array_objectData=("${newProperties[@]}") +} diff --git a/src/Object/setProperty.bats b/src/Object/setProperty.bats new file mode 100755 index 00000000..8efe7a42 --- /dev/null +++ b/src/Object/setProperty.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 +# shellcheck disable=SC2034 + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Assert/posixFunctionName.sh +source "${srcDir}/Assert/posixFunctionName.sh" +# shellcheck source=src/Array/contains.sh +source "${srcDir}/Array/contains.sh" + +function Object::setProperty::simpleObject { #@test + declare -a simpleObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + local status=0 + Object::setProperty simpleObject --property-property "newPropertyValue" || status=1 + [[ "${status}" = "0" ]] + run echo "${simpleObject[@]}" + assert_output "--type simpleObjectType --property-property newPropertyValue" +} + +function Object::setProperty::multipleProperties { #@test + declare -a multipleProperties=( + --type "multiplePropertiesType" + --property-property "propertyValue" + --property-property2 "propertyValue2" + ) + local status=0 + Object::setProperty multipleProperties --property-property "newPropertyValue" || status=1 + [[ "${status}" = "0" ]] + run echo "${multipleProperties[@]}" + assert_output "--type multiplePropertiesType --property-property newPropertyValue --property-property2 propertyValue2" +} + +function Object::setProperty::withArray { #@test + declare -a withArray=( + --type "withArrayType" + --property-property "propertyValue" + --array-list "elem1" "elem2" -- + --property-property2 "propertyValue2" + ) + local status=0 + Object::setProperty withArray --property-property2 "newPropertyValue" || status=1 + [[ "${status}" = "0" ]] + run echo "${withArray[@]}" + assert_output "--type withArrayType --property-property propertyValue --array-list "elem1" "elem2" -- --property-property2 newPropertyValue" +} + +function Object::setProperty::newProperty { #@test + declare -a newPropertyObject=( + --type "simpleObjectType" + --property-property "propertyValue" + ) + local status=0 + Object::setProperty newPropertyObject --property-newProperty "value" || status=1 + [[ "${status}" = "0" ]] + run echo "${newPropertyObject[@]}" + assert_output "--type simpleObjectType --property-property propertyValue --property-newProperty value" +} diff --git a/src/Object/setProperty.sh b/src/Object/setProperty.sh new file mode 100755 index 00000000..a9566592 --- /dev/null +++ b/src/Object/setProperty.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# @description create or overwrite property value on object +# @arg $1 object_set_property_objectData:&String[] the object +# @arg $2 propertyName:String the name of the property to search (eg: --property) +# @arg $3 propertyValue:String property value to set +Object::setProperty() { + local -n object_set_property_objectData=$1 + local propertyName="${2:-}" + local propertyValue="${3:-}" + + local i=0 || true + local -i propertiesLength="${#object_set_property_objectData[@]}" + local -a newProperties=() + local propertyFound="0" + while ((i < propertiesLength)); do + if [[ "${object_set_property_objectData[${i}]}" = "${propertyName}" ]]; then + propertyFound="1" + newProperties+=( + "${object_set_property_objectData[${i}]}" "${propertyValue}" + ) + if ((i < propertiesLength - 2)); then + newProperties+=("${object_set_property_objectData[@]:i+2}") + fi + break + fi + newProperties+=("${object_set_property_objectData[${i}]}") + ((i = i + 1)) + done + if [[ "${propertyFound}" = "0" ]]; then + newProperties+=("${propertyName}" "${propertyValue}") + fi + object_set_property_objectData=("${newProperties[@]}") +} diff --git a/src/Options/templates/option.tpl b/src/Options/templates/option.tpl index bace6af2..5beb6f61 100755 --- a/src/Options/templates/option.tpl +++ b/src/Options/templates/option.tpl @@ -63,7 +63,7 @@ echo 'echo -e " <%% helpOpt "1" %>"' .INCLUDE "${tplDir}/helpArg.tpl" % if [[ -n "${defaultValue}" ]]; then - echo "echo ' Default value: <% ${defaultValue} %>'" + echo 'echo " Default value: <% ${defaultValue} %>"' % fi % if [[ -n "${authorizedValues}" ]]; then echo "echo ' Possible values: <% ${authorizedValues} %>'" diff --git a/src/Options2/__all.sh b/src/Options2/__all.sh new file mode 100755 index 00000000..ba2d956c --- /dev/null +++ b/src/Options2/__all.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +# shellcheck source=src/UI/theme.sh +source "${FRAMEWORK_ROOT_DIR}/src/UI/theme.sh" +# shellcheck source=src/Options/bashTpl.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options/bashTpl.sh" + # shellcheck source=src/Options/assertAlt.sh + source "${FRAMEWORK_ROOT_DIR}/src/Options/assertAlt.sh" +# shellcheck source=/src/Assert/posixFunctionName.sh +source "${FRAMEWORK_ROOT_DIR}/src/Assert/posixFunctionName.sh" +# shellcheck source=/src/Assert/bashFrameworkFunction.sh +source "${FRAMEWORK_ROOT_DIR}/src/Assert/bashFrameworkFunction.sh" +# shellcheck source=/src/Assert/validVariableName.sh +source "${FRAMEWORK_ROOT_DIR}/src/Assert/validVariableName.sh" +# shellcheck source=/src/Array/contains.sh +source "${FRAMEWORK_ROOT_DIR}/src/Array/contains.sh" +# shellcheck source=/src/Array/join.sh +source "${FRAMEWORK_ROOT_DIR}/src/Array/join.sh" +# shellcheck source=/src/Filters/removeAnsiCodes.sh +source "${FRAMEWORK_ROOT_DIR}/src/Filters/removeAnsiCodes.sh" +# shellcheck source=/src/Array/wrap2.sh +source "${FRAMEWORK_ROOT_DIR}/src/Array/wrap2.sh" +# shellcheck source=/src/Framework/createTempFile.sh +source "${FRAMEWORK_ROOT_DIR}/src/Framework/createTempFile.sh" +# shellcheck source=/src/Crypto/uuidV4.sh +source "${FRAMEWORK_ROOT_DIR}/src/Crypto/uuidV4.sh" +# shellcheck source=src/Options/generateFunction.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options/generateFunction.sh" +# shellcheck source=src/Object/create.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/create.sh" +# shellcheck source=src/Object/getProperty.sh +source "${FRAMEWORK_ROOT_DIR}/src/Object/getProperty.sh" + +# shellcheck source=src/Options2/validateCommandObject.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/validateCommandObject.sh" + +# shellcheck source=src/Options2/validateGroupObject.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/validateGroupObject.sh" +# shellcheck source=src/Options2/renderGroupHelp.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/renderGroupHelp.sh" + +# shellcheck source=src/Options2/validateArgObject.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/validateArgObject.sh" +# shellcheck source=src/Options2/renderArgHelp.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/renderArgHelp.sh" + +# shellcheck source=src/Options2/validateOptionObject.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/validateOptionObject.sh" +# shellcheck source=src/Options2/renderOptionHelp.sh +source "${FRAMEWORK_ROOT_DIR}/src/Options2/renderOptionHelp.sh" diff --git a/src/Options2/renderArgHelp.bats b/src/Options2/renderArgHelp.bats new file mode 100755 index 00000000..3464738a --- /dev/null +++ b/src/Options2/renderArgHelp.bats @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::renderArgHelp::noOption { #@test + run Options2::renderArgHelp + assert_lines_count 1 + assert_output --partial "ERROR - Options2::renderArgHelp - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::renderArgHelp::invalidObject1 { #@test + declare -a invalidObject1=() + run Options2::renderArgHelp invalidObject1 + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - passed object is not an argument" + assert_failure 2 +} + +function Options2::renderArgHelp::invalidObject2 { #@test + invalidObject2() { :; } + run Options2::renderArgHelp invalidObject2 + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - passed object is not an argument" + assert_failure 2 +} + +function Options2::renderArgHelp::notAnOption { #@test + declare -a notAnOption=( + --type "NotAnOption" + ) + + run Options2::renderArgHelp notAnOptionFunction + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - passed object is not an argument" + assert_failure 2 +} + +function Options::renderArgHelp::OptionValid { #@test + local status=0 + declare -a arg=( + --type "Arg" + --property-variableName "var" + --property-name "name" + --property-help "help" + --property-title "Global options" + ) + Options2::validateArgObject() { + return 0 + } + run Options2::renderArgHelp arg 2>&1 + assert_success + assert_line --index 0 "[${__HELP_OPTION_COLOR}name${__HELP_NORMAL} {list} (optional)]" + assert_line --index 1 "Global options" + assert_line --index 2 "help" + assert_lines_count 3 +} diff --git a/src/Options2/renderArgHelp.sh b/src/Options2/renderArgHelp.sh new file mode 100755 index 00000000..2902572d --- /dev/null +++ b/src/Options2/renderArgHelp.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +# @description Generates a function that allows to manipulate a group of options. +# function generated allows group options using `--group` option when +# using `Options::generateOption` +# +# #### Output on stdout +# +# By default the name of the random generated function name +# is displayed as output of this function. +# By providing the option `--function-name`, the output of this +# function will be the generated function itself with the chosen name. +# +# #### Syntax +# +# ```text +# Usage: Options2::renderOptionHelp [OPTIONS] +# +# OPTIONS: +# --title +# [--help ] +# [--function-name ] +# ``` +# +# #### Example +# +# ```bash +# declare optionGroup="$( +# Options2::renderOptionHelp \ +# --title "Command global options" \ +# --help "The Console component adds some predefined options to all commands:" +# )" +# Options::sourceFunction "${optionGroup}" +# "${optionGroup}" help +# ``` +# +# @option --title (mandatory) provides group title +# @option --help (optional) provides command description help +# @option --function-name (optional) the name of the function that will be generated +# @exitcode 1 if error during option parsing +# @exitcode 1 if bash-tpl error during template rendering +# @exitcode 2 if file generation error (only if functionName argument empty) +# @stderr diagnostics information is displayed +# @see [generateCommand function](#/doc/guides/Options/generateCommand) +# @see [generateOption function](#/doc/guides/Options/generateOption) +# @see [group function](#/doc/guides/Options/functionGroup) +Options2::renderArgHelp() { + if (( $# != 1 )); then + Log::displayError "Options2::renderArgHelp - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n renderArgHelpObject=$1 + if ! Options2::validateArgObject renderArgHelpObject; then + return 2 + fi + local help title min max name mandatory + title="$(Object::getProperty renderArgHelpObject --property-title)" + help="$(Object::getProperty renderArgHelpObject --property-help "strict" || echo '')" + name="$(Object::getProperty renderArgHelpObject --property-name "strict" || echo '')" + min="$(Object::getProperty renderArgHelpObject --property-min "strict" || echo '0')" + max="$(Object::getProperty renderArgHelpObject --property-max "strict" || echo '-1')" + if [[ "${min}" = "0" ]]; then + mandatory="$(Object::getProperty renderArgHelpObject --property-mandatory "strict" || echo '0')" + if [[ "${mandatory}" = "1" ]]; then + min="1" + fi + fi + + displayHelp() { + echo -e "${__HELP_TITLE_COLOR}${title}${__RESET_COLOR}" + if [[ -z "${help}" ]]; then + echo "No help available'" + elif [[ $(type -t "${help}") == "function" ]]; then + local -a helpArray + # shellcheck disable=SC2054,SC2206 + mapfile -t helpArray < <(${help}) + Array::wrap2 " " 76 4 "${helpArray[@]}" + else + local -a helpArray + local helpEscaped + printf -v helpEscaped '%q' "${help}" + # shellcheck disable=SC2054,SC2206 + helpArray=(${helpEscaped}) + Array::wrap2 " " 76 4 "${helpArray[@]}" + fi + } + + helpArg() { + local spec="" + spec+="${__HELP_OPTION_COLOR}${name}${__HELP_NORMAL}" + if ((max == 1)); then + spec+=' {single}' + if ((min == 1)); then + spec+=' (mandatory)' + fi + else + spec+=' {list}' + if ((min > 0)); then + spec+=" (at least ${min} times)" + else + spec+=' (optional)' + fi + if ((max > 0)); then + spec+=" (at most ${max} times)" + fi + fi + local helpArg="" + ((min == 0)) && helpArg+="[" + helpArg+="${spec//^[[:blank:]]/}" + ((min == 0)) && helpArg+="]" + echo -e "${helpArg}" + } + + helpArg + displayHelp +} diff --git a/src/Options2/renderGroupHelp.bats b/src/Options2/renderGroupHelp.bats new file mode 100755 index 00000000..73bcced0 --- /dev/null +++ b/src/Options2/renderGroupHelp.bats @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::renderGroupHelp::noOption { #@test + run Options2::renderGroupHelp + assert_lines_count 1 + assert_output --partial "ERROR - Options2::renderGroupHelp - exactly one parameter has to be provided" + assert_failure 1 +} + + +function Options2::renderGroupHelp::groupOptionValid { #@test + local status=0 + declare -a group=( + --type "Group" + --property-title "Global options" + --property-help "help" + ) + Options2::validateGroupObject() { + return 0 + } + run Options2::renderGroupHelp group >"${BATS_TEST_TMPDIR}/result" 2>&1 + assert_success + assert_line --index 0 "$(echo -e "${__HELP_TITLE_COLOR}Global options${__RESET_COLOR}")" + assert_line --index 1 "help" +} + +function Options2::renderGroupHelp::groupObjectInvalid { #@test + local status=0 + declare -a group=() + Options2::validateGroupObject() { + return 1 + } + run Options2::renderGroupHelp group 2>&1 + assert_output "" + assert_failure 2 +} diff --git a/src/Options2/renderGroupHelp.sh b/src/Options2/renderGroupHelp.sh new file mode 100755 index 00000000..802ec9ed --- /dev/null +++ b/src/Options2/renderGroupHelp.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# @description Generates a function that allows to manipulate a group of options. +# function generated allows group options using `--group` option when +# using `Options::generateOption` +# +# #### Output on stdout +# +# By default the name of the random generated function name +# is displayed as output of this function. +# By providing the option `--function-name`, the output of this +# function will be the generated function itself with the chosen name. +# +# #### Syntax +# +# ```text +# Usage: Options2::renderGroupHelp [OPTIONS] +# +# OPTIONS: +# --title +# [--help ] +# [--function-name ] +# ``` +# +# #### Example +# +# ```bash +# declare optionGroup="$( +# Options2::renderGroupHelp \ +# --title "Command global options" \ +# --help "The Console component adds some predefined options to all commands:" +# )" +# Options::sourceFunction "${optionGroup}" +# "${optionGroup}" help +# ``` +# +# @option --title (mandatory) provides group title +# @option --help (optional) provides command description help +# @option --function-name (optional) the name of the function that will be generated +# @exitcode 1 if error during option parsing +# @exitcode 1 if bash-tpl error during template rendering +# @exitcode 2 if file generation error (only if functionName argument empty) +# @stderr diagnostics information is displayed +# @see [generateCommand function](#/doc/guides/Options/generateCommand) +# @see [generateOption function](#/doc/guides/Options/generateOption) +# @see [group function](#/doc/guides/Options/functionGroup) +Options2::renderGroupHelp() { + if (( $# != 1 )); then + Log::displayError "Options2::renderGroupHelp - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n renderGroupHelpObject=$1 + if ! Options2::validateGroupObject renderGroupHelpObject; then + return 2 + fi + local help title + title="$(Object::getProperty renderGroupHelpObject --property-title)" + help="$(Object::getProperty renderGroupHelpObject --property-help)" + + echo -e "${__HELP_TITLE_COLOR}${title}${__RESET_COLOR}" + if [[ -n "${help}" ]]; then + echo -e "${help}" + fi +} diff --git a/src/Options2/renderOptionHelp.bats b/src/Options2/renderOptionHelp.bats new file mode 100755 index 00000000..28ab1b3d --- /dev/null +++ b/src/Options2/renderOptionHelp.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::renderOptionHelp::noOption { #@test + run Options2::renderOptionHelp + assert_lines_count 1 + assert_output --partial "ERROR - Options2::renderOptionHelp - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::renderOptionHelp::invalidObject { #@test + function invalidObject() { + : + } + run Options2::renderOptionHelp invalidObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - passed object is not an option" + assert_failure 2 +} + +function Options2::renderOptionHelp::notAnOption { #@test + declare -a object=( + --type "NotAnOption" + ) + + run Options2::renderOptionHelp object + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - passed object is not an option" + assert_failure 2 +} + +function Options2::renderOptionHelp::OptionValid { #@test + local status=0 + callback() { + : + } + declare -a object=( + --type "Option" + --property-variableName "var" + --property-variableType "Boolean" + --property-help "help" + --property-title "Global options" + --array-alt "--help" + ) + + run Options2::renderOptionHelp object 2>&1 + assert_success + assert_line --index 0 "$(echo -e "${__HELP_TITLE_COLOR}Global options${__RESET_COLOR}")" + assert_line --index 1 "help" +} diff --git a/src/Options2/renderOptionHelp.sh b/src/Options2/renderOptionHelp.sh new file mode 100755 index 00000000..5a6b570b --- /dev/null +++ b/src/Options2/renderOptionHelp.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +# @description Generates a function that allows to manipulate a group of options. +# function generated allows group options using `--group` option when +# using `Options::generateOption` +# +# #### Output on stdout +# +# By default the name of the random generated function name +# is displayed as output of this function. +# By providing the option `--function-name`, the output of this +# function will be the generated function itself with the chosen name. +# +# #### Syntax +# +# ```text +# Usage: Options2::renderOptionHelp [OPTIONS] +# +# OPTIONS: +# --title +# [--help ] +# [--function-name ] +# ``` +# +# #### Example +# +# ```bash +# declare optionGroup="$( +# Options2::renderOptionHelp \ +# --title "Command global options" \ +# --help "The Console component adds some predefined options to all commands:" +# )" +# Options::sourceFunction "${optionGroup}" +# "${optionGroup}" help +# ``` +# +# @option --title (mandatory) provides group title +# @option --help (optional) provides command description help +# @option --function-name (optional) the name of the function that will be generated +# @exitcode 1 if error during option parsing +# @exitcode 1 if bash-tpl error during template rendering +# @exitcode 2 if file generation error (only if functionName argument empty) +# @stderr diagnostics information is displayed +# @see [generateCommand function](#/doc/guides/Options/generateCommand) +# @see [generateOption function](#/doc/guides/Options/generateOption) +# @see [group function](#/doc/guides/Options/functionGroup) +Options2::renderOptionHelp() { + if (( $# != 1 )); then + Log::displayError "Options2::renderOptionHelp - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n renderOptionHelpInstanceObject=$1 + if ! Options2::validateOptionObject renderOptionHelpInstanceObject; then + return 2 + fi + local help title variableType helpValueName min max + title="$(Object::getProperty renderOptionHelpInstanceObject --property-title)" + help="$(Object::getProperty renderOptionHelpInstanceObject --property-help)" + variableType="$(Object::getProperty renderOptionHelpInstanceObject --property-variableType)" + helpValueName="$(Object::getProperty renderOptionHelpInstanceObject --property-helpValueName)" + min="$(Object::getProperty renderOptionHelpInstanceObject --property-min)" + max="$(Object::getProperty renderOptionHelpInstanceObject --property-max)" + local -a alts + readarray -t alts < <(Object::getArray renderOptionHelpInstanceObject --array-alt) + + displayHelp() { + echo -e "${__HELP_TITLE_COLOR}${title}${__RESET_COLOR}" + if [[ -z "${help}" ]]; then + echo "No help available'" + elif [[ $(type -t "${help}") == "function" ]]; then + local -a helpArray + # shellcheck disable=SC2054,SC2206 + mapfile -t helpArray < <(${help}) + Array::wrap2 " " 76 4 "${helpArray[@]}" + else + local -a helpArray + local helpEscaped + printf -v helpEscaped '%q' "${help}" + # shellcheck disable=SC2054,SC2206 + helpArray=(${helpEscaped}) + Array::wrap2 " " 76 4 "${helpArray[@]}" + fi + } + + helpOpt() { + local spec="" + spec+="${__HELP_OPTION_COLOR}" + spec+="$(Array::join "${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}" "${alts[@]}")" + if [[ "${variableType}" != "Boolean" ]]; then + spec+=" <${helpValueName}>" + fi + spec+="${__HELP_NORMAL}" + if ((max == 1)); then + spec+=' {single}' + if ((min == 1)); then + spec+=' (mandatory)' + fi + else + spec+=' {list}' + if ((min > 0)); then + spec+=" (at least ${min} times)" + else + spec+=' (optional)' + fi + if ((max > 0)); then + spec+=" (at most ${max} times)" + fi + fi + local helpOpt="" + helpOpt+="${spec//^[[:blank:]]/}" + echo -e "${helpOpt}" + } + + displayHelp + helpOpt + if [[ -n "${defaultValue}" ]]; then + echo "Default value: ${defaultValue}" + fi + if [[ -n "${authorizedValues}" ]]; then + echo "Possible values: ${authorizedValues}" + fi +} diff --git a/src/Options2/validateArgObject.bats b/src/Options2/validateArgObject.bats new file mode 100755 index 00000000..9a032084 --- /dev/null +++ b/src/Options2/validateArgObject.bats @@ -0,0 +1,269 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::validateArgObject::noOption { #@test + run Options2::validateArgObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateArgObject::missingValue { #@test + run Options2::validateArgObject invalidObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - passed object is not an argument" + assert_failure 2 +} + +function Options2::validateArgObject::tooMuchArgs { #@test + declare -a arg=( + --type "Arg" + ) + run Options2::validateArgObject arg arg + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateArgObject::invalidObjectType { #@test + declare -a arg=( + --type "notAnArg" + ) + + run Options2::validateArgObject arg + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateArgObject - passed object is not an argument" + assert_failure 2 +} + +function Options2::validateArgObject::variableNameMandatory { #@test + declare -a arg=( + --type "Arg" + --property-variableType "String" + ) + + run Options2::validateArgObject arg 2>&1 + assert_output --partial "ERROR - Options2::validateArgObject - variableName is mandatory" + assert_failure 1 + assert_lines_count 1 +} + +function Options2::validateArgObject::variableNameInvalid { #@test + declare -a arg=( + --type "Arg" + --property-name "valid" + --property-variableName "François" + ) + + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - invalid variableName François" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::nameMandatory { #@test + declare -a arg=( + --type "Arg" + --property-variableName "validVariableName" + ) + + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - name is mandatory" + assert_failure 1 + assert_lines_count 1 +} + +function Options2::validateArgObject::nameInvalid { #@test + declare -a arg=( + --type "Arg" + --property-name "François" + --property-variableName "validVariableName" + ) + + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - invalid name François" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::callbackInvalid { #@test + declare -a arg2=( + --type "Arg" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-callback "François" + ) + + run Options2::validateArgObject arg2 2>&1 + assert_output --partial "ERROR - Options2::validateArgObject - callback 'François' - function does not exists" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateArgObject::callbackValid { #@test + callback() { + : + } + declare -a arg=( + --type "Arg" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-callback "callback" + ) + run Options2::validateArgObject arg + assert_output "" + assert_success +} + +function Options2::validateArgObject::String::authorizedValuesValueInvalidValue { #@test + declare -a arg=( + --type "Arg" + --property-variableType "String" + --property-variableName "varName" + --property-name "valid" + --property-authorizedValues " invalid | valid" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - authorizedValues invalid regexp ' invalid | valid'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::authorizedValuesValueInvalidValue { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-authorizedValues " invalid | valid" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - authorizedValues invalid regexp ' invalid | valid'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::regexpInvalid { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-regexp " " + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - regexp invalid regexp ' '" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::String::helpValueNameInvalidOption { #@test + declare -a arg=( + --type "Arg" + --property-variableType "String" + --property-variableName "varName" + --property-name "valid" + --property-helpValueName "invalid help" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - helpValueName should be a single word 'invalid help'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::helpValueNameInvalidOption { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-helpValueName "invalid help" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - helpValueName should be a single word 'invalid help'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::minValueEmpty { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-min "" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::minValueInvalid { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-min "François" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::minValueLessThan0 { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-min "-1" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateArgObject::StringArray::minValueGreaterThanMaxValue { #@test + declare -a arg=( + --type "Arg" + --property-variableType "StringArray" + --property-variableName "varName" + --property-name "valid" + --property-min "3" + --property-max "1" + --array-alt "--help" + ) + run Options2::validateArgObject arg + assert_output --partial "ERROR - Options2::validateArgObject - max value should be greater than min value" + assert_failure 2 + assert_lines_count 1 +} diff --git a/src/Options2/validateArgObject.sh b/src/Options2/validateArgObject.sh new file mode 100755 index 00000000..ceabbfad --- /dev/null +++ b/src/Options2/validateArgObject.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +# @description Validates options properties created +# using `Object::create` +# +# #### Example +# +# ```bash +# declare Object::create \ +# --function-name "optionFunction" \ +# --type "Option" \ +# --property-variableName "varName" +# +# Options2::validateArgObject "${optionFunction}" +# ``` +# #### Arg Structure +# +# - --function-name (optional) the name of the function that will be generated +# - --type "Arg" +# - --property-variableType automatically computed based on min/max +# - --property-variableName (mandatory) provides the variable name that will be used to store the parsed options. +# - --property-name (optional) provides the name of the argument (default: variableName) +# - --property-mandatory (optional) as its name indicates, by default an option is optional. But using `--mandatory` you can make the option mandatory. An error will be generated if the option is not found during parsing arguments. +# - --property-help (optional) provides option help description (Default: Empty string) +# - --array-callback (0 or several times) the callback called if the option is parsed successfully. The option value will be passed as parameter (several parameters if type StringArray). +# - --property-regexp (optional) regexp to use to validate the option value +# - --property-authorizedValues (optional) Indicates the possible value list separated by | character (Default: "" means no check) +# - --property-helpValueName (optional) Indicates the name of value of the option to display in help (Default: "String") +# - --property-mandatory (optional) if 1 then will set min value to 1 (except if min is already set) (Default: "0") +# - --property-min (optional) minimum number of options to provide (Defaults to 0 or 1 if mandatory). +# - --property-max (optional) maximum number of options to provide (Default "" means no limit) +# +# Example: +# ```bash +# Object::create \ +# --function-name "optionFunction" \ +# --type "Option" \ +# --property-variableType "Boolean" \ +# --property-variableName "varName" \ +# --array-alt "--help" \ +# --array-alt "-h" \ +# --array-callback "callback" +# ``` +# +# @exitcode 1 if mandatory parameter missing +# @exitcode 2 if invalid parameter provided +# @stderr diagnostics information is displayed +Options2::validateArgObject() { + if (( $# != 1 )); then + Log::displayError "Options2::validateArgObject - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n validateArgObject=$1 + if [[ "$(Object::getProperty validateArgObject --type)" != "Arg" ]]; then + Log::displayError "Options2::validateArgObject - passed object is not an argument" + return 2 + fi + + # variable name + if ! Object::memberExists validateArgObject --property-variableName; then + Log::displayError "Options2::validateArgObject - variableName is mandatory" + return 1 + fi + local variableName + variableName="$(Object::getProperty validateArgObject --property-variableName)" + if ! Assert::validVariableName "${variableName}"; then + Log::displayError "Options2::validateArgObject - invalid variableName ${variableName}" + return 2 + fi + + # name + if ! Object::memberExists validateArgObject --property-name; then + Log::displayError "Options2::validateArgObject - name is mandatory" + return 1 + fi + local name + name="$(Object::getProperty validateArgObject --property-name)" + if ! Assert::validVariableName "${name}"; then + Log::displayError "Options2::validateArgObject - invalid name ${name}" + return 2 + fi + + # callback + if Object::memberExists validateArgObject --array-callback; then + local callbacks + callbacks="$(Object::getArray validateArgObject --array-callback)" + if [[ -n "${callbacks}" ]]; then + local callback + while IFS= read -r callback ; do + if ! declare -F "${callback}" &>/dev/null; then + Log::displayError "Options2::validateArgObject - callback '${callback}' - function does not exists" + return 2 + fi + done <<< "${callbacks}" + fi + fi + + local authorizedValues + authorizedValues="$(Object::getProperty validateArgObject --property-authorizedValues "strict")" || authorizedValues="" + if [[ "${authorizedValues}" =~ [[:space:]] ]]; then + Log::displayError "Options2::validateArgObject - authorizedValues invalid regexp '${authorizedValues}'" + return 2 + fi + + local regexp + regexp="$(Object::getProperty validateArgObject --property-regexp)" + if [[ "${regexp}" =~ [[:space:]] ]]; then + Log::displayError "Options2::validateArgObject - regexp invalid regexp '${regexp}'" + return 2 + fi + + local helpValueName + helpValueName="$(Object::getProperty validateArgObject --property-helpValueName)" + if [[ -n "${helpValueName}" && ! "${helpValueName}" =~ ^[A-Za-z0-9_-]+$ ]]; then + Log::displayError "Options2::validateArgObject - helpValueName should be a single word '${helpValueName}'" + return 2 + fi + + local min + min="$(Object::getProperty validateArgObject --property-min "strict")" || min="0" + if [[ ! "${min}" =~ ^[0-9]+$ ]]; then + Log::displayError "Options2::validateArgObject - min value should be an integer greater than or equal to 0" + return 2 + fi + + local max + max="$(Object::getProperty validateArgObject --property-max "strict")" || max="-1" + if [[ -n "${max}" && ! "${max}" =~ ^([1-9][0-9]*|-1)$ ]]; then + Log::displayError "Options2::validateArgObject - max value should be an integer greater than 0 or -1" + return 2 + fi + + if ((max != -1 && min > max)); then + Log::displayError "Options2::validateArgObject - max value should be greater than min value" + return 2 + fi +} diff --git a/src/Options2/validateCommandObject.bats b/src/Options2/validateCommandObject.bats new file mode 100755 index 00000000..f3ff75c0 --- /dev/null +++ b/src/Options2/validateCommandObject.bats @@ -0,0 +1,142 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::validateCommandObject::noOption { #@test + run Options2::validateCommandObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateCommandObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateCommandObject::missingValue { #@test + run Options2::validateCommandObject invalidObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateCommandObject - passed object is not a command" + assert_failure 2 +} + +function Options2::validateCommandObject::tooMuchArgs { #@test + declare -a object=( + --type "Command" + ) + run Options2::validateCommandObject object object + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateCommandObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateCommandObject::invalidObjectType { #@test + declare -a object=( + --type "NotACommand" + ) + + run Options2::validateCommandObject object + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateCommandObject - passed object is not a command" + assert_failure 2 +} + +function Options2::validateCommandObject::nameInvalid { #@test + declare -a object=( + --type "Command" + --property-name "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - invalid command name François" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateCommandObject::callbackInvalid { #@test + declare -a object=( + --type "Command" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-callback "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - only function can be passed as callback - invalid 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateCommandObject::unknownOptionCallbackInvalid { #@test + declare -a object=( + --type "Command" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-unknownOptionCallback "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - only function can be passed as callback - invalid 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateCommandObject::unknownArgumentCallbackInvalid { #@test + declare -a object=( + --type "Command" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-unknownArgumentCallback "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - only function can be passed as callback - invalid 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateCommandObject::everyOptionCallbackInvalid { #@test + declare -a object=( + --type "Command" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-everyOptionCallback "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - only function can be passed as callback - invalid 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateCommandObject::everyArgumentCallbackInvalid { #@test + declare -a object=( + --type "Command" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --property-name "valid" + --array-everyArgumentCallback "François" + ) + + run Options2::validateCommandObject object + assert_output --partial "ERROR - Options2::validateCommandObject - only function can be passed as callback - invalid 'François'" + assert_lines_count 1 + assert_failure 2 +} diff --git a/src/Options2/validateCommandObject.sh b/src/Options2/validateCommandObject.sh new file mode 100755 index 00000000..57a734d7 --- /dev/null +++ b/src/Options2/validateCommandObject.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env bash + +# @description Validates command properties created +# using `Object::create` +# +# #### Example +# +# ```bash +# declare Object::create \ +# --function-name "commandFunction" \ +# --type "Option" \ +# --property-variableName "varName" +# +# Options2::validateCommandObject "${commandFunction}" +# ``` +# #### Arg Structure +# +# - --function-name (optional) the name of the function that will be generated +# - --type "Command" +# - --property-name (optional) provides the name of the command (Default: name of current command file without path) +# - --property-shortDescription (optional) provides command short description (Default: Empty string) +# - --property-longDescription (optional) provides command long description (Default: Empty string) +# - --property-version (optional) provides version section help. Section not generated if not provided. (Default: Empty string) +# - --property-author (optional) provides author section help. Section not generated if not provided. +# - --property-license (optional) provides License section. Section not generated if not provided. +# - --property-sourceFile (optional) provides Source file section. Section not generated if not provided. +# - --property-copyright (optional) provides copyright section. Section not generated if not provided. +# +# - --array-unknownOptionCallback (0 or more) the callback called when an option is unknown (Default: options parser display error message if option provided does not match any specified options). +# - --array-unknownArgumentCallback (0 or more) the callback called when an argument is unknown (Default: parser does not report any error). +# - --array-everyOptionCallback (0 or more) the callback called for every option. +# - --array-everyArgumentCallback (0 or more) the callback called for every argument. +# - --array-callback (0 or more) the callback called when all options and arguments have been parsed. +# +# - --array-element (1 or more) list of option/arguments objects +# +# Example: +# ```bash +# Object::create \ +# --function-name command" \ +# --type "Command" \ +# --array-callback "callback" +# Options2::validateCommandObject +# +# @exitcode 1 if mandatory parameter missing +# @exitcode 2 if invalid parameter provided +# @stderr diagnostics information is displayed +Options2::validateCommandObject() { + if (( $# != 1 )); then + Log::displayError "Options2::validateCommandObject - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n cmdInstanceObject=$1 + if [[ "$(Object::getProperty cmdInstanceObject --type)" != "Command" ]]; then + Log::displayError "Options2::validateCommandObject - passed object is not a command" + return 2 + fi + + # name + local name + if Object::memberExists cmdInstanceObject --property-name; then + name="$(Object::getProperty cmdInstanceObject --property-name)" + if ! Assert::validVariableName "${name}"; then + Log::displayError "Options2::validateCommandObject - invalid command name ${name}" + return 2 + fi + fi + + # callback + checkCallbacks() { + local callbackType="$1" + if Object::memberExists cmdInstanceObject "${callbackType}"; then + local callbacks + callbacks="$(Object::getArray cmdInstanceObject "${callbackType}")" + local callback + while IFS= read -r callback ; do + if [[ $(type -t "${callback}") != "function" ]]; then + Log::displayError "Options2::validateCommandObject - only function can be passed as callback - invalid '${callback}'" + return 2 + fi + done <<< "${callbacks}" + fi + } + checkCallbacks --array-unknownOptionCallback || return 2 + checkCallbacks --array-unknownArgumentCallback || return 2 + checkCallbacks --array-everyOptionCallback || return 2 + checkCallbacks --array-everyArgumentCallback || return 2 + checkCallbacks --array-callback || return 2 + + # option/argument list + if ! Object::memberExists cmdInstanceObject --array-element; then + Log::displayError "Options2::validateCommandObject - at least one option or argument must be provided" + return 1 + fi + local -a argumentList=() + local elements + elements="$(Object::getArray cmdInstanceObject --array-element)" + local element + while IFS= read -r element ; do + # check element type is Option or Arg + if [[ ! "$(declare -p "${element}")" =~ declare\ -a ]]; then + Log::displayError "Options2::validateCommandObject - only object can be passed as element - invalid '${element}'" + return 2 + fi + local elementType + elementType="$(Object::getProperty element --type)" + if Array::contains "${elementType}" "Option" "Arg"; then + Log::displayError "Options2::validateOptionObject - Element ${elementType} is not a valid object type" + return 2 + fi + + # check variable name is not used by another option/argument + local variableName + variableName="$(Object::getProperty element --property-variableName "strict")" || { + Log::displayError "Options2::validateOptionObject - ${elementType} ${element} - command variableName failed" + return 1 + } + if Array::contains "${variableName}" "${variableNameList[@]}"; then + Log::displayError "Options2::validateOptionObject - ${elementType} ${element} - variable name ${variableName} is already used by a previous option/argument" + return 2 + fi + variableNameList+=("${variableName}") + + # check alts not duplicated + if [[ "${elementType}" = "Option" ]]; then + local optionAlts + optionAlts="$(Object::getArray element --array-alt)" || { + Log::displayError "Options2::validateOptionObject - Option ${element} - command alt failed" + return 1 + } + local optionAlt + while IFS= read -r optionAlt ; do + if Array::contains "${optionAlt}" "${altList[@]}"; then + Log::displayError "Options2::validateOptionObject - Option ${element} - alt ${optionAlt} is already used by a previous Option" + return 1 + fi + altList+=("${optionAlt}") + done <<< "${optionAlts}" + elif [[ "${elementType}" = "Arg" ]]; then + argumentList+=("${element}") + fi + done <<< "${elements}" + + # check min/max coherence + + # check arguments coherence + local currentArg currentArgMin currentArgMax + local optionalArg="" + for currentArg in "${argumentList[@]}"; do + currentArgMin="$(Object::getProperty currentArg --property-min "strict")" || { + Log::displayError "Options2::validateOptionObject - Argument ${currentArg} - command min failed" + return 1 + } + if [[ ! "${currentArgMin}" =~ ^[0-9]+$ ]]; then + Log::displayError "Options::generateArg - Option --property-min - should be an int >= 0" + return 1 + fi + currentArgMax="$(Object::getProperty currentArg --property-max "strict")" || { + Log::displayError "Options2::validateOptionObject - Argument ${currentArg} - command max failed" + return 1 + } + if [[ ! "${currentArgMax}" =~ ^([0-9]+|-1)$ ]]; then + Log::displayError "Options::generateArg - Option --property-max - should be an int >=0 or -1(infinite)" + return 1 + fi + if ((currentArgMin != currentArgMax)); then + optionalArg="$("${currentArg}" variableName)" + elif [[ -n "${optionalArg}" ]]; then + Log::displayError "Options2::validateOptionObject - variable list argument $("${currentArg}" variableName) after an other variable list argument ${optionalArg}, it would not be possible to discriminate them" + return 1 + fi + done +} diff --git a/src/Options2/validateGroupObject.bats b/src/Options2/validateGroupObject.bats new file mode 100755 index 00000000..3c7355a8 --- /dev/null +++ b/src/Options2/validateGroupObject.bats @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" +# shellcheck source=src/Object/memberExists.sh +source "${srcDir}/Object/memberExists.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::validateGroupObject::noOption { #@test + run Options2::validateGroupObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateGroupObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateGroupObject::invalidObject { #@test + function invalidObject() { + : + } + run Options2::validateGroupObject invalidObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateGroupObject - passed object is not a group" + assert_failure 2 +} + +function Options2::validateGroupObject::notAGroup { #@test + local -a notAGroup=( + --type "NotAGroup" + ) + + run Options2::validateGroupObject notAGroup + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateGroupObject - passed object is not a group" + assert_failure 2 +} + +function Options2::validateGroupObject::missingTitle { #@test + local -a simpleObject=( + --type "Group" + ) + + run Options2::validateGroupObject simpleObject + assert_output --partial "ERROR - Options2::validateGroupObject - title is mandatory" + assert_failure 3 + assert_lines_count 1 +} + +function Options::validateGroupObject::groupOptionValid { #@test + local status=0 + local -a groupObject=( + --type "Group" + --property-title "Global options" + --property-help "help" + ) + run Options2::validateGroupObject groupObject >"${BATS_TEST_TMPDIR}/result" 2>&1 + assert_success + assert_output "" +} diff --git a/src/Options2/validateGroupObject.sh b/src/Options2/validateGroupObject.sh new file mode 100755 index 00000000..ef89d1f0 --- /dev/null +++ b/src/Options2/validateGroupObject.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# @description Generates a function that allows to manipulate a group of options. +# function generated allows group options using `--group` option when +# using `Options::generateOption` +# +# #### Output on stdout +# +# By default the name of the random generated function name +# is displayed as output of this function. +# By providing the option `--function-name`, the output of this +# function will be the generated function itself with the chosen name. +# +# #### Syntax +# +# ```text +# Usage: Options2::validateGroupObject [OPTIONS] +# +# OPTIONS: +# --title +# [--help ] +# [--function-name ] +# ``` +# +# #### Example +# +# ```bash +# declare optionGroup="$( +# Options2::validateGroupObject \ +# --title "Command global options" \ +# --help "The Console component adds some predefined options to all commands:" +# )" +# Options::sourceFunction "${optionGroup}" +# "${optionGroup}" help +# ``` +# +# @option --title (mandatory) provides group title +# @option --help (optional) provides command description help +# @option --function-name (optional) the name of the function that will be generated +# @exitcode 1 if error during option parsing +# @exitcode 1 if bash-tpl error during template rendering +# @exitcode 2 if file generation error (only if functionName argument empty) +# @stderr diagnostics information is displayed +# @see [generateCommand function](#/doc/guides/Options/generateCommand) +# @see [generateOption function](#/doc/guides/Options/generateOption) +# @see [group function](#/doc/guides/Options/functionGroup) +Options2::validateGroupObject() { + if (( $# != 1 )); then + Log::displayError "Options2::validateGroupObject - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n validateGroupObject=$1 + if [[ "$(Object::getProperty validateGroupObject --type)" != "Group" ]]; then + Log::displayError "Options2::validateGroupObject - passed object is not a group" + return 2 + fi + if ! Object::memberExists validateGroupObject --property-title; then + Log::displayError "Options2::validateGroupObject - title is mandatory" + return 3 + fi +} diff --git a/src/Options2/validateOptionObject.bats b/src/Options2/validateOptionObject.bats new file mode 100755 index 00000000..d0b02e35 --- /dev/null +++ b/src/Options2/validateOptionObject.bats @@ -0,0 +1,348 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/Options/_bats.sh +source "${srcDir}/Options/_bats.sh" +# shellcheck source=src/Object/__all.sh +source "${srcDir}/Object/__all.sh" +# shellcheck source=src/Options2/__all.sh +source "${srcDir}/Options2/__all.sh" + +function setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export _COMPILE_ROOT_DIR="${FRAMEWORK_ROOT_DIR}" +} + +function Options2::validateOptionObject::noOption { #@test + run Options2::validateOptionObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateOptionObject::missingValue { #@test + run Options2::validateOptionObject invalidObject + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - passed object is not an option" + assert_failure 2 +} + +function Options2::validateOptionObject::tooMuchArgs { #@test + declare -a object=( + --type "Option" + ) + run Options2::validateOptionObject object object + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - exactly one parameter has to be provided" + assert_failure 1 +} + +function Options2::validateOptionObject::invalidObjectType { #@test + declare -a notAnOption=( + --type "NotAnOption" + ) + + run Options2::validateOptionObject notAnOption + assert_lines_count 1 + assert_output --partial "ERROR - Options2::validateOptionObject - passed object is not an option" + assert_failure 2 +} + +function Options2::validateOptionObject::variableTypeMandatory { #@test + declare -a object=( + --type "Option" + --property-variableName "varName" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - variableType is mandatory" + assert_lines_count 1 + assert_failure 1 +} + +function Options2::validateOptionObject::variableTypeInvalid { #@test + declare -a object=( + --type "Option" + --property-variableType "invalid" + --property-variableName "varName" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - invalid variableType invalid" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateOptionObject::variableNameMandatory { #@test + declare -a object=( + --type "Option" + --property-variableType "String" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - variableName is mandatory" + assert_failure 1 + assert_lines_count 1 +} + +function Options2::validateOptionObject::variableNameInvalid { #@test + declare -a object=( + --type "Option" + --property-variableName "François" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - invalid variableName François" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::missingAltOption { #@test + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - you must provide at least one alt option" + assert_lines_count 1 + assert_failure 1 +} + +function Options2::validateOptionObject::invalidAltValue { #@test + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + --array-alt "François" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - invalid alt option value 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateOptionObject::groupOptionInvalid { #@test + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + --property-group "invalidGroup" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - Group invalidGroup is not a valid group object" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateOptionObject::groupOptionValid { #@test + declare -a group=( + --type "Group" + --property-title "Global options" + --property-help "help" + --function-name "groupObjectFunction" + ) + + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + --property-group group + --array-alt "--help" + ) + + local status=0 + run Options2::validateOptionObject object + assert_output "" + assert_success +} + +function Options2::validateOptionObject::callbackOptionInvalid { #@test + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --array-callback "François" + ) + + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - invalid alt option value 'François'" + assert_lines_count 1 + assert_failure 2 +} + +function Options2::validateOptionObject::callbackOptionValid { #@test + callback() { + : + } + declare -a object=( + --type "Option" + --property-variableName "varName" + --property-variableType "String" + --array-alt "--help" + --array-callback "callback" + ) + run Options2::validateOptionObject object + assert_output "" + assert_success +} + +function Options2::validateOptionObject::Boolean::onValueMissingValue { #@test + declare -a object=( + --type "Option" + --property-variableType "Boolean" + --property-variableName "varName" + --property-onValue "" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - onValue cannot be empty" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::Boolean::offValueMissingValue { #@test + declare -a object=( + --type "Option" + --property-variableType "Boolean" + --property-variableName "varName" + --property-offValue "" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - offValue cannot be empty" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::Boolean::onOffSameValue { #@test + declare -a object=( + --type "Option" + --property-variableType "Boolean" + --property-variableName "varName" + --property-offValue "1" + --property-onValue "1" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - onValue and offValue cannot be equal" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::String::authorizedValuesValueInvalidValue { #@test + declare -a object=( + --type "Option" + --property-variableType "String" + --property-variableName "varName" + --property-authorizedValues " invalid | valid" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - authorizedValues invalid regexp ' invalid | valid'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::authorizedValuesValueInvalidValue { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-authorizedValues " invalid | valid" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - authorizedValues invalid regexp ' invalid | valid'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::String::helpValueNameInvalidOption { #@test + declare -a object=( + --type "Option" + --property-variableType "String" + --property-variableName "varName" + --property-helpValueName "invalid help" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - helpValueName should be a single word 'invalid help'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::helpValueNameInvalidOption { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-helpValueName "invalid help" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - helpValueName should be a single word 'invalid help'" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::minValueEmpty { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-min "" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::minValueInvalid { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-min "François" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::minValueLessThan0 { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-min "-1" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - min value should be an integer greater than or equal to 0" + assert_failure 2 + assert_lines_count 1 +} + +function Options2::validateOptionObject::StringArray::minValueGreaterThanMaxValue { #@test + declare -a object=( + --type "Option" + --property-variableType "StringArray" + --property-variableName "varName" + --property-min "3" + --property-max "1" + --array-alt "--help" + ) + run Options2::validateOptionObject object + assert_output --partial "ERROR - Options2::validateOptionObject - max value should be greater than min value" + assert_failure 2 + assert_lines_count 1 +} diff --git a/src/Options2/validateOptionObject.sh b/src/Options2/validateOptionObject.sh new file mode 100755 index 00000000..4df243f9 --- /dev/null +++ b/src/Options2/validateOptionObject.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash + +# @description Validates options properties created +# using `Object::create` +# +# #### Example +# +# ```bash +# declare Object::create \ +# --function-name "optionFunction" \ +# --type "Option" \ +# --property-variableName "varName" +# +# Options2::validateOptionObject "${optionFunction}" +# ``` +# #### Option Common Structure +# +# - --function-name (optional) the name of the function that will be generated +# - --type "Option" +# - --property-variableType (optional) option type (default: Boolean) +# - --property-variableName | --var (mandatory) provides the variable name that will be used to store the parsed options. +# - --property-mandatory (optional) as its name indicates, by default an option is optional. But using `--mandatory` you can make the option mandatory. An error will be generated if the option is not found during parsing arguments. +# - --property-help (optional) provides option help description (Default: Empty string) +# - --property-group (optional) the group to which the option will be attached. Grouped option will be displayed under that group. (Default: no group) +# - --array-alt (mandatory at least one) option name possibility, the string allowing to discriminate the option. +# - --array-callback (0 or several times) the callback called if the option is parsed successfully. The option value will be passed as parameter (several parameters if type StringArray). +# - --* (optional) Others options are passed to specific option handler depending on variable type +# +# #### Option Boolean +# +# Structure: +# +# - --property-offValue (optional) value set by default on the variable (Default: 0) +# - --property-onValue (optional) value set on the variable if option provided (Default: 1) +# +# #### Option String/StringArray +# +# Common Structure to String/StringArray: +# +# - --property-authorizedValues (optional) Indicates the possible value list separated by | character (Default: "" means no check) +# - --property-helpValueName (optional) Indicates the name of value of the option to display in help (Default: "String") +# - --property-mandatory (optional) if 1 then will set min value to 1 (except if min is already set) (Default: "0") +# +# Structure specific to String only +# +# - --property-defaultValue (optional) value set by default on the variable (Default: "") +# +# Structure specific to StringArray only +# +# - --property-min (optional) minimum number of options to provide (Defaults to 0 or 1 if mandatory). +# - --property-max (optional) maximum number of options to provide (Default "" means no limit) +# +# Example: +# ```bash +# Object::create \ +# --function-name "optionFunction" \ +# --type "Option" \ +# --property-variableType "Boolean" \ +# --property-variableName "varName" \ +# --array-alt "--help" \ +# --array-alt "-h" \ +# --array-callback "callback" +# ``` +# +# @exitcode 1 if mandatory parameter missing +# @exitcode 2 if invalid parameter provided +# @stderr diagnostics information is displayed +Options2::validateOptionObject() { + if (( $# != 1 )); then + Log::displayError "Options2::validateOptionObject - exactly one parameter has to be provided" + return 1 + fi + + # shellcheck disable=SC2034 + local -n validateOptionObject=$1 + if [[ "$(Object::getProperty validateOptionObject --type)" != "Option" ]]; then + Log::displayError "Options2::validateOptionObject - passed object is not an option" + return 2 + fi + + # variable name + if ! Object::memberExists validateOptionObject --property-variableName; then + Log::displayError "Options2::validateOptionObject - variableName is mandatory" + return 1 + fi + local variableName + variableName="$(Object::getProperty validateOptionObject --property-variableName)" + if ! Assert::validVariableName "${variableName}"; then + Log::displayError "Options2::validateOptionObject - invalid variableName ${variableName}" + return 2 + fi + + # variable type + if ! Object::memberExists validateOptionObject --property-variableType; then + Log::displayError "Options2::validateOptionObject - variableType is mandatory" + return 1 + fi + local variableType + variableType="$(Object::getProperty validateOptionObject --property-variableType)" + if ! Array::contains "${variableType}" "Boolean" "String" "StringArray"; then + Log::displayError "Options2::validateOptionObject - invalid variableType ${variableType}" + return 2 + fi + + # group + local groupObject + groupObject="$(Object::getProperty validateOptionObject --property-group)" + if [[ -n "${groupObject}" ]]; then + if [[ "$(Object::getProperty "${groupObject}" --type)" != "Group" ]]; then + Log::displayError "Options2::validateOptionObject - Group ${groupObject} is not a valid group object" + return 2 + fi + fi + + # alts + if ! Object::memberExists validateOptionObject --array-alt; then + Log::displayError "Options2::validateOptionObject - you must provide at least one alt option" + return 1 + fi + local alts + alts="$(Object::getArray validateOptionObject --array-alt)" + local alt + while IFS= read -r alt ; do + if ! Options::assertAlt "${alt}"; then + Log::displayError "Options2::validateOptionObject - invalid alt option value '${alt}'" + return 2 + fi + done <<< "${alts}" + + # callback + if Object::memberExists validateOptionObject --array-callback; then + local callbacks + callbacks="$(Object::getProperty validateOptionObject --array-callback)" + local callback + while IFS= read -r callback ; do + if ! declare -F "${callback}" &>/dev/null; then + Log::displayError "Options2::validateOptionObject - callback '${callback}' - function does not exists" + return 2 + fi + done <<< "${callbacks}" + fi + + if [[ "${variableType}" = "Boolean" ]]; then + local onValue + onValue="$(Object::getProperty validateOptionObject --property-onValue "strict")" || onValue="1" + if [[ -z "${onValue}" ]]; then + Log::displayError "Options2::validateOptionObject - onValue cannot be empty" + return 2 + fi + local offValue + offValue="$(Object::getProperty validateOptionObject --property-offValue "strict")" || offValue="0" + if [[ -z "${offValue}" ]]; then + Log::displayError "Options2::validateOptionObject - offValue cannot be empty" + return 2 + fi + if [[ "${onValue}" = "${offValue}" ]]; then + Log::displayError "Options2::validateOptionObject - onValue and offValue cannot be equal" + return 2 + fi + elif [[ "${variableType}" = "String" || "${variableType}" = "StringArray" ]]; then + local authorizedValues + authorizedValues="$(Object::getProperty validateOptionObject --property-authorizedValues "strict")" || authorizedValues="" + if [[ "${authorizedValues}" =~ [[:space:]] ]]; then + Log::displayError "Options2::validateOptionObject - authorizedValues invalid regexp '${authorizedValues}'" + return 2 + fi + + local helpValueName + helpValueName="$(Object::getProperty validateOptionObject --property-helpValueName "strict")" || helpValueName="" + if [[ -n "${helpValueName}" && ! "${helpValueName}" =~ ^[A-Za-z0-9_-]+$ ]]; then + Log::displayError "Options2::validateOptionObject - helpValueName should be a single word '${helpValueName}'" + return 2 + fi + + if [[ "${variableType}" = "StringArray" ]]; then + + local min + min="$(Object::getProperty validateOptionObject --property-min "strict")" || min="0" + if [[ ! "${min}" =~ ^[0-9]+$ ]]; then + Log::displayError "Options2::validateOptionObject - min value should be an integer greater than or equal to 0" + return 2 + fi + + local max + max="$(Object::getProperty validateOptionObject --property-max "strict")" || max="-1" + if [[ -n "${max}" && ! "${max}" =~ ^([1-9][0-9]*|-1)$ ]]; then + Log::displayError "Options2::validateOptionObject - max value should be an integer greater than 0 or -1" + return 2 + fi + + if ((max != -1 && min > max)); then + Log::displayError "Options2::validateOptionObject - max value should be greater than min value" + return 2 + fi + fi + + fi +}