diff --git a/docs/README-option b/docs/README-option new file mode 100644 index 0000000..50df4f4 --- /dev/null +++ b/docs/README-option @@ -0,0 +1,81 @@ +# Options Handling. + +Set of scripts to handle script options/parameters. + +## Manage Options: text mode. + +To create an option menu: + +* Import the option script: + + import util/option + +* Create an option menu and an option menu wrapper. + + Options optionMenu + OptionsWrapper optionMenuWrapper + +* Create a default values array using index with the format: + + [optionName,optionValue,optionLetter,optionFlag,optionRequired] + + declare -A DEFAULTS + DEFAULTS[one,1,a,false,true]='' + DEFAULTS[two,2,b,false,false]='' + DEFAULTS[verbose,false,v,true,false]='' + + Where each index part is: + + * optionName: name of the option. Example: username. + + * optionValue: the value to set as default. Example: 777. + + * optionLetter: letter used on text mode to especify an argument. Example: a. + + * optionFlag: true or false value indicating if this option is a flag. Flag options can only take + true or false values, are marked as 'without argument' on the string passed to getopts + and are displayed as checkboxes on the GUI. + + * optionRequired: true or false value indicating if this option is required. This attribute is + used on the GUI to verify if the user left a required option empty. + +* Set the default values: + + Options optionMenu=$($var:optionMenuWrapper SetDefaults optionMenu DEFAULTS) + +* Now you can use Option functions: + + declare -A PARAMETERS=$($var:optionMenuSample ToArray) + echo "${PARAMETERS[one]}" + echo "${PARAMETERS[two]}" + +* You can also add options manually: + + Option optionA + $var:optionA name = 'one' + $var:optionA value = 1 + $var:optionA letter = 'a' + $var:optionA flag = false + $var:optionA required = true + + $var:optionMenu Set optionA + +## Manage Options: graphical user interface mode. + +* Create the defaults as above and then call yad function: + + Options optionMenu=$($var:optionMenuWrapper GetOptionsGUI optionMenu) + +## Manage Options: fast text mode. + +* Create the arrays DEFAULTS and OPTIONS same as above and then call the call the fast get arguments funtion: + + Options::FastParseArguments DEFAULTS OPTIONS "$@" + + +## Manage Options: fast GUI mode. + +* Create the arrays DEFAULTS and OPTIONS same as above and then call the call the fast get arguments funtion: + + Options::FastGetOptionsGUI DEFAULTS OPTIONS + diff --git a/docs/README-option.md b/docs/README-option.md new file mode 100644 index 0000000..96cc1d5 --- /dev/null +++ b/docs/README-option.md @@ -0,0 +1,69 @@ +# Options Handling. + +Set of scripts to handle script options/parameters. + +## Manage Options: text mode. + +To create an option menu: + +* Import the option script: + ```python + import util/option + ``` + +* Create an option menu and an option menu wrapper: + ```python + Options optionMenu + OptionsWrapper optionMenuWrapper + ``` + +* Create a default values array using index with the format: +[optionName,optionValue,optionLetter,optionFlag,optionRequired] + ```python + declare -A DEFAULTS + DEFAULTS[one,1,a,false,true]='' + DEFAULTS[two,2,b,false,false]='' + DEFAULTS[verbose,false,v,true,false]='' + ``` + +* Set the default values: +```javascript + Options optionMenu=$($var:optionMenuWrapper SetDefaults optionMenu DEFAULTS) + ``` + + * Now you can use Option functions: +```javascript + declare -A OPTIONS=$($var:optionMenuSample ToArray) + echo "${OPTIONS[one]}" + echo "${OPTIONS[two]}" + ``` +* You can also add options manually: + ```javascript + Option optionA + $var:optionA name = 'one' + $var:optionA value = 1 + $var:optionA letter = 'a' + $var:optionA flag = false + $var:optionA required = true + $var:optionMenu Set optionA + ``` +## Manage Options: graphical user interface. + +* Create the defaults as above and then call yad function: + ```javascript + Options optionMenu=$($var:optionMenuWrapper GetOptionsGUI optionMenu) + ``` +## Manage Options: fast text mode. + +* Create the arrays DEFAULTS and OPTIONS same as above and then call the call the fast get arguments funtion: + ```javascript + Options::FastParseArguments DEFAULTS OPTIONS "$@" + ``` + +## Manage Options: fast GUI mode. + +* Create the arrays DEFAULTS and OPTIONS same as above and then call the call the fast get arguments funtion: + ```javascript + Options::FastGetOptionsGUI DEFAULTS OPTIONS + ``` + diff --git a/example/option.sh b/example/option.sh new file mode 100755 index 0000000..6935d11 --- /dev/null +++ b/example/option.sh @@ -0,0 +1,190 @@ +#!/bin/bash +# +# Example Option, Options and OptionsWrapper objects use. + +source "$( cd "${BASH_SOURCE[0]%/*}" && cd .. && pwd )/lib/oo-bootstrap.sh" + +import util/option + +OptionsWrapper optionMenuWrapper +Options optionMenu + +echo 'Set default values manually.' + +Option optionA +$var:optionA name = 'one' +$var:optionA value = 1 +$var:optionA letter = 'a' +$var:optionA flag = false +$var:optionA required = true + +Option optionB +$var:optionB name = 'two' +$var:optionB value = 2 +$var:optionB letter = 'b' +$var:optionB flag = false +$var:optionB required = false + +Option optionVerbose +$var:optionVerbose name = 'verbose' +$var:optionVerbose value = false +$var:optionVerbose letter = 'v' +$var:optionVerbose flag = true +$var:optionVerbose required = false + +# Add options. +$var:optionMenu Set optionA +$var:optionMenu Set optionB +$var:optionMenu Set optionVerbose + +$var:optionMenu +echo '----------------' + +echo 'Set default values from array.' +# Set defaults index as: [name,value,letter,flag,required] +declare -A DEFAULTS +DEFAULTS[one,1,a,false,true]='' +DEFAULTS[two,2,b,false,false]='' +DEFAULTS[three,3,c,false,false]='' +DEFAULTS[verbose,false,v,true,false]='' + +Options optionMenu=$($var:optionMenuWrapper SetDefaults optionMenu DEFAULTS) +$var:optionMenu +echo '----------------' + +echo 'Serialize all options.' +$var:optionMenu +echo '----------------' + +echo 'Delete option.' +$var:optionMenu Delete optionB +$var:optionMenu +echo '----------------' + +echo 'Serialize option.' +serializedOption=$($var:optionA) +echo "$serializedOption" +echo '----------------' + +echo 'Get attributes from serialized string.' +Options::GetSerializedAttribute "$serializedOption" 'name' +Options::GetSerializedAttribute "$serializedOption" 'value' +echo '----------------' + +echo 'Unserialize option.' +Option optionC +Options::Unserialize "$serializedOption" $ref:optionC +$var:optionC name +$var:optionC value +echo '----------------' + +echo 'Search option by name.' +Option optionC=$($var:optionMenu Search 'name' 'verbose') +$var:optionC +echo '----------------' + +echo 'Search option by letter.' +Option optionC=$($var:optionMenu Search 'letter' 'a') +$var:optionC +echo '----------------' + +echo 'Get options string.' +$var:optionMenu GetOptionsString +echo '----------------' + +# Simulate call the script like: ./option.sh -a 777 +echo 'Parse arguments -a 777' +set -- "${@:1:2}" '-a 777' +Options optionMenu=$($var:optionMenuWrapper ParseArguments optionMenu "$@") +$var:optionMenu +echo '----------------' + +echo 'Copy values from Object to associative array.' +declare -A OPTIONS=$($var:optionMenu ToArray) +echo "${OPTIONS[@]}" +echo '----------------' + +echo 'Basic use: read defaults from array, write results to associative array.' + +echo ' DEFAULTS[one,1,a,false,true] > Options Object' +echo ' DEFAULTS[two,2,b,false,false] > Options Object' + +echo ' Option Object > PARAMETERS[one]="1"' +echo ' Option Object > PARAMETERS[two]="2"' + +Options optionMenuSample +OptionsWrapper optionMenuWrapperSample + +# Name,value,letter,flag,required. +declare -A DEFAULTS_SAMPLE +DEFAULTS_SAMPLE[one,1,a,false,true]='' +DEFAULTS_SAMPLE[two,2,b,false,false]='' +Options optionMenuSample=$($var:optionMenuWrapperSample SetDefaults optionMenuSample DEFAULTS_SAMPLE) + +declare -A OPTIONS_SAMPLE=$($var:optionMenuSample ToArray) + +for index in "${!OPTIONS_SAMPLE[@]}"; do + echo "$index : ${OPTIONS_SAMPLE[$index]}" +done + +echo "${OPTIONS_SAMPLE[one]}" +echo "${OPTIONS_SAMPLE[two]}" +echo '----------------' + +echo 'Graphical User Interface with yad.' +Options optionMenuYad +OptionsWrapper optionMenuWrapperYad + +# Name,value,letter,flag,required. +declare -A DEFAULTS_YAD +DEFAULTS_YAD[one,1,a,false,true]='' +DEFAULTS_YAD[two,2,b,false,false]='' +DEFAULTS_YAD[verbose,false,v,true,false]='' + +Options optionMenuYad=$($var:optionMenuWrapperYad SetDefaults optionMenuYad DEFAULTS_YAD) +Options optionMenuYad=$($var:optionMenuWrapperYad GetOptionsGUI optionMenuYad) + +if [[ "$($var:optionMenuYad yadSuccess)" == true ]]; then + $var:optionMenuYad +else + echo "An error happend with yad." +fi +echo '----------------' + +echo 'Fast parse arguments.' + +# Name,value,letter,flag,required. +declare -A DEFAULTS_FAST +DEFAULTS_FAST[one,1,a,false,true]='' +DEFAULTS_FAST[two,2,b,false,false]='' +DEFAULTS_FAST[verbose,false,v,true,false]='' + +declare -A OPTIONS_FAST + +set -- "${@:1:2}" '-a 777' +Options::FastParseArguments DEFAULTS_FAST OPTIONS_FAST "$@" + +echo "${!OPTIONS_FAST[@]}" +echo "${OPTIONS_FAST[@]}" + +echo '----------------' + +echo 'Fast get options GUI.' + +# Name,value,letter,flag,required. +declare -A DEFAULTS_FAST_GUI +DEFAULTS_FAST_GUI[one,1,a,false,true]='' +DEFAULTS_FAST_GUI[two,2,b,false,false]='' +DEFAULTS_FAST_GUI[verbose,false,v,true,false]='' + +declare -A OPTIONS_FAST_GUI + +Options::FastGetOptionsGUI DEFAULTS_FAST_GUI OPTIONS_FAST_GUI + +echo "${!OPTIONS_FAST_GUI[@]}" +echo "${OPTIONS_FAST_GUI[@]}" + +echo '----------------' + +exit 0 + diff --git a/lib/util/option.sh b/lib/util/option.sh new file mode 100644 index 0000000..8b3997e --- /dev/null +++ b/lib/util/option.sh @@ -0,0 +1,613 @@ +import util/class util/namedParameters +import util/tryCatch + +class:Option() { + + public string name + public string value + public string letter + public string flag + public string required + + Option.__getter__() { + serializedOption="{\"name\":\"$(this name)\",\"value\":\"$(this value)\",\"letter\":\"$(this letter)\",\"flag\":\"$(this flag)\",\"required\":\"$(this required)\"}" + @return:value $serializedOption + } + +} + +Type::Initialize Option + +class:Options() { + + public map optionsMap + public string yadSuccess + + Options.ToArray() { + map toSetOptionsArray + string indexList=$(this optionsMap) + indexList=$($var:indexList sanitizeJSON) + string serializedOption + + for serializedOption in $indexList; do + serializedOption=$($var:serializedOption sanitizeSingleJSON) + optionName=$(Options::GetSerializedAttribute "$serializedOption" 'name') + optionValue=$(Options::GetSerializedAttribute "$serializedOption" 'value') + toSetOptionsArray[$optionName]=$optionValue + done + @return toSetOptionsArray + } + + Options.__getter__() { + string indexList=$(this optionsMap) + @return:value "$($var:indexList sanitizeJSON)" + } + + Options.Set() { + [reference] toSet + this optionsMap set "$($var:toSet name)" "$($var:toSet)" + } + + Options.Delete () { + [reference] toDelete + this optionsMap delete "$($var:toDelete name)" + } + + Options::GetSerializedAttribute() { + [string] serializedOption + [string] attributeName + attributeValue='' + regex="$attributeName\":\"([[:alnum:]]+)" + [[ $serializedOption =~ $regex ]] && attributeValue="${BASH_REMATCH[1]}" + echo "$attributeValue" + } + + Options::Unserialize() { + [string] serializedOption + [reference] toReturn + serializedOption=$($var:serializedOption sanitizeSingleJSON) + $var:toReturn name = $(Options::GetSerializedAttribute "$serializedOption" 'name') + $var:toReturn value = $(Options::GetSerializedAttribute "$serializedOption" 'value') + $var:toReturn letter = $(Options::GetSerializedAttribute "$serializedOption" 'letter') + $var:toReturn flag = $(Options::GetSerializedAttribute "$serializedOption" 'flag') + $var:toReturn required = $(Options::GetSerializedAttribute "$serializedOption" 'required') + } + + Options.Search() { + [string] attributeName + [string] textToSearch + Option optionFound + string serializedOption + string indexList=$(this optionsMap) + indexList=$($var:indexList sanitizeJSON) + itemFound=false + + for serializedOption in $indexList; do + serializedOption=$($var:serializedOption sanitizeSingleJSON) + attributeValue=$(Options::GetSerializedAttribute "$serializedOption" "$attributeName") + if [[ "$attributeValue" == "$textToSearch" ]]; then + itemFound=true + break + fi + done + + [[ "$itemFound" == false ]] && return 1 + Options::Unserialize "$serializedOption" $ref:optionFound + @return optionFound + } + + Options.GetOptionsString() { + optionsString='' + optionLetter='' + optionFlag=false + string serializedOption + string indexList=$(this optionsMap) + indexList=$($var:indexList sanitizeJSON) + + for serializedOption in $indexList; do + serializedOption=$($var:serializedOption sanitizeSingleJSON) + optionLetter=$(Options::GetSerializedAttribute "$serializedOption" 'letter') + optionFlag=$(Options::GetSerializedAttribute "$serializedOption" 'flag') + + optionsString+=$optionLetter + if [[ "$optionFlag" == true ]]; then + optionsString+=',' + else + optionsString+=':' + fi + done + @return:value $optionsString + } + + ################################################################ + # Creates an options menu array from defaults values array and + # the arguments string $@. + # Unlike OptionsWrapper.ParseArguments this function does not + # uses objects, this with the purpose of improve performance. + # Arguments: + # sourceDefaults: default values array, + # it must have indexes with the format: + # name,value,letter,flag,required. + # destinyOptions: array where to store options. + # Globals: + # $@: string arguments passed to this script. + # Returns: + # The options menu in the destiny array name. + ################################################################ + Options::FastParseArguments() { + sourceDefaults=$1 + destinyOptions=$2 + optionsString='' + optionName='' + optionValue='' + optionLetter='' + optionFlag=false + local -a 'valuesIndex=("${!'"$sourceDefaults"'[*]}")' + + # Set default values and create options string. + for valueIndex in $valuesIndex; do + attributesArray=($valueIndex) + DEFAULT_IFS="$IFS" + IFS="," + attributeItemArray=($attributesArray) + IFS="$DEFAULT_IFS" + + optionName="${attributeItemArray[0]}" + optionValue="${attributeItemArray[1]}" + optionLetter="${attributeItemArray[2]}" + optionFlag="${attributeItemArray[3]}" + optionsString+="$optionLetter" + + # If option is a flag then an argument is not required. + if [[ "$optionFlag" == true ]]; then + optionsString+=',' + else + optionsString+=':' + fi + eval $destinyOptions[$optionName]="$(echo $optionValue)" + done + + shift 2 + while getopts $optionsString opt; do + try { + ! [[ "$opt" == "?" ]] + } catch { + echo "Ilegal option '$opt'." + return 1 + } + + for valueIndex in $valuesIndex; do + letterRegex=',([[:alpha:]]{1}),' + + if [[ $valueIndex =~ $letterRegex ]]; then + optionLetter="${BASH_REMATCH[1]}" + if [[ "$optionLetter" == "$opt" ]]; then + attributesArray=($valueIndex) + DEFAULT_IFS="$IFS" + IFS="," + attributeItemArray=($attributesArray) + optionName="${attributeItemArray[0]}" + optionValue="${attributeItemArray[1]}" + optionFlag="${attributeItemArray[3]}" + optionsString+="$opt" + IFS="$DEFAULT_IFS" + + # If option is a flag then value is always true (present). + if [[ "$optionFlag" == true ]]; then + optionValue=true + else + optionValue="${OPTARG}" + fi + + # Overwrite default value. + eval $destinyOptions[$optionName]="$(echo $optionValue)" + # Option found. + break + fi + fi + done + done + return 0 + } + + ################################################################ + # Shows a GUI with yad to capture option values. + # Unlike OptionsWrapper.GetOptionsGUI this function does not + # uses objects, this with the purpose of improve performance. + # Arguments: + # sourceDefaults: default values array, + # it must have indexes with the format: + # name,value,letter,flag,required. + # destinyOptions: array where to store options. + # Returns: + # The options menu in the destiny array name. + ################################################################ + Options::FastGetOptionsGUI() { + sourceDefaults=$1 + destinyOptions=$2 + local -a 'valuesIndex=("${!'"$sourceDefaults"'[*]}")' + optionName='' + optionValue='' + optionFlag=false + optionRequired=false + + yadInstalled=$(which 'yad') + if [[ -z "$yadInstalled" ]]; then + return 1 + fi + + # Yad form. + yadString='yad --form --title="Set options" ' + yadOptionsString='' + yadFlagsString='' + yadOption='' + yadLabels='' + + # Save index names to manipulate options array later. + optionsNamesList='' + flagsNamesList='' + + # Set defaults and create yad form string. + for valueIndex in $valuesIndex; do + attributesArray=($valueIndex) + DEFAULT_IFS="$IFS" + IFS="," + attributeItemArray=($attributesArray) + IFS="$DEFAULT_IFS" + + optionName="${attributeItemArray[0]}" + optionValue="${attributeItemArray[1]}" + optionFlag="${attributeItemArray[3]}" + + # Set default value. + eval $destinyOptions[$optionName]="$(echo $optionValue)" + + # When executing yad with eval, the text with the form: --field="${option[value]}" + # does not get well parsed, to prevent that, add to the yad string the keywords 'name:' and 'value' + # and then replace them with the actual values. + yadOption='--field="name": value ' + yadOption=${yadOption/name/$optionName} + + # Set the type to checkbox. + if [[ "$optionFlag" == true ]]; then + yadOption=${yadOption/ value/CHK $optionValue} + yadFlagsString+=$yadOption + flagsNamesList+="$optionName " + + else + yadOption=${yadOption/value/$optionValue} + yadOptionsString+=$yadOption + optionsNamesList+="$optionName " + fi + done + + # Show menu. + yadString+=${yadOptionsString}${yadFlagsString} + yadInput=$(eval $yadString) + + string optionsLabelsList="${optionsNamesList}${flagsNamesList}" + + optionsLabelsList=$($var:optionsLabelsList trim) + yadLabels=($optionsLabelsList) + + # Read the values and store them on the options map. + index=0 + DEFAULT_IFS="$IFS" + IFS='|' read -ra input <<< "$yadInput" + for optionValue in "${input[@]}"; do + IFS="$DEFAULT_IFS" + optionName="${yadLabels[$index]}" + + # Find option to check if is required. + nameValueRegex="$optionName,.*,.*,.*,([[:alpha:]]+)" + for valueIndex in $valuesIndex; do + + if [[ $valueIndex =~ $nameValueRegex ]]; then + optionRequired="${BASH_REMATCH[1]}" + if [[ "$optionRequired" == true ]] && [[ -z "$optionValue" ]]; then + echo "The option '$optionName' is required, process aborted." | yad --text-info --width=400 --height=200 + return 1 + fi + fi + done + + # Rewrite default. + eval $destinyOptions[$optionName]="$(echo '$optionValue')" + ((index++)) + IFS='|' + done + IFS="$DEFAULT_IFS" + return 0 + } + +} + +Type::Initialize Options + +class:OptionsWrapper() { + + OptionsWrapper.SetDefaults() { + [reference] toSaveDefaultOptions + [reference] defaultOptions + string serializedOption + string indexList="$($var:defaultOptions)" + indexList=$($var:indexList unJsonfy) + Option toDefault + optionName='' + optionValue='' + optionLetter='' + optionFlag=false + optionRequired=false + + DEFAULT_IFS="$IFS" + IFS='|' + for attributeList in $indexList; do + IFS=',' + attributesArray=($attributeList) + + IFS=$DEFAULT_IFS + $var:toDefault name = "${attributesArray[0]}" + $var:toDefault value = "${attributesArray[1]}" + $var:toDefault letter = "${attributesArray[2]}" + $var:toDefault flag = "${attributesArray[3]}" + $var:toDefault required = "${attributesArray[4]}" + $var:toSaveDefaultOptions Set toDefault + done + IFS="$DEFAULT_IFS" + @return toSaveDefaultOptions + } + + OptionsWrapper.ParseArguments() { + [reference] toSaveParsedOptions + optionsString=$($var:toSaveParsedOptions GetOptionsString) + string optionValue='' + optionName='' + optionFlag=false + + shift + while getopts $optionsString opt; do + try { + ! [[ "$opt" == "?" ]] + } catch { + echo "Ilegal option '$opt'." + return 1 + } + + Option parsedOption=$($var:toSaveParsedOptions Search 'letter' "$opt") + optionFlag=$($var:parsedOption flag) + + optionValue=true + [[ "$optionFlag" == false ]] && optionValue="${OPTARG}" + + optionValue=$($var:optionValue trim) + $var:parsedOption value = "$optionValue" + $var:toSaveParsedOptions Set parsedOption + + done + @return toSaveParsedOptions + } + + OptionsWrapper.GetOptionsGUI() { + [reference] toSaveOptionsGUI + string serializedOption + Option guiOption + string indexList="$($var:toSaveOptionsGUI optionsMap)" + indexList=$($var:indexList sanitizeJSON) + optionName='' + optionValue='' + optionFlag=false + $var:toSaveOptionsGUI yadSuccess = true + + yadInstalled=$(which 'yad') + if [[ -z "$yadInstalled" ]]; then + $var:toSaveOptionsGUI yadSuccess = false + fi + + # Generate yad form. + yadString='yad --form --title="Set options" ' + yadOptionsString='' + yadFlagsString='' + yadOption='' + yadLabels='' + + # Save index names to manipulate options array later. + optionsNamesList='' + flagsNamesList='' + + for serializedOption in $indexList; do + serializedOption=$($var:serializedOption sanitizeSingleJSON) + optionName=$(Options::GetSerializedAttribute "$serializedOption" 'name') + optionValue=$(Options::GetSerializedAttribute "$serializedOption" 'value') + optionFlag=$(Options::GetSerializedAttribute "$serializedOption" 'flag') + + # When executing yad with eval, the text with the form: --field="${option[value]}" + # does not get well parsed, to prevent that, add to the yad string the keywords 'name:' and 'value' + # and then replace them with the actual values. + yadOption='--field="name": value ' + yadOption=${yadOption/name/$optionName} + + # Set the type to checkbox. + if [[ "$optionFlag" == true ]]; then + yadOption=${yadOption/ value/CHK $optionValue} + yadFlagsString+=$yadOption + flagsNamesList+="$optionName " + + else + yadOption=${yadOption/value/$optionValue} + yadOptionsString+=$yadOption + optionsNamesList+="$optionName " + fi + done + + # Show menu. + yadString+=${yadOptionsString}${yadFlagsString} + yadInput=$(eval $yadString) + + string optionsLabelsList="${optionsNamesList}${flagsNamesList}" + + optionsLabelsList=$($var:optionsLabelsList trim) + yadLabels=($optionsLabelsList) + + # Read the values and store them on the options map. + index=0 + DEFAULT_IFS="$IFS" + IFS='|' read -ra input <<< "$yadInput" + for value in "${input[@]}"; do + IFS="$DEFAULT_IFS" + optionName="${yadLabels[$index]}" + optionValue="$value" + + Option guiOption=$($var:toSaveOptionsGUI Search 'name' "$optionName") + + # If required value must be provided. + optionRequired=$($var:guiOption required) + if [[ "$optionRequired" == true ]]; then + try { + ! [[ -z "$optionValue" ]] + } catch { + echo "The option '$optionName' is required, process aborted." | yad --text-info --width=400 --height=200 + $var:toSaveOptionsGUI yadSuccess = false + } + fi + + $var:guiOption value = "$optionValue" + + $var:toSaveOptionsGUI Set guiOption + ((index++)) + IFS='|' + done + IFS="$DEFAULT_IFS" + @return toSaveOptionsGUI + } +} + +Type::Initialize OptionsWrapper + +# Sanitize to a maximun of two levels only. +string.sanitizeJSON() { + @resolve:this + local toSanitize="$this" + local sanitizedJSONString='' + + # Trim. + toSanitize=$(var: toSanitize trim) + + # Remove \". + toSanitize="${toSanitize//\\\"/}" + # Remove ". + toSanitize="${toSanitize//\"/}" + # Remove [. + toSanitize="${toSanitize//\[/}" + # Replace ]= with :. + toSanitize="${toSanitize//\]=/:}" + # Replace ( with {. + toSanitize="${toSanitize//\(/{}" + # Replace ) with }. + toSanitize="${toSanitize//\)/}}" + # Replace space} with }. + toSanitize="${toSanitize//\ \}/}}" + # Replace }space with }. + toSanitize="${toSanitize//\}/\}}" + # Replace }space with }|. + toSanitize="${toSanitize//\} /\}\|}" + + # Sanitize json object by object. + DEFAULT_IFS="$IFS" + IFS="|" + for jsonItem in $toSanitize; do + jsonItem=$(var: jsonItem sanitizeSingleJSON) + sanitizedJSONString+=$jsonItem + sanitizedJSONString+=' ,' + done + IFS="$DEFAULT_IFS" + sanitizedJSONString="${sanitizedJSONString::-2}" + + # Verify if this is a single json option. + # If not, put {}. + openingBracketsCount=$(echo $sanitizedJSONString | grep -o '{' | wc -l) + [[ $openingBracketsCount -gt 1 ]] && sanitizedJSONString="{${sanitizedJSONString}}" + + @return sanitizedJSONString +} + +string.sanitizeSingleJSON() { + @resolve:this + local toSanitizeSingle="$this" + regex='.*(\{.*\})' + + # Verify this is a single JSON option. + openingBracketsCount=$(echo $toSanitizeSingle | grep -o '{' | wc -l) + [[ $openingBracketsCount -gt 2 ]] && @return toSanitizeSingle + + # Remove \". + toSanitizeSingle="${toSanitizeSingle//\\\"/}" + # Remove ". + toSanitizeSingle="${toSanitizeSingle//\"/}" + + # Replace double {{ }} with single { }. + toSanitizeSingle="${toSanitizeSingle//\{\{/\{}" + toSanitizeSingle="${toSanitizeSingle//\}\}/\}}" + + [[ $toSanitizeSingle =~ $regex ]] && toSanitizeSingle="${BASH_REMATCH[1]}" + + # Remove all { }. + toSanitizeSingle="${toSanitizeSingle//\{/}" + toSanitizeSingle="${toSanitizeSingle//\}/}" + + # Put " back. + toSanitizeSingle="${toSanitizeSingle//:/\":\"}" + toSanitizeSingle="${toSanitizeSingle//,/\",\"}" + toSanitizeSingle="\"${toSanitizeSingle}\"" + + # Put { } back. + toSanitizeSingle="{${toSanitizeSingle}}" + + @return toSanitizeSingle +} + +string.unJsonfy() { + @resolve:this + local toUnJsonfy="$this" + # Remove (. + toUnJsonfy=${toUnJsonfy//\(/} + # Remove ). + toUnJsonfy=${toUnJsonfy//\)/} + # Remove ]. + toUnJsonfy=${toUnJsonfy//\]/} + # Remove {. + toUnJsonfy=${toUnJsonfy//\}/} + # Remove }. + toUnJsonfy=${toUnJsonfy//\{/} + # Remove =. + toUnJsonfy=${toUnJsonfy//\=/} + # Remove ". + toUnJsonfy="${toUnJsonfy//\"/}" + + # Replace [ with |, + # Here we use | to denote option separation. + toUnJsonfy=${toUnJsonfy//\[/\|} + + # Replace space| with |. + toUnJsonfy=${toUnJsonfy// \|/\|} + + # Replace |space with |. + toUnJsonfy=${toUnJsonfy//\| /\|} + + toUnJsonfy=$(var: toUnJsonfy trim) + + # Delete first |. + [[ "${toUnJsonfy:0:1}" == '|' ]] && toUnJsonfy="${toUnJsonfy:1}" + @return toUnJsonfy +} + +string.trim() { + @resolve:this + local toTrim="$this" + # Remove leading whitespace. + toTrim="${toTrim#"${toTrim%%[![:space:]]*}"}" + # Remove trailing whitespace. + toTrim="${toTrim%"${var##*[![:space:]]}"}" + toTrim=$(echo -n "$toTrim") + @return toTrim +} + diff --git a/test/test-option.sh b/test/test-option.sh new file mode 100755 index 0000000..b065e64 --- /dev/null +++ b/test/test-option.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# +# Options object test. + +source "$( cd "${BASH_SOURCE[0]%/*}" && cd .. && pwd )/lib/oo-bootstrap.sh" + +import util/test UI/Color +import util/option + +Option optionA +$var:optionA name = 'one' +$var:optionA value = 1 +$var:optionA letter = 'a' +$var:optionA flag = false +$var:optionA required = true + +Option optionB +$var:optionB name = 'two' +$var:optionB value = 2 +$var:optionB letter = 'b' +$var:optionB flag = false +$var:optionB required = false + +Option optionVerbose +$var:optionVerbose name = 'verbose' +$var:optionVerbose value = true +$var:optionVerbose letter = 'v' +$var:optionVerbose flag = true +$var:optionVerbose required = false + +OptionsWrapper optionMenuWrapper +Options optionMenu +Option optionItem + +it 'should manually add options.' +try + $var:optionMenu Set optionA + $var:optionMenu Set optionB + $var:optionMenu Set optionVerbose + serializedOptions=$($var:optionMenu) + [[ "$serializedOptions" = *'verbose'* ]] +expectPass + +it 'should add options from array.' + declare -A DEFAULTS + DEFAULTS[one,1,a,false,true]='' + DEFAULTS[two,2,b,false,false]='' + DEFAULTS[three,3,c,false,false]='' + DEFAULTS[verbose,true,v,true,false]='' + Options optionMenu=$($var:optionMenuWrapper SetDefaults optionMenu DEFAULTS) + serializedOptions=$($var:optionMenu) + [[ "$serializedOptions" = *'three'* ]] +try + +expectPass + +it 'should delete option.' +try + $var:optionMenu Delete optionB + serializedOptions=$($var:optionMenu) + ! [[ "$serializedOptions" = *'two'* ]] +expectPass + +it 'should get serialized attribute.' +try + optionName=$(Options::GetSerializedAttribute "$($var:optionA)" 'name') + test "$optionName" = 'one' +expectPass + +it 'should unserialize option.' +try + Options::Unserialize "$($var:optionA)" $ref:optionItem + optionName=$($var:optionItem name) + test "$optionName" = 'one' +expectPass + +it 'should find option.' +try + $var:optionMenu Set optionA + $var:optionMenu Set optionVerbose + Option optionItem=$($var:optionMenu Search 'name' 'one') + optionName=$($var:optionItem name) + test "$optionName" = 'one' +expectPass + +it 'should get options string.' +try + optionsString=$($var:optionMenu GetOptionsString) + test "$optionsString" = 'b:c:v,a:' +expectPass + +it 'should parse arguments.' +try + # Simulate call the script like: ./test-option.sh -a 777 + set -- "${@:1:2}" '-a 777' + Options optionMenu=$($var:optionMenuWrapper ParseArguments optionMenu "$@") + serializedOptions=$($var:optionMenu) + [[ "$serializedOptions" = *'777'* ]] +expectPass + +it 'should copy values from Object to associative array.' +try + Options optionMenu=$($var:optionMenuWrapper SetDefaults optionMenu DEFAULTS) + declare -A PARAMETERS=$($var:optionMenu ToArray) + [[ "${PARAMETERS[two]}" = '2' ]] +expectPass + +it 'should fast parse arguments.' +try + declare -A DEFAULTS_FAST + DEFAULTS_FAST[one,1,a,false,true]='' + declare -A OPTIONS_FAST + set -- "${@:1:2}" '-a 777' + Options::FastParseArguments DEFAULTS_FAST OPTIONS_FAST "$@" + testText="${!OPTIONS_FAST[@]}" + [[ "$testText" = *'one'* ]] +expectPass +