-
Notifications
You must be signed in to change notification settings - Fork 0
/
argsh.min.sh
executable file
·12 lines (12 loc) · 15.8 KB
/
argsh.min.sh
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash
# shellcheck disable=SC2178 disable=SC2120 disable=SC1090 disable=SC2046 disable=SC2155
set -euo pipefail; ARGSH_COMMIT_SHA="c344a0aa041a808a182f76da22313b3ef64503d0"; ARGSH_VERSION="v0.2.1"
: "${ARGSH_VERSION:=unknown}";: "${ARGSH_COMMIT_SHA:=unknown}";: "${ARGSH_FIELD_WIDTH:=24}";COMMANDNAME=("$(s="${ARGSH_SOURCE:-"${0}"}"; echo "${s##*/}")");:usage() { local a20="${1}"; shift;declare -p usage &>/dev/null || local -a usage=();declare -p args &>/dev/null || local -a args=();[[ $(( ${#usage[@]} % 2 )) -eq 0 ]] || :args::_error "usage must be an associative array";[[ $(( ${#usage[@]} % 2 )) -eq 0 ]] || :args::_error "usage must be an associative array";if [[ -z ${1:-} || ${1} == "-h" || ${1} == "--help" ]];then :usage::text "${a20}";exit 0;fi;if ! (( ${#COMMANDNAME[@]} )) && [[ ${1:-} == "--argsh" ]];then echo "https://arg.sh ${ARGSH_COMMIT_SHA:-} ${ARGSH_VERSION:-}";exit 0;fi;local -A a28=();local -a a63=("${@}");local a51 a25="";while (( ${#a63[@]} ));do if [[ ${a63[0]:0:1} != "-" ]];then [[ -z "${a51:-}" ]] || break;a51="${a63[0]}";a63=("${a63[@]:1}");continue;fi;:args::parse_flag || break;a28["${a25}"]=1;done;:args::check_required_flags;local a40;for (( a65=0; a65 < ${#usage[@]}; a65+=2 ));do for a29 in $(echo "${usage[a65]/:*}" | tr '|' "\n");do a29="${a29#\#}";[[ "${a51}" == "${a29}" ]] || continue;a25="${usage[a65]#\#}";a40="${usage[a65]/*:-}";a40="${a40#\#}";[[ "${a40}" == "${usage[a65]}" ]] || break 2;a40="${a40/|*}";break 2;done;done;[[ -n "${a40:-}" ]] || :args::error_usage "Invalid command: ${a51}";COMMANDNAME+=("${a25/[|:]*}");usage=("${a40}" "${a63[@]}");};:usage::text() { local a20="${1:-}";string::indent "${a20}";echo;echo "Usage: ${COMMANDNAME[*]} <command> [args]";[[ ${usage[0]:-} == '-' ]] || echo -e "\nAvailable Commands:";for (( a65=0; a65 < ${#usage[@]}; a65+=2 ));do [[ "${usage[a65]:0:1}" != "#" ]] || continue;[[ "${usage[a65]}" != "-" ]] || { echo;echo "${usage[a65+1]}";continue;};printf " %-${ARGSH_FIELD_WIDTH}s %s\n" "${usage[a65]/[:|]*}" "${usage[a65+1]}";done;:args::text_flags;echo;echo "Use \"${COMMANDNAME[*]} <command> --help\" for more information about a command.";};:args() { local a20="${1}"; shift;declare -p args &>/dev/null || local -a args=();[[ $(( ${#args[@]} % 2 )) -eq 0 ]] || :args::_error "args must be an associative array";if [[ ${1:-} == "-h" || ${1:-} == "--help" ]];then :args::text;exit 0;fi;local a24=0 a25="" a65 a0=1;local -A a28=();local -a a63=("${@}");while (( ${#a63[@]} ));do if [[ ${a63[0]:0:1} != "-" ]];then local a47 a21;a65="$(:args::field_positional "${a0}")" || :args::error_usage "too many arguments: ${a63[0]}";a25="${args[a65]}";a47="$(args::field_name "${a25}")";a21="$(:args::field_value "${a63[0]}")" || exit "${?}";local -n a64="${a47}";if is::array "${a47}";then (( a24 )) || { a64=();a24=1;};a64+=("${a21}");else a64="${a21}";fi;a63=("${a63[@]:1}");(( ++a0 ));continue;fi;:args::parse_flag || :args::error_usage "unknown a45: ${a63[0]}";a28["${a25}"]=1;done;if a65="$(:args::field_positional "${a0}")";then a25="$(args::field_name "${args[a65]}")";if is::uninitialized "${a25}" && ! is::array "${a25}";then :args::error_usage "missing required argument: ${a25}";fi;fi;:args::check_required_flags;[[ ${#a63[@]} -eq 0 ]] || :args::error_usage "too many arguments: ${a63[*]}";};:args::text() { declare -p args &>/dev/null || return 0;local -a a2=() a14=();:args::positional;string::indent "${a20}";echo;echo "Usage:";echo " ${COMMANDNAME[*]} ${a14[*]}";(( ${#a2[@]} == 0 )) || { echo;echo "Arguments:";for a65 in "${a2[@]}";do [[ ${args[a65]} != "-" ]] || continue;a38="$( printf " %-${ARGSH_FIELD_WIDTH}s%s" " " "${args[a65+1]}" | fmt::tty;)";printf " %-${ARGSH_FIELD_WIDTH}s%s\n" "$(:args::fieldf "${args[a65]}")" "$(string::trim-left "${a38}")";done;};:args::text_flags;echo;};:args::text_flags() { local -a args=("${args[@]}");local -a a32=();array::contains 'help|h:+' "${args[@]}" || args+=('help|h:+' "Show this help message");:args::flags;(( ${#a32[@]} )) || return 0;[[ "${args[${a32[0]}]}" == "-" ]] || echo -e "\nOptions:";for a65 in "${a32[@]}";do [[ "${args[a65]:0:1}" != "#" ]] || continue;[[ "${args[a65]}" != "-" ]] || { echo;echo "${args[a65+1]}";continue;};:args::fieldf "${args[a65]}";{ echo -n " ";echo -e "${args[a65+1]}\n";} | fmt::tty;done;};:args::flags() { declare -p args &>/dev/null || local -a args;declare -p a32 &>/dev/null || local -a a32;for (( a65=0; a65 < "${#args[@]}"; a65+=2 ));do if [[ ${args[a65]} == *"|"* || ${args[a65]} == '-' ]];then a32+=("${a65}");fi;done;};:args::positional() { declare -p args &>/dev/null || local -a args;declare -p a2 &>/dev/null || local -a a2;declare -p a14 &>/dev/null || local -a a14;local a64;for (( a65=0; a65 < "${#args[@]}"; a65+=2 ));do [[ ${args[a65]} != *"|"* && ${args[a65]} != '-' ]] || continue;a64="$(args::field_name "${args[a65]}")";a2+=("${a65}");if is::array "${a64}";then a14+=("...${a64}");continue;fi;if ! is::uninitialized "${a64}";then a14+=("[${a64}]");continue;fi;a14+=("<${a64}>");done;};:args::parse_flag() { declare -p a63 a25 &>/dev/null || return 1;local a45="${a63[0]/=*}";if [[ ${a45:0:2} == "--" ]];then a25="$(:args::field_lookup "${a45:2}")" || return "${?}";elif [[ ${a45:0:1} == "-" ]];then a45="${a45:0:2}";a25="$(:args::field_lookup "${a45:1}")" || return "${?}";fi;:args::field_set_flag "${a25}";};:args::check_required_flags() { declare -p a28 args &>/dev/null || return 1;local a25;local -a a17;for (( a65=0; a65 < ${#args[@]}; a65+=2 ));do a25="${args[a65]}";:args::field_attrs "${a25}";if (( a17[2] )) && ! (( a17[4] ));then local -n a64="${a17[0]}";a64=0;fi;if (( a17[6] )) && [[ -z ${a28[${args[a65]}]:-} ]];then :args::error_usage "missing required a45: ${args[a65]/|*}";fi;done;};:args::field_set_flag() { local a25="${1}";declare -p a63 a45 &>/dev/null || return 1;local -a a17;:args::field_attrs "${a25}";local -n a64="${a17[0]}";local a3 a4;if (( a17[2] ));then a3=1;if [[ ${a45:0:2} == "--" ]];then a63=("${a63[@]:1}");else a63[0]="-${a63[0]:2}";[[ ${a63[0]} != "-" ]] || a63=("${a63[@]:1}");fi;fi;[[ -n ${a3:-} ]] || { a4="${a63[0]/${a45}}";if [[ ${a4} == "" ]];then (( ${#a63[@]} )) || :args::error "missing value for a45: ${a17[0]}";a3="${a63[1]}";a63=("${a63[@]:1}");else [[ "${a4:0:1}" != "=" ]] || a4="${a4:1}";a3="${a4}";fi;a3="$(:args::field_value "${a3}")" || exit "${?}";a63=("${a63[@]:1}");};if (( a17[5] ));then a64+=("${a3}");else a64="${a3}";fi;};:args::field_value() { local a21="${1}";declare -p a25 &>/dev/null || return 1;declare -p a17 &>/dev/null || { local -a a17;:args::field_attrs "${a25}";};declare -f "to::${a17[3]}" &>/dev/null || :args::_error "unknown type: ${a17[3]}";"to::${a17[3]}" "${a21}" "${a17[0]}" || :args::error_usage "invalid type (${a17[3]}): ${a21}";};:args::field_lookup() { local a25="${1}";declare -p args &>/dev/null || return 1;for (( a65=0; a65 < ${#args[@]}; a65+=2 ));do if [[ ${args[a65]} =~ (^${a25}\||\|${a25}:|\|${a25}$) ]];then echo "${args[a65]}";return 0;fi;done;return 1;};:args::field_positional() { local a6="${1:-1}";declare -p args &>/dev/null || return 1;for (( a65=0; a65 < ${#args[@]}; a65+=2 ));do if [[ ${args[a65]} != *"|"* && ${args[a65]} != '-' ]];then if is::array "$(args::field_name "${args[a65]}")" || (( --a6 == 0 ));then echo "${a65}";return 0;fi;fi;done;return 1;};args::field_name() { local a25="${1}";local a27="${2:-1}";a25="${a25/[|:]*}";a25="${a25#\#}";if (( a27 ));then a25="${a25//-/_}";fi;echo "${a25}";};:args::field_attrs() { local a25="${1}";declare -p a17 &>/dev/null || local -a a17;a17=( "" "" 0 "" 0 0 0 0 "" );local a44="+~!";local a41="${a25#*[:]}";[ "${a41}" != "${a25}" ] || a41="";a17[0]="$(args::field_name "${a25}")";a17[8]="$(args::field_name "${a25}" 0)";[[ ${a17[0]:0:1} != "#" ]] || { a17[7]=1;};local -n a64="${a17[0]}";local -a a32;mapfile -t a32 < <(echo "${a25/[:]*}" | tr '|' '\n');[[ ${#a32[@]} -eq 1 ]] || { a17[1]="${a32[1]}";};if is::array "${a17[0]}";then a17[5]=1;! is::uninitialized "${a17[0]}" || a64=();! (( ${#a64[@]} )) || a17[4]=1;elif ! is::uninitialized "${a17[0]}";then a17[4]=1;fi;while (( ${#a41} > 0 ));do if [[ ${a41:0:1} == "+" ]];then [[ -z ${a17[3]} ]] || :args::_error "cannot have multiple types: ${a17[3]} and boolean";a17[2]=1;a41="${a41:1}";continue;fi;if [[ ${a41:0:1} == "~" ]];then ! (( a17[2] )) || :args::_error "already flagged as boolean";a41="${a41:1}";a17[3]="${a41/[$a44]*}";a41="${a41:${#a17[3]}}";continue;fi;if [[ ${a41:0:1} == "!" ]];then ! (( a17[6] )) || :args::_error "field already flagged as required";a17[6]=1;a41="${a41:1}";continue;fi;echo ":args error: unknown modifier: ${a41:0:1}" >&2;exit 2;done;if [[ -z ${a17[3]} && ${a17[2]} -eq 0 ]];then a17[3]="string";fi;};:args::fieldf() { local a25="${1}";declare -p a17 &>/dev/null || { local -a a17;:args::field_attrs "${a25}";};[[ ${a25} == *"|"* ]] || { echo "${a17[8]} ${a17[3]}";return 0;};local -n a64="${a17[0]}";a13=" ";! (( a17[6] )) || a13=" ! ";if [[ -n ${a17[1]} ]];then a13+="-${a17[1]}, --${a17[8]}";else a13+=" --${a17[8]}";fi;a13+=" ";! (( a17[5] )) || a13+="...";a13+="${a17[3]}";if (( a17[4] )) && ! (( a17[2] ));then a13+=" (default: ${a64[*]})";fi;echo "${a13}";};args::run() { local a61="${1}"; shift;for (( a65=0; a65<${#}; a65++ ));do local a56="${1}"; shift;local a40="${1}"; shift;if ! (( a61 )) || (( a56 ));then "${a40}";fi;done;};array::contains() { local -r a12="${1}"; shift;for a7 in "${@}";do [[ "${a7}" != "${a12}" ]] || return 0;done;return 1;};array::join() { local -r a5="${1}"; shift;local a15;printf -v a15 "${a5}%s" "${@}";echo "${a15:${#a5}}";};array::nth() { local -n a58="${1}";local -r a62="${2}";shift 2;for (( a65=1; a65<=${#}; a65++ ));do (( a65 % a62 )) || a58+=("${!a65}");done;};bash::version() { local a18="${1:-4}";local a30="${2:-3}";local a23="${3:-0}";if [[ "${BASH_VERSINFO[0]}" -lt "${a18}" ]];then return 1;elif [[ "${BASH_VERSINFO[0]}" -gt "${a18}" ]];then return 0;fi;if [[ "${BASH_VERSINFO[1]}" -lt "${a30}" ]];then return 1;elif [[ "${BASH_VERSINFO[1]}" -gt "${a30}" ]];then return 0;fi;if [[ "${BASH_VERSINFO[2]}" -lt "${a23}" ]];then return 1;fi;return 0;};binary::exists() { local a8="${1}";command -v "${a8}" &> /dev/null || { echo "${a8} is required to run this script" >&2;return 1;};};binary::github() { local a42="${1}";local -r a8="$(basename "${a42}")";local a46="${2}";local a48="${3}";local a53="${4:-}";curl -Lso /dev/stdout "https://github.com/${a46}/releases/download/${a48}" | { if [[ -n "${a53}" ]];then tar -xz -C "$(dirname "${a42}")" "${a53}";else tee "${a42}" &> /dev/null;fi;chmod +x "${a42}";};};binary::arch() { local a26="${1}";local -r a37="$(uname -m)";case "${a37}" in
x86_64|amd64)if (( a26 )); then echo "64-bit"; else echo "amd64"; fi;;
armv7l)echo "arm";;
aarch64)echo "arm64";;
*)echo "${a37}";;
esac;};binary::jq() { binary::exists "jq" 2>/dev/null || { local -r a9="$(github::latest "stedolan/jq")" system="$(uname -s)";binary::github "${PATH_BIN?}/jq" "stedolan/jq" "${a9}/jq-${system,,}-$(binary::arch)";};};binary::vale() { binary::exists "vale" 2>/dev/null || { local -r a9="$(github::latest "errata-ai/vale")" system="$(uname -s)";binary::github "${PATH_BIN?}/vale" "errata-ai/vale" "${a9}/vale_${a9:1}_$(uname -s)_$(binary::arch 1).tar.gz" "vale";};};docker::user() { local a50="${1:-"$(id -u)"}";local a55="${2:-"$(id -g)"}";local a43="${3:-"$(whoami)"}";local a36="${4:-"/workspace"}";local a31="${5:-"/bin/sh"}";local a34;a34="$(pwd)";a34="${a34#"${PATH_BASE:-}"}";if [[ "${a34}" == "$(pwd)" ]];then a34="${a36}";else a34="${a36}${a34}";fi;echo "${a43}:x:${a50}:${a55}::${a36}:${a31}" > /tmp/docker_passwd;echo "${a43}:x:${a55}:" > /tmp/docker_group;echo "-v /tmp/docker_passwd:/etc/passwd -v /tmp/docker_group:/etc/group";echo "-u ${a50}:${a55}";echo "-v ${PATH_BASE:-"$(pwd)"}:${a36}";echo "-w ${a34}";};error::stacktrace() { local -r a49="${1:-${?}}";if (( a49 ));then echo -e "\n\033[38;5;196m■■ Stacktrace(${a49}): \e[1m${BASH_COMMAND}\e[22m";for a65 in $(seq 1 $((${#FUNCNAME[@]} - 2)));do echo -e "${a65}. ${BASH_SOURCE[a65]}:${BASH_LINENO[a65-1]} ➜ ${FUNCNAME[a65]}()";done;echo -e "\033[0m";return "${a49}";fi;};:args::_error() { declare -p a25 &>/dev/null || local a25="???";echo ":args error [${a25}] ➜ ${1}" >&2;exit 2;};:args::error() { declare -p a25 &>/dev/null || local a25="???";echo -e "[ ${a25/[:|]*} ] invalid argument\n➜ ${1}\n" >&2;exit 2;};:args::error_usage() { declare -p a25 &>/dev/null || local a25="???";echo -e "[ ${a25/[:|]*} ] invalid usage\n➜ ${1}\n" >&2;echo -e "Use \"${0##*/} -h\" for more information" >&2;exit 2;};fmt::tty() { local a57="${1:-"$(cat)"}";if ! command -v fmt &>/dev/null || [[ ! -t 1 ]];then echo "${a57}";return 0;fi;local a35;a35="$(tput cols)";echo "${a57}" | fmt -w "${a35}";};github::latest() { local a46="${1}";curl -fsSLI -o /dev/null -w "%{url_effective}" "https://github.com/${a46}/releases/latest" | rev | cut -d'/' -f1 | rev;};declare -gA a1=();import() { local a54="${1}";(( ${a1["${a54}"]:-} )) || { a1["${a54}"]=1;if [[ ${a54:0:1} == "@" ]];then a54="${PATH_BASE:?"PATH_BASE missing"}/${a54:1}";elif [[ ${a54:0:1} == "~" ]];then local _s="${ARGSH_SOURCE:-${BASH_SOURCE[-1]}}";a54="${_s%/*}/${a54:1}";else local _s="${ARGSH_SOURCE:-${BASH_SOURCE[0]}}";a54="${_s%/*}/${a54}";fi;import::source "${a54}" || exit 1;};};import::source() { local a54="${1}";for a59 in "" ".sh" ".bash";do if [[ -f "${a54}${a59}" ]];then . "${a54}${a59}";return;fi;done;echo "Library not found ${a54}" >&2;return 1;};import::clear() { a1=();};is::tty() { [[ -t 1 ]];};is::array() { declare -p "${1}" &>/dev/null && [[ $(declare -p "${1}") == "declare -a"* ]];};is::uninitialized() { local a60="${1}";if is::array "${a60}";then [[ $(declare -p "${a60}") == "declare -a ${a60}" ]];else [[ ! ${!a60+x} ]];fi;};is::set() { ! is::uninitialized "${1}";};argsh::shebang() { local -r a48="${1}";: "${ARGSH_SOURCE="${a48}"}";export ARGSH_SOURCE;[[ "${BASH_SOURCE[-1]}" != "${a48}" && -f "${a48}" ]] || { binary::exists docker || { echo "This script requires Docker to be installed";return 1;} >&2;local a52="";! tty -s || a52="-it";docker run --rm ${a52} $(docker::user) -e "BATS_LOAD" -e "ARGSH_SOURCE" -e "GIT_COMMIT_SHA=$(git rev-parse HEAD 2>/dev/null || :)" -e "GIT_VERSION=$(git describe --tags --dirty 2>/dev/null || :)" ghcr.io/arg-sh/argsh:latest "${@}";return 0;};bash::version 4 3 0 || { echo "This script requires bash 4.3.0 or later";return 1;} >&2;shift;. "${a48}";};string::drop-index() { local a16="${1}";local a33="${2}";local a11="${3:-1}";echo "${a16:0:a33}${a16:a33+a11}";};string::random() { local a11="${1:-42}";local a19="${2:-"a-zA-Z0-9"}";local a57;until [[ "${a57:-}" =~ ^[:alpha:] ]];do a57=$(tr -dc "${a19}" < /dev/urandom | fold -w "${a11}" | head -n 1 || :);done;echo "${a57}";};string::indent() { local a16="${1:-'-'}";local a10="${2:-0}";local a39 a22;[[ ${a16} != '-' ]] || a16="$(cat)";mapfile -t a22 < <(echo "${a16}");for a39 in "${a22[@]}";do a39="$(string::trim-left "${a39}")";(( a10 == 0 )) || printf "%${a10}s" " ";echo "${a39}";done;};string::trim-left() { local a16="${1}";local a19="${2:-" "$'\n'$'\t'}";[[ -n ${a16:-} ]] || return 0;[[ ${a16} != '-' ]] || a16="$(cat)";while [[ -n "${a16}" ]];do [[ ${a19} == *${a16:0:1}* ]] || break;a16="${a16:1}";done;echo "${a16}";};string::trim-right() { local a16="${1:-'-'}";local a19="${2:-" "$'\n'$'\t'}";[[ ${a16} != '-' ]] || a16="$(cat)";while [[ -n "${a16}" ]];do [[ ${a19} == *${a16: -1}* ]] || break;a16="${a16:0: -1}";done;echo "${a16}";};string::trim() { local a16="${1:-'-'}";local a19="${2:-" "$'\n'$'\t'}";[[ ${a16} != '-' ]] || a16="$(cat)";echo "${a16}" | string::trim-left - "${a19}" | string::trim-right - "${a19}";};to::string() { local a21="${1}";echo "${a21}";};to::boolean() { local a21="${1}";case "${a21}" in
""|"false"|"0")a21="0";;
*)a21="1";;
esac;echo "${a21}";};to::int() { local a21="${1}";[[ ${a21} =~ ^-?[0-9]+$ ]] || return 1;echo "${a21}";};to::float() { local a21="${1}";[[ ${a21} =~ ^-?[0-9]+(\.[0-9]+)?$ ]] || return 1;echo "${a21}";};to::stdin() { local a21="${1}";[[ ${a21} != "-" ]] || a21="$(cat)";echo "${a21}";};to::file() { local a21="${1}";[[ -f "${a21}" ]] || return 1;echo "${a21}";};[[ "${BASH_SOURCE[0]}" != "${0}" ]] || argsh::shebang "${@}"