General fixes & improvements:
- Make isNumeric accept optional first argument - Support rest params for commands to collect all remaining inputs - Support switching apps if the app name already exists for g::app - Make g::source look for files relative to the source file where it was called - Add ability to override $g__CALLER_PATH - Make g::source store resolved file paths, not file slugs - Print a full stack trace on g::error::throw
This commit is contained in:
parent
f2884d4606
commit
9fe327a78a
149
src/g.bash
149
src/g.bash
@ -56,21 +56,29 @@ g__BOOTSTRAPPED=no
|
|||||||
# Array of file paths that should be deleted on exit
|
# Array of file paths that should be deleted on exit
|
||||||
g__FILES_TO_CLEANUP=()
|
g__FILES_TO_CLEANUP=()
|
||||||
|
|
||||||
|
# Names of registered app namespaces
|
||||||
g__APP_NAMES=()
|
g__APP_NAMES=()
|
||||||
|
|
||||||
|
# The currently active app namespace
|
||||||
g__APP_CURRENT_NAME="app"
|
g__APP_CURRENT_NAME="app"
|
||||||
|
|
||||||
|
# UUID of the last argument set parsed
|
||||||
g__APP_LAST_ARGPARSE=''
|
g__APP_LAST_ARGPARSE=''
|
||||||
|
|
||||||
|
# Prefix of the last argument set alias generated
|
||||||
g__APP_LAST_ALIAS=''
|
g__APP_LAST_ALIAS=''
|
||||||
|
|
||||||
|
# Path to the directory where lock files can be created
|
||||||
g__LOCKING_FILE_DIR="/tmp"
|
g__LOCKING_FILE_DIR="/tmp"
|
||||||
|
|
||||||
|
# Array of lock files held by this process
|
||||||
g__LOCKING_HOLDS=()
|
g__LOCKING_HOLDS=()
|
||||||
|
|
||||||
|
# Amount of time (in seconds) to sleep between failed lock attempts
|
||||||
g__LOCKING_INTERVAL=0.25
|
g__LOCKING_INTERVAL=0.25
|
||||||
|
|
||||||
g__DEBUG_EVAL=yes
|
# If 'yes' eval calls will be logged to the console
|
||||||
|
g__DEBUG_EVAL=no
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -105,6 +113,13 @@ function g::internals::cleanup() {
|
|||||||
g::lock::cleanupForExit
|
g::lock::cleanupForExit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Override the top-level caller reference.
|
||||||
|
#
|
||||||
|
function g::internals::setCaller() {
|
||||||
|
local caller="$1"
|
||||||
|
g__CALLER_PATH="$(g::util::realpath $caller)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
## ====================== UTILITIES ====================== ##
|
## ====================== UTILITIES ====================== ##
|
||||||
@ -128,7 +143,7 @@ function g::silence() {
|
|||||||
# @return 0 if numeric, 1 otherwise
|
# @return 0 if numeric, 1 otherwise
|
||||||
#
|
#
|
||||||
function g::util::isNumeric() {
|
function g::util::isNumeric() {
|
||||||
local value="$1"
|
local value="${1:-}"
|
||||||
|
|
||||||
local rex='^[+-]?[0-9]+([.][0-9]+)?$'
|
local rex='^[+-]?[0-9]+([.][0-9]+)?$'
|
||||||
|
|
||||||
@ -417,6 +432,11 @@ function g::path::resolveFrom() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function g::path::cd() {
|
||||||
|
local resolved="$(g::path::resolve "$@")"
|
||||||
|
cd "$resolved"
|
||||||
|
}
|
||||||
|
|
||||||
function g::path::tmp() {
|
function g::path::tmp() {
|
||||||
local name="$(mktemp)"
|
local name="$(mktemp)"
|
||||||
g__FILES_TO_CLEANUP+=("$name")
|
g__FILES_TO_CLEANUP+=("$name")
|
||||||
@ -435,7 +455,10 @@ function g::path::tmpdir() {
|
|||||||
#
|
#
|
||||||
function g::error::throw() {
|
function g::error::throw() {
|
||||||
local message="$1"
|
local message="$1"
|
||||||
echo "$message" 1>&2
|
echo ""
|
||||||
|
echo ""
|
||||||
|
g::util::trace "ERROR: $message" 2 1>&2
|
||||||
|
echo ""
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,10 +480,11 @@ function g::now() {
|
|||||||
function g::util::trace() {
|
function g::util::trace() {
|
||||||
local stack=""
|
local stack=""
|
||||||
local i message="${1:-""}"
|
local i message="${1:-""}"
|
||||||
|
local skip="${2:-1}"
|
||||||
local stack_size=${#FUNCNAME[@]}
|
local stack_size=${#FUNCNAME[@]}
|
||||||
|
|
||||||
# to avoid noise we start with 1 to skip the trace function
|
# to avoid noise we start with 1 to skip the trace function
|
||||||
for (( i=1; i<$stack_size; i++ )); do
|
for (( i=$skip; i<$stack_size; i++ )); do
|
||||||
local func="${FUNCNAME[$i]}"
|
local func="${FUNCNAME[$i]}"
|
||||||
|
|
||||||
[ x$func = x ] && func=MAIN
|
[ x$func = x ] && func=MAIN
|
||||||
@ -470,7 +494,7 @@ function g::util::trace() {
|
|||||||
|
|
||||||
[ x"$src" = x ] && src=non_file_source
|
[ x"$src" = x ] && src=non_file_source
|
||||||
|
|
||||||
stack+=$'\n'" at: "$func" "$src" "$linen
|
stack+=$'\n'" at ($func) in $src (line $linen)"
|
||||||
done
|
done
|
||||||
|
|
||||||
stack="${message}${stack}"
|
stack="${message}${stack}"
|
||||||
@ -1256,6 +1280,12 @@ MATH
|
|||||||
|
|
||||||
function g::source::resolve() {
|
function g::source::resolve() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
|
local up="${2:-1}"
|
||||||
|
|
||||||
|
(
|
||||||
|
local reldir="$(dirname ${BASH_SOURCE[$up]})"
|
||||||
|
g::internal "Changing to dir to resolve source: $reldir"
|
||||||
|
g::path::cd "$reldir"
|
||||||
|
|
||||||
local fsPath="$(realpath "${path}.bash")"
|
local fsPath="$(realpath "${path}.bash")"
|
||||||
if g::file::exists "$fsPath"; then
|
if g::file::exists "$fsPath"; then
|
||||||
@ -1278,12 +1308,14 @@ function g::source::resolve() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
return $(g::util::false)
|
return $(g::util::false)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function g::source::exists() {
|
function g::source::exists() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
|
local up="${2:-1}"
|
||||||
|
|
||||||
local resolved="$(g::source::resolve "$path")"
|
local resolved="$(g::source::resolve "$path" $(( up + 1 )))"
|
||||||
local length="$(g::str::length "$resolved")"
|
local length="$(g::str::length "$resolved")"
|
||||||
|
|
||||||
if [[ "$length" -gt 0 ]]; then
|
if [[ "$length" -gt 0 ]]; then
|
||||||
@ -1295,9 +1327,10 @@ function g::source::exists() {
|
|||||||
|
|
||||||
function g::source::force() {
|
function g::source::force() {
|
||||||
local slug="$1"
|
local slug="$1"
|
||||||
|
local up="${2:-1}"
|
||||||
|
|
||||||
local resolved="$(g::source::resolve "$slug")"
|
local resolved="$(g::source::resolve "$slug" $(( up + 1 )))"
|
||||||
if ! g::source::exists "$slug"; then
|
if ! g::source::exists "$slug" $(( up + 1 )); then
|
||||||
g::error::throw "Cannot resolve source to include: $slug"
|
g::error::throw "Cannot resolve source to include: $slug"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -1319,8 +1352,10 @@ function g::source::force() {
|
|||||||
|
|
||||||
function g::source::has() {
|
function g::source::has() {
|
||||||
local slug="$1"
|
local slug="$1"
|
||||||
|
local up="${2:-1}"
|
||||||
|
|
||||||
if g::arr::includes "$slug" "${g__IMPORTED_FILES[@]}"; then
|
local resolved="$(g::source::resolve "$slug" $(( up + 1 )))"
|
||||||
|
if g::arr::includes "$resolved" "${g__IMPORTED_FILES[@]}"; then
|
||||||
return $(g::util::true)
|
return $(g::util::true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -1329,10 +1364,11 @@ function g::source::has() {
|
|||||||
|
|
||||||
function g::source() {
|
function g::source() {
|
||||||
local slug="$1"
|
local slug="$1"
|
||||||
|
local up="${2:-1}"
|
||||||
|
|
||||||
if ! g::source::has "$slug"; then
|
if ! g::source::has "$slug" $(( up + 1 )); then
|
||||||
g::source::force "$slug"
|
g::source::force "$slug" $(( up + 1 ))
|
||||||
g__IMPORTED_FILES+=("$slug")
|
g__IMPORTED_FILES+=("$resolved")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1448,9 +1484,10 @@ function g::app() {
|
|||||||
local description="${2:-}"
|
local description="${2:-}"
|
||||||
|
|
||||||
g__APP_CURRENT_NAME="$name"
|
g__APP_CURRENT_NAME="$name"
|
||||||
|
if ! g::arr::includes "$name" "${g__APP_NAMES[@]}"; then
|
||||||
g__APP_NAMES+=("$name")
|
g__APP_NAMES+=("$name")
|
||||||
|
|
||||||
g::app::set DESCRIPTION "$description"
|
g::app::set DESCRIPTION "$description"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function g::app::var() {
|
function g::app::var() {
|
||||||
@ -1533,6 +1570,26 @@ function g::app::command() {
|
|||||||
g::app::set "$(g::app::command::var DESCRIPTION)" "$description"
|
g::app::set "$(g::app::command::var DESCRIPTION)" "$description"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function g::app::command::rest() {
|
||||||
|
local name="$1"
|
||||||
|
local description="${2:-$name}"
|
||||||
|
|
||||||
|
local restVarName="$(g::app::command::var REST_ARG_NAME)"
|
||||||
|
local restVarDescription="$(g::app::command::var REST_ARG_DESCRIPTION)"
|
||||||
|
|
||||||
|
if ! g::app::has "$restVarName"; then
|
||||||
|
g::app::set "$restVarName" "$name"
|
||||||
|
g::app::set "$restVarDescription" "$description"
|
||||||
|
else
|
||||||
|
g::error::throw "Cannot register rest argument '$name': command already has rest argument"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function g::app::command::hasRest() {
|
||||||
|
local restVarName="$(g::app::command::var REST_ARG_NAME)"
|
||||||
|
return $(g::app::has "$restVarName")
|
||||||
|
}
|
||||||
|
|
||||||
function g::app::command::arg() {
|
function g::app::command::arg() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local description="${2:-$name}"
|
local description="${2:-$name}"
|
||||||
@ -1544,6 +1601,10 @@ function g::app::command::arg() {
|
|||||||
defaultValue="$3"
|
defaultValue="$3"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if g::app::command::hasRest; then
|
||||||
|
g::error::throw "Cannot register positional argument '$name' after rest argument."
|
||||||
|
fi
|
||||||
|
|
||||||
local currentArgVar="$(g::app::command::var CURRENT_ARG_NUM)"
|
local currentArgVar="$(g::app::command::var CURRENT_ARG_NUM)"
|
||||||
if ! g::app::has "$currentArgVar"; then
|
if ! g::app::has "$currentArgVar"; then
|
||||||
g::app::set "$currentArgVar" 0
|
g::app::set "$currentArgVar" 0
|
||||||
@ -1685,6 +1746,24 @@ function g::app::command::parse::has::flag() {
|
|||||||
return $(g::util::false)
|
return $(g::util::false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function g::app::command::parse::register::rest() {
|
||||||
|
local uuid="$1"
|
||||||
|
shift
|
||||||
|
local value="$@"
|
||||||
|
|
||||||
|
local varName="$(g::app::command::parse::var REST_ARGS "$uuid")"
|
||||||
|
g::internal "Registering rest args: $varName" g::app::command::parse
|
||||||
|
g::app::set "$varName" "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
function g::app::command::parse::get::rest() {
|
||||||
|
local uuid="$1"
|
||||||
|
|
||||||
|
local varName="$(g::app::command::parse::var REST_ARGS "$uuid")"
|
||||||
|
g::internal "Getting rest args" g::app::command::parse
|
||||||
|
g::app::get "$varName"
|
||||||
|
}
|
||||||
|
|
||||||
##
|
##
|
||||||
# Register the value of a positional argument in an argument set.
|
# Register the value of a positional argument in an argument set.
|
||||||
#
|
#
|
||||||
@ -1758,12 +1837,19 @@ function g::app::command::parse() {
|
|||||||
g::app::set "$cmdNameVar" "$cmdName"
|
g::app::set "$cmdNameVar" "$cmdName"
|
||||||
|
|
||||||
local nextPositionalIndex=1
|
local nextPositionalIndex=1
|
||||||
|
local trapRestArgs=no
|
||||||
|
local restArgs=()
|
||||||
local trappedFlag=no
|
local trappedFlag=no
|
||||||
local trappedFlagName
|
local trappedFlagName
|
||||||
local trappedFlagIndex
|
local trappedFlagIndex
|
||||||
for arg in "${args[@]}"; do
|
for arg in "${args[@]}"; do
|
||||||
g::internal "Parsing argument: ${arg}" g::app::command::parse
|
g::internal "Parsing argument: ${arg}" g::app::command::parse
|
||||||
|
|
||||||
|
if [[ $trapRestArgs == yes ]]; then
|
||||||
|
restArgs+=("$arg")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
local isFlag=no
|
local isFlag=no
|
||||||
local flagOffset=0
|
local flagOffset=0
|
||||||
# If the string starts with -- or -, then it is a flag parameter
|
# If the string starts with -- or -, then it is a flag parameter
|
||||||
@ -1852,8 +1938,15 @@ function g::app::command::parse() {
|
|||||||
|
|
||||||
local currentArg="$(g::app::get $currentArgVar)"
|
local currentArg="$(g::app::get $currentArgVar)"
|
||||||
if (( $nextPositionalIndex > $currentArg )); then
|
if (( $nextPositionalIndex > $currentArg )); then
|
||||||
|
local restVarName="$(g::app::command::var REST_ARG_NAME "${cmdName}")"
|
||||||
|
if g::app::has "$restVarName"; then
|
||||||
|
trapRestArgs=yes
|
||||||
|
restArgs+=("$arg")
|
||||||
|
continue
|
||||||
|
else
|
||||||
g::error::throw "Unexpected positional argument: $arg"
|
g::error::throw "Unexpected positional argument: $arg"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
g::app::command::parse::register::arg $nextPositionalIndex "$arg" "$groupUuid"
|
g::app::command::parse::register::arg $nextPositionalIndex "$arg" "$groupUuid"
|
||||||
nextPositionalIndex=$(($nextPositionalIndex + 1))
|
nextPositionalIndex=$(($nextPositionalIndex + 1))
|
||||||
@ -1866,6 +1959,10 @@ function g::app::command::parse() {
|
|||||||
g::error::throw "Missing required value for flag: ${trappedFlagName}"
|
g::error::throw "Missing required value for flag: ${trappedFlagName}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $trapRestArgs == yes ]]; then
|
||||||
|
g::app::command::parse::register::rest "$groupUuid" "${restArgs[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
g::app::command::validate "$cmdName" "$groupUuid"
|
g::app::command::validate "$cmdName" "$groupUuid"
|
||||||
g__APP_LAST_ARGPARSE="$groupUuid"
|
g__APP_LAST_ARGPARSE="$groupUuid"
|
||||||
}
|
}
|
||||||
@ -1993,13 +2090,13 @@ function g::app::invoke() {
|
|||||||
function g::args::buildAlias() {
|
function g::args::buildAlias() {
|
||||||
local uuid="$1"
|
local uuid="$1"
|
||||||
|
|
||||||
alias g::args::alias::${uuid}="g::args \"$uuid\""
|
alias ${uuid}="g::args \"$uuid\""
|
||||||
alias g::args::alias::${uuid}::arg="g::arg \"$uuid\""
|
alias ${uuid}::arg="g::arg \"$uuid\""
|
||||||
alias g::args::alias::${uuid}::arg::has="g::arg:has \"$uuid\""
|
alias ${uuid}::arg::has="g::arg:has \"$uuid\""
|
||||||
alias g::args::alias::${uuid}::flag="g::flag \"$uuid\""
|
alias ${uuid}::flag="g::flag \"$uuid\""
|
||||||
alias g::args::alias::${uuid}::flag::has="g::flag:has \"$uuid\""
|
alias ${uuid}::flag::has="g::flag:has \"$uuid\""
|
||||||
|
|
||||||
g__APP_LAST_ALIAS="g::args::alias::${uuid}"
|
g__APP_LAST_ALIAS="${uuid}"
|
||||||
}
|
}
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -2068,6 +2165,20 @@ function g::arg::has() {
|
|||||||
return $(g::app::command::parse::has::arg "$nameOrPosition" "$uuid")
|
return $(g::app::command::parse::has::arg "$nameOrPosition" "$uuid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function g::arg::rest() {
|
||||||
|
local uuid="$1"
|
||||||
|
|
||||||
|
local cmdNameVar="$(g::app::command::parse::var "COMMAND_NAME" "$uuid")"
|
||||||
|
local cmdName="$(g::app::get "$cmdNameVar")"
|
||||||
|
|
||||||
|
local restVarName="$(g::app::command::var REST_ARG_NAME "$cmdName")"
|
||||||
|
if ! g::app::has "$restVarName"; then
|
||||||
|
g::error::throw "Unable to get rest arguments for command: $cmdName: command has no rest argument configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
g::app::command::parse::get::rest "$uuid"
|
||||||
|
}
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get the value of a given flag in an argument set.
|
# Get the value of a given flag in an argument set.
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user