#!/bin/bash
#  settings - setting variable support

set -e


######
# Settings Configuration

core_settings_config_init() {
	# $settings_dump - If `true`, settings are printed using ``debug``.
	# Default: ``$config_debug``
	lib_setting_vars settings_dump
	settings_dump=${settings_dump:-$config_debug}
	$debug || debug=$settings_dump

	# $settings_types[] - Lists the known types of settings:
	# `vars arrays assocs funcs`.
	lib_setting_arrays -ro settings_types
	settings_types=( vars arrays assocs funcs )

	lib_setting_arrays -ro settings_attrs
	settings_attrs=( readonly null )
}


######
# Setting Lists

# settings_varname() - Prints the name of a settings list, given
# the namespace ($1) and the setting type ($2).
settings_varname() { echo "${1}_settings_${2}"; }

settings_insert() {
	min_args 3 "$@"
	local ext=$1
	local name=$2
	shift 2

	# add them to the script settings array
	local settings
	settings=$(settings_varname "$name" "$ext")

	declare -ga $settings
	eval "$settings+=( \"\$@\" )"
}

# settings_add() - Adds the named settings ($@) to the given data type ($1).
# Declares the settings ($@) with the given ``declare`` options ($2) in
# the given script namespace ($3).
settings_add() {
	min_args 4 "$@"
	local ext=$1
	local opts=$2
	local name=$3
	shift 3

	local ro=false
	local null=false
	local got_opt=true
	while $got_opt; do
		case "$1" in
		(-ro|--readonly) ro=true; shift; continue ;;
		(--empty|--null) null=true; shift; continue ;;
		(-*) error "$1: unknown option" ;;
		esac
		got_opt=false
	done

	settings_insert "all" "$name" "$@"
	! $null || settings_insert "null" "$name" "$@"
	! $ro || settings_insert "readonly" "$name" "$@"
	settings_insert "$ext" "$name" "$@"

	# declare all of the nammed settings as globals
	declare -g $opts "$@"
}

settings_add_vars() { settings_add "vars" "" "$@"; }
settings_add_funcs() { settings_add "funcs" "-f" "$@"; }
settings_add_assocs() { settings_add "assocs" "-A" "$@"; }
settings_add_arrays() { settings_add "arrays" "-a" "$@"; }


settings_list() {
	has_args 2 "$@"
	local settings
	settings=$(settings_varname "$1" "$2")
	eval "echo \${${settings}[@]}"
}


######
# Setting Attributes

# settings_list_all() - Prints list of all known settings in the given
# namespace ($1).
settings_list_all() { settings_list "$1" "all"; }

# settings_list_vars() - Prints list of known atomic settings in the
# given namespace ($1).
settings_list_vars() { settings_list "$1" "vars"; }

# settings_list_funcs() - Prints list of known functions in the given
# namespace ($1).
settings_list_funcs() { settings_list "$1" "funcs"; }

# settings_list_assocs() - Prints list of known associate arrays in the
# given namespace ($1).
settings_list_assocs() { settings_list "$1" "assocs"; }

# settings_list_arrays() - Prints list of known indexed arrays in the
# given namespace ($1).
settings_list_arrays() { settings_list "$1" "arrays"; }

# settings_list_readonly()() - Prints list of read-only settings in the
# given namespace ($1).
settings_list_readonly() { settings_list "$1" "readonly"; }

# settings_list_null()() - Prints list of settings that may be `null`
# in the given namespace ($1).
settings_list_null() { settings_list "$1" "null"; }

#; settings_list_all2 - deprecated, but useful for testing
settings_list_all2() {
	echo $(for_each "settings_list $1" "${settings_types[@]}")
}

# settings_list_has_attr() - Checks if a given library ($2) setting ($1)
# exists in the given attribute list ($3).
#   $1 - Setting name
#   $2 - Library name
#   $3 - Attribute list to check
settings_list_has_attr() { in_list "$1" $(settings_list "$2" "$3"); }

# is_setting_readonly() - Checks if a setting ($1) in the given
# namespace ($2) is read-only.
is_setting_readonly() {
	has_args 2 "$@"
	settings_list_has_attr "$1" "$2" "readonly"
}

# is_setting_nullable() - Checks if a setting ($1) in the given
# namespace ($2) may be configured with a `null` value.
is_setting_nullable() {
	has_args 2 "$@"
	settings_list_has_attr "$1" "$2" "null"
}

######
# Setting Attribute Display

# settings_dump() - Dumps configuration settings for debugging
settings_dump() {
	$debug || return 0
	debug "Active Configuration:"
	for_each_lib_loaded settings_dump_lib
	settings_dump_tool
}

# settings_dump_lib() - Dumps configuration settings for a library script ($1).
settings_dump_lib() { settings_dump_script "$1" "$(lib_symbol "$1")"; }

# settings_dump_tool() - Dumps configuration settings for a tool script ($1).
settings_dump_tool() { settings_dump_script "$1" "$(script_symbol "$1")"; }

settings_dump_script() {
	local name=$1
	local sym=$2

	if [ -z "$(settings_list_all "$sym")" ]; then
		return
	fi

	local s
	debug "$name settings:"
	for s in $(settings_list_vars "$sym"); do
		local v
		eval "v=\"\$$s\""

		local ro=" (ro)"
		is_setting_readonly "$s" "$sym" || ro=""
		null=" (null)"
		is_setting_nullable "$s" "$sym" || null=""

		local attr="$ro$null"
		debug "\t$s='$v'$attr"
	done
	for s in $(settings_list_arrays "$sym"); do
		local v
		eval "v=\"( \${$s[*]} )\""
		debug "\t$s=$v"
	done
	for s in $(settings_list_assocs "$sym"); do
		debug "\t$s=("

		local -a sk
		eval "sk=( \"\${!${s}[@]}\" )"

		local k
		for k in "${sk[@]}"; do
			local v
			eval "v=\${$s[\$k]}"
			debug "\t\t$k=$v"
		done

		debug "\t\t)"
	done
}


######
# Setting Attribute Verification

# settings_check() - Verifies that all configuration settings are valid.
settings_check() {
	! $settings_dump || settings_dump

	info "checking configuration settings..."
	local -a settings_check_failures=()
	for_each_lib_loaded settings_check_lib
	settings_check_tool

	local failures="${settings_check_failures[*]}"
	[ "$failures" ] || return 0
	error "some scripts have configuration errors:\n\t$failures"
}

# settings_check_lib() - Verifies that configuration settings are valid
# for a library script ($1).
settings_check_lib() { settings_check_script "$1" "$(lib_symbol "$1")"; }

# settings_check_tool() - Verifies that configuration settings are valid
# for a tool script ($1).
settings_check_tool() { settings_check_script "$1" "$(script_symbol "$1")"; }

settings_check_script() {
	local name=$1
	local sym=$2

	if [ -z "$(settings_list_all "$sym")" ]; then
		return
	fi

	debug "$name: checking settings..."
	local -a vars
	vars=( $(settings_list_vars "$sym") )
	list_filter settings_check_invalid vars

	[ "${vars[*]}" ] || return 0

	for_each settings_check_report "${vars[@]}"
	settings_check_failures+=( "$name" )
}

settings_check_invalid() {
	local var=$1
	local -n value=$var
	! is_setting_nullable "$var" "$sym" && [ -z "$value" ]
}

settings_check_report() { warn "\$$1 is not set"; }


######
# Settings Attribute Finalization

# settings_final() - Finalizes all library and script settings.
settings_final() {
	for_each_lib_loaded settings_final_lib
}

# settings_final_lib() - Finalizes settings for a library script ($1)
# by applying or checking their configured variable attributes (e.g.
# `readonly` and `nullable`).
settings_final_lib() {
	local lib=$1
	local ext
	for ext in "${settings_types[@]}"; do
		local -a names
		names=( $(settings_list "$lib" "$ext") )

		list_filter "is_setting_readonly $lib" names
		[ "${names[*]}" ] || continue

		info "$lib: $ext: readonly: ${names[*]}"
		case "$ext" in
		(vars) typeset -gr "${names[@]}" ;;
		(arrays) typeset -gar "${names[@]}" ;;
		(assocs) typeset -gAr "${names[@]}" ;;
		(*) warn "$lib: $ext: readonly: ignoring ${names[*]}" ;;
		esac
	done
}

View the Developer Guide Index

View the Reference Manual Index


Generated on Fri Jul 28 14:35:04 PDT 2017 by mcsh d14 v0.23.0.