From e42a2b248f8a4494a72ee51ec0c6586ebc1f26e4 Mon Sep 17 00:00:00 2001 From: Nick Chambers Date: Wed, 17 Nov 2021 16:29:47 -0600 Subject: [PATCH] Implement a state manager POC --- rowbot | 197 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 149 insertions(+), 48 deletions(-) diff --git a/rowbot b/rowbot index 8483545..b686263 100755 --- a/rowbot +++ b/rowbot @@ -97,23 +97,6 @@ b64_encode() { # code reloading helpers -get_option() { - local var_name=${1//-/_} - local env_var=${var_name^^} - - if [[ ! -v config[$1] || -v OPT_OW ]]; then - if [[ -v $env_var ]]; then - config[$1]=${!env_var} - elif [[ -v opts[$1] ]]; then - config[$1]=${opts[$1]} - elif [[ -v $var_name ]]; then - config[$1]=${!var_name} - elif (( $# > 1 )); then - config[$1]=$2 - fi - fi -} - is_reloaded() { [[ $RELOADED = yes ]] || (( RELOAD_COUNT )) } @@ -157,11 +140,7 @@ shuffle() { done } -# misc - -is_running () { - kill -0 "$1" 2>/dev/null -} +# process management and friends die() { local fmt=$1 @@ -181,6 +160,12 @@ has() { fi } +is_running () { + kill -0 "$1" 2>/dev/null +} + +# misc + run_callbacks() { if (( ! $# )); then return 1 @@ -245,12 +230,12 @@ url() { } ### -# configure rowbot's environment +# Prepare rowbot's configuration ### # parse command line arguments -declare -A opts +declare -A config opts cmd_line=( "${@:0}" ) while (( $# )); do @@ -280,6 +265,8 @@ done # load custom configuration files +mapfile -t old_set < <(compgen -v) + for file do if [[ -f $file ]]; then # These files (if any) are provided dynamically at run-time. @@ -290,27 +277,131 @@ for file do fi done +mapfile -t new_set < <(compgen -v) + +for new in "${new_set[@]}"; do + found=0 + + for old in "${old_set[@]}"; do + if [[ $new = "$old" ]]; then + found=1 + break + fi + done + + if (( !found )); then + config[$new]=${!new} + fi +done + +for setting in "${!opts[@]}"; do + config[$setting]=${opts[$setting]} +done + +while IFS= read -r setting; do + config[${setting,,}]=${!setting} +done < <(compgen -e) + # cleanup -unset key file +unset key opts old_set file new_set new found old setting ### -# load default config +# state management ### -declare -A config +state_resolve() { + local ns=${NS-global} managed found=0 + declare -gA __rowbot_state_store_"$ns" + declare -n ns_config=__rowbot_state_store_"$ns" -get_option owner "${USER:-uplime}" -get_option trigger \` -get_option dev yes + if [[ $ns != global ]]; then + for managed in "${states_managed[@]}"; do + if [[ $managed = "$ns" ]]; then + found=1 + break + fi + done + + if (( !found )); then + states_managed+=( "$ns" ) + fi + fi + + if [[ -v config[$1] ]]; then + ns_config[$1]=${config[$1]} + elif [[ -v DEFAULT ]]; then + ns_config[$1]=$DEFAULT + else + return 1 + fi +} + +state_get() { + local ns=${NS-global} + # The `ns_config` variable is a reference to an array + # shellcheck disable=SC2178 + declare -n ns_config=__rowbot_state_store_"$ns" + + if [[ -v ns_config[$1] ]]; then + printf %s "${ns_config[$1]}" + elif [[ -v DEFAULT ]]; then + printf %s "$DEFAULT" + else + return 1 + fi +} + +state_has() { + local ns=${NS-global} found=1 managed + declare -n ns_config=__rowbot_state_store_"$ns" + + for managed in "${ns_config[@]}"; do + if [[ $managed = "$1" ]]; then + found=0 + break + fi + done + + return "$found" +} + +on_sys_init_001_state() { + states_managed=( global ) +} + +on_sys_before_999_state() { + local managed + + for managed in "${states_managed[@]}"; do + put_assoc_array __rowbot_state_store_"$managed" + done + + put_array states_managed +} + +on_sys_after_001_state() { + local managed + get_array states_managed + + for managed in "${states_managed[@]}"; do + get_assoc_array __rowbot_state_store_"$managed" + done +} ### # logger ### log() { - if [[ -v LOG_LEVEL ]] && (( log_levels[$log_level] <= log_levels[$LOG_LEVEL] )); then - printf "%s: $1\n" "${LOG_LEVEL^^}" "${@:2}" >&"$log_fd" + if NS=log state_has fd; then + # The only possible fail conditions are already checked for. + # shellcheck disable=SC2155 + local level=$(NS=log state_get level) fd=$(NS=log state_get fd) + + if (( log_levels[$level] <= log_levels[$LOG_LEVEL] )); then + printf "%s: $1\n" "${LOG_LEVEL^^}" "${@:2}" >&"$fd" + fi fi } @@ -346,40 +437,50 @@ log_has_level() { return 1 } -on_sys_init_001_log() { +on_sys_init_005_log() { declare -gA log_levels=( [trace]=1 [debug]=2 [info]=3 [warn]=4 [error]=5 ) - get_option log-level info + NS=log DEFAULT=info state_resolve level - if ! log_has_level "${config[log-level]}"; then - die "%s is not a valid logging level" "${config[log-level]}" + if ! log_has_level "$(NS=log state_get level)"; then + die "%s is not a valid logging level" "$(NS=log state_get level)" fi - get_option log "" - get_option overwrite no - log_level=${config[log-level]} + local log_fd=1 - if [[ ${config[log]} ]]; then + if [[ -v ${config[log]} ]]; then if [[ ${config[overwrite]} = yes ]]; then exec {log_fd}>"${config[log]}" else exec {log_fd}>>"${config[log]}" fi - else - log_fd=1 fi + + NS=log DEFAULT=$log_fd state_resolve fd } on_sys_before_999_log() { - if [[ -v log_fd ]] && (( log_fd != 1 )); then - log_debug "shutting logger down for reload" - exec {log_fd}>&- + if NS=log state_has fd; then + # The only possible fail condition is already checked for. + # shellcheck disable=2155 + local fd=$(NS=log state_get fd) + + if (( fd != 1 )); then + log_debug "shutting logger down for reload" + exec {fd}>&- + fi fi } on_sys_exit_999_log() { - if [[ -v log_fd ]] && (( log_fd != 1 )); then - log_debug "shutting logger down for good" - exec {log_fd}>&- + if NS=log state_has fd; then + # The only possible fail condition is already checked for. + # shellcheck disable=2155 + local fd=$(NS=log state_get fd) + + if (( fd != 1 )); then + log_debug "shutting logger down for good" + exec {fd}>&- + fi fi }