From d8714042c7b4b7ed810bb5f258f3318d60907824 Mon Sep 17 00:00:00 2001 From: pvincent Date: Sat, 11 Apr 2026 11:16:24 +0400 Subject: [PATCH] functions + pluralize --- lib/functions.bash | 60 ++++++++++++++++++++++++++++++++++++++++++++ lib/miaou.completion | 54 ++++++++++++++++----------------------- tools/miaou-destroy | 24 +++++++++--------- 3 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 lib/functions.bash diff --git a/lib/functions.bash b/lib/functions.bash new file mode 100644 index 0000000..93fb7a4 --- /dev/null +++ b/lib/functions.bash @@ -0,0 +1,60 @@ +## library of useful functions, usually prefixed with '_' + +## +## ARRAY Functions +## + +function _array_contains { + local -n array=$1 + local element=$2 + [[ " ${array[@]} " =~ " ${element} " ]] && return 0 || return 1 +} + +function _array_intersect { + local -n arr1=$1 + local -n arr2=$2 + local -n result=$3 + + declare -A include + for item in "${arr2[@]}"; do include["$item"]=1; done + + result=() + for item in "${arr1[@]}"; do [[ -n ${include["$item"]} ]] && result+=("$item"); done +} + +function _array_subtract { + local -n arr1=$1 # First array + local -n arr2=$2 # Array to subtract + local -n result=$3 # Output array + + [[ ${#arr2[@]} == 0 ]] && result=("${arr1[@]}") && return + + declare -A exclude + for item in "${arr2[@]}"; do exclude["$item"]=1; done + + result=() + for item in "${arr1[@]}"; do [[ -z ${exclude["$item"]} ]] && result+=("$item"); done +} + +## +## TEXT Functions +## + +function _pluralize_simple { + echo -n "$1 " + [[ $1 -eq 1 || $1 -eq -1 ]] && echo ${2} || echo ${2}s +} + +## +## READ Functions +## + +function _confirm_destructive { + local message="${1:-This cannot be undone!}" + echo "⚠️ WARNING: $message" + read -p "Type YES to confirm: " response + case "$response" in + [yY][eE][sS] | [yY]) return 0 ;; + *) echo "canceled!" && return 1 ;; + esac +} diff --git a/lib/miaou.completion b/lib/miaou.completion index 75829df..dfc6a34 100644 --- a/lib/miaou.completion +++ b/lib/miaou.completion @@ -1,3 +1,9 @@ +# CONSTANTS + +MIAOU_INCUS_DIR=${MIAOU_INCUS_DIR:-/opt/miaou-incus} + +# FUNCTIONS + function _incus_container { incus list --format csv --columns n type=CONTAINER } @@ -170,32 +176,6 @@ function _miaou_ls { fi } -function _array_intersect { - local -n arr1=$1 - local -n arr2=$2 - local -n result=$3 - - declare -A include - for item in "${arr2[@]}"; do include["$item"]=1; done - - result=() - for item in "${arr1[@]}"; do [[ -n ${include["$item"]} ]] && result+=("$item"); done -} - -function _array_subtract { - local -n arr1=$1 # First array - local -n arr2=$2 # Array to subtract - local -n result=$3 # Output array - - [[ ${#arr2[@]} == 0 ]] && result=("${arr1[@]}") && return - - declare -A exclude - for item in "${arr2[@]}"; do exclude["$item"]=1; done - - result=() - for item in "${arr1[@]}"; do [[ -z ${exclude["$item"]} ]] && result+=("$item"); done -} - function _comp_debug { echo -e "\nDEBUG: $@" >&2 } @@ -219,16 +199,24 @@ function _miaou_stop { } function _miaou_destroy { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur suggestions previous_words - #TODO: if COMP_WORDS contains --force, use _incus_container instead of _incus_stopped_container - - # containers... - local suggestions=($(compgen -W "--yes --force $(_incus_stopped_container)" -- "$cur")) - local previous_containers=("${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}") # drop first and last items - _array_subtract suggestions previous_containers COMPREPLY + cur="${COMP_WORDS[COMP_CWORD]}" + if [[ $cur =~ ^- ]]; then + suggestions=(--yes --force) + else + _array_contains COMP_WORDS --force && suggestions=($(_incus_container)) || suggestions=($(_incus_stopped_container)) + [[ ${#suggestions[@]} == 0 ]] && suggestions=(--force) + fi + suggestions=($(compgen -W "${suggestions[*]}" -- "$cur")) + previous_words=("${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}") # drop first and last items + _array_subtract suggestions previous_words COMPREPLY } +# MAIN + +# shellcheck source=/opt/miaou-incus/lib/functions.bash +source "$MIAOU_INCUS_DIR/lib/functions.bash" complete -F _miaou_login "miaou-login" complete -F _miaou_exec "miaou-exec" complete -F _miaou_ls "miaou-ls" diff --git a/tools/miaou-destroy b/tools/miaou-destroy index 94badda..e6174b4 100755 --- a/tools/miaou-destroy +++ b/tools/miaou-destroy @@ -2,6 +2,7 @@ # CONSTANTS +MIAOU_INCUS_DIR=${MIAOU_INCUS_DIR:-/opt/miaou-incus} BASEDIR=$(dirname "$0") CONTAINERS=() YES=false @@ -54,29 +55,28 @@ function parse_options { [[ ${#CONTAINERS[@]} == 0 ]] && usage && exit 1 || true } -function confirm_destructive { - local message="${1:-This cannot be undone!}" - echo "⚠️ WARNING: $message" - read -p "Type YES to confirm: " response - case "$response" in - [yY][eE][sS] | [yY]) return 0 ;; - *) echo "canceled!" && return 1 ;; - esac -} - function force_option { [[ $FORCE == false ]] && return echo '--force ' } function destroy { - [[ $YES == false ]] && confirm_destructive "you are about to destroy ${#CONTAINERS[@]} containers <${CONTAINERS[*]}>" $(force_option) + if [[ $YES == false ]]; then + local message count + count=${#CONTAINERS[@]} + message="you are about to destroy $(_pluralize_simple $count container): ${CONTAINERS[*]}" + _confirm_destructive "$message" $(force_option) + fi incus delete "${CONTAINERS[@]}" $(force_option) } # MAIN -set -Eue +set -Eue -o pipefail + +# shellcheck source=/opt/miaou-incus/lib/functions.bash +source "$MIAOU_INCUS_DIR/lib/functions.bash" + flatten_short_options flat "$@" parse_options "${flat[@]}" destroy