wgctl/core/command.sh
Nuno Duque Nunes a3fe7f5986 feat: command mixin system, --json output for list/inspect
- 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
2026-05-26 23:18:56 +00:00

83 lines
2 KiB
Bash

#!/usr/bin/env bash
# ============================================
# Command Registry
# ============================================
declare -A _LOADED_COMMANDS=()
readonly _COMMAND_NAMESPACE="cmd"
readonly _COMMAND_AUTO_LOAD_HOOK="on_load"
# ============================================
# Helpers
# ============================================
function command::loaded() { [[ -n "${_LOADED_COMMANDS["$1"]:-}" ]]; }
# Convert path-style name to namespace
# e.g. service/wireguard -> service::wireguard
function command::to_namespace() { echo "${1//\//:}"; }
# Build fully qualified function name
# e.g. command::fn "add" "run" -> cmd::add::run
# e.g. command::fn "service/wg" "run" -> cmd::service::wg::run
function command::fn() {
local name namespace
namespace=$(command::to_namespace "$1")
echo "${_COMMAND_NAMESPACE}::${namespace}::${2}"
}
function command::has_function() { declare -F "$(command::fn "$1" "$2")" >/dev/null 2>&1; }
function command::is_auto_load() { declare -F "$(command::fn "$1" on_load)" >/dev/null 2>&1; }
function command::exists() { command::has_function "$1" run; }
# ============================================
# Runner
# ============================================
function command::run() {
local cmd="$1"
shift
command::_reset_mixin_state # reset values only, keep _ACTIVE_MIXINS
local -a args=("$@")
command::_preprocess_flags args
local fn
fn=$(command::fn "$cmd" run)
core::call_function "$fn" "${args[@]}"
}
function core::call_function() {
local fn="$1"
shift
"$fn" "$@"
}
# ============================================
# Loader
# ============================================
function load_command() {
local name="$1"
command::loaded "$name" && return 0
local path
path="$(ctx::commands)/${name}.command.sh"
if [[ ! -f "$path" ]]; then
log::error "Command not found: ${name} (${path})"
return 1
fi
source "$path"
_LOADED_COMMANDS["$name"]=1
core::call_if_exists "$(command::fn "$name" on_load)"
return 0
}