- core/command_mixins.sh: mixin infrastructure with auto-loader - core/mixins/json_output.mixin.sh: --json flag mixin - core/mixins/no_color.mixin.sh: --no-color flag mixin - commands/mixins/MIXIN_TEMPLATE.mixin.sh: template for new mixins - command::run: reset mixin state, preprocess flags before dispatch - command::_preprocess_flags: nameref-based flag stripping, empty array fix - command::mixin: opt-in registration from on_load - list --json: structured JSON output with envelope - inspect --json: structured JSON peer detail output - json::envelope, json::error_envelope helpers
108 lines
No EOL
3.1 KiB
Bash
108 lines
No EOL
3.1 KiB
Bash
#!/usr/bin/env bash
|
|
# core/command_mixins.sh
|
|
# Mixin infrastructure — loads mixin files and provides command::mixin
|
|
|
|
# ============================================
|
|
# Active mixin tracking (per-process)
|
|
# ============================================
|
|
|
|
declare -a _ACTIVE_MIXINS=()
|
|
|
|
# ============================================
|
|
# Auto-load all mixin files
|
|
# ============================================
|
|
|
|
function command::_load_mixins() {
|
|
local mixin_file
|
|
local -a mixin_paths=(
|
|
"${WGCTL_DIR}/core/mixins/"*.mixin.sh
|
|
"${WGCTL_DIR}/commands/mixins/"*.mixin.sh
|
|
)
|
|
for mixin_file in "${mixin_paths[@]:-}"; do
|
|
[[ -f "$mixin_file" ]] && source "$mixin_file"
|
|
done
|
|
}
|
|
|
|
# ============================================
|
|
# command::mixin <name>
|
|
# Called from cmd::<name>::on_load to opt into a mixin
|
|
# ============================================
|
|
|
|
function command::mixin() {
|
|
local name="${1:-}"
|
|
[[ -z "$name" ]] && log::error "command::mixin: missing name" && return 1
|
|
|
|
local register_fn="command::mixin::${name}::register"
|
|
if declare -f "$register_fn" >/dev/null 2>&1; then
|
|
# Track for reset/process — avoid duplicates
|
|
local m already=false
|
|
for m in "${_ACTIVE_MIXINS[@]:-}"; do
|
|
[[ "$m" == "$name" ]] && already=true && break
|
|
done
|
|
$already || _ACTIVE_MIXINS+=("$name")
|
|
"$register_fn"
|
|
else
|
|
log::error "Unknown mixin: ${name} (no command::mixin::${name}::register found)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================
|
|
# command::_reset_mixins
|
|
# Called by command::run before each invocation
|
|
# ============================================
|
|
|
|
function command::_reset_mixin_state() {
|
|
# Reset values but keep _ACTIVE_MIXINS populated
|
|
local m
|
|
for m in "${_ACTIVE_MIXINS[@]:-}"; do
|
|
local reset_fn="command::mixin::${m}::reset"
|
|
declare -f "$reset_fn" >/dev/null 2>&1 && "$reset_fn"
|
|
done
|
|
}
|
|
|
|
function command::_reset_mixins() {
|
|
_ACTIVE_MIXINS=()
|
|
local reset_fn mixin_file
|
|
# Reset all known mixins regardless of active state
|
|
for mixin_file in \
|
|
"${WGCTL_DIR}/core/mixins/"*.mixin.sh \
|
|
"${WGCTL_DIR}/commands/mixins/"*.mixin.sh; do
|
|
[[ -f "$mixin_file" ]] || continue
|
|
local mixin_name
|
|
mixin_name=$(basename "$mixin_file" .mixin.sh)
|
|
reset_fn="command::mixin::${mixin_name}::reset"
|
|
declare -f "$reset_fn" >/dev/null 2>&1 && "$reset_fn"
|
|
done
|
|
}
|
|
|
|
# ============================================
|
|
# command::_preprocess_flags <args_nameref>
|
|
# Called by command::run — strips mixin flags from args
|
|
# ============================================
|
|
|
|
function command::_preprocess_flags() {
|
|
local -n _args_ref="$1"
|
|
local -a _filtered=()
|
|
|
|
for _arg in "${_args_ref[@]:-}"; do
|
|
local _consumed=false
|
|
local _mixin
|
|
for _mixin in "${_ACTIVE_MIXINS[@]:-}"; do
|
|
local _process_fn="command::mixin::${_mixin}::process"
|
|
if declare -f "$_process_fn" >/dev/null 2>&1; then
|
|
if "$_process_fn" "$_arg"; then
|
|
_consumed=true
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
$_consumed || _filtered+=("$_arg")
|
|
done
|
|
|
|
if [[ ${#_filtered[@]} -gt 0 ]]; then
|
|
_args_ref=("${_filtered[@]}")
|
|
else
|
|
_args_ref=()
|
|
fi
|
|
} |