function flag::parse() { local ctx="${_CURRENT_COMMAND:-__global__}" # Reset runtime _FLAG_VALUES=(); _FLAG_ARRAYS=(); _FLAG_SET=(); _FLAG_ARGS=() # Check for --help/-h first (fast path) local arg for arg in "$@"; do if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then local cmd="${_CURRENT_COMMAND%%::*}" declare -f hook::fire &>/dev/null && \ hook::fire "command:help:${cmd}" "$cmd" "${_CURRENT_COMMAND##*::}" return 1 fi done # Initialize from per-command index (fast — no full registry scan) local flag key type default_val for flag in ${_FLAG_INDEX[$ctx]:-}; do key="${ctx}:${flag}" [[ -z "${_FLAG_REGISTRY[$key]+x}" ]] && continue type="${_FLAG_REGISTRY[$key]%%|*}" default_val="${_FLAG_C_DEFAULT[$key]:-}" case "$type" in bool) _FLAG_VALUES["$flag"]="${default_val:-false}" ;; value) [[ -n "$default_val" ]] && _FLAG_VALUES["$flag"]="$default_val" ;; array) _FLAG_ARRAYS["$flag"]="" ;; esac done # Parse args while [[ $# -gt 0 ]]; do arg="$1" if [[ "$arg" == "--" ]]; then shift; _FLAG_ARGS+=("$@"); break fi if [[ "$arg" != --* ]]; then _FLAG_ARGS+=("$arg"); shift; continue fi key="${ctx}:${arg}" if [[ -z "${_FLAG_REGISTRY[$key]+x}" ]]; then log::error "Unknown flag: ${arg}"; return 1 fi type="${_FLAG_REGISTRY[$key]%%|*}" case "$type" in bool) _FLAG_VALUES["$arg"]="true" _FLAG_SET["$arg"]="1" shift ;; value) if [[ $# -lt 2 || "$2" == --* ]]; then log::error "Flag ${arg} requires a value"; return 1 fi local val="$2" local vtype="${_FLAG_C_TYPE[$key]:-}" if [[ "$vtype" == "int" ]]; then if ! [[ "$val" =~ ^[0-9]+$ ]]; then log::error "Flag ${arg} requires an integer, got: ${val}"; return 1 fi local min="${_FLAG_C_MIN[$key]:-}" max="${_FLAG_C_MAX[$key]:-}" [[ -n "$min" && "$val" -lt "$min" ]] && \ log::error "Flag ${arg} minimum is ${min}, got: ${val}" && return 1 [[ -n "$max" && "$val" -gt "$max" ]] && \ log::error "Flag ${arg} maximum is ${max}, got: ${val}" && return 1 fi local choices="${_FLAG_C_CHOICES[$key]:-}" if [[ -n "$choices" ]]; then local valid=false choice local OLD_IFS="$IFS" IFS='|' local -a choice_list=($choices) IFS="$OLD_IFS" local choice for choice in "${choice_list[@]}"; do [[ "$val" == "$choice" ]] && valid=true && break done unset IFS if ! $valid; then log::error "Flag ${arg} must be one of: ${choices//|/, }, got: ${val}" return 1 fi fi _FLAG_VALUES["$arg"]="$val" _FLAG_SET["$arg"]="1" shift 2 ;; array) if [[ $# -lt 2 || "$2" == --* ]]; then log::error "Flag ${arg} requires a value"; return 1 fi if [[ -n "${_FLAG_ARRAYS[$arg]:-}" ]]; then _FLAG_ARRAYS["$arg"]+=$'\n'"$2" else _FLAG_ARRAYS["$arg"]="$2" fi _FLAG_SET["$arg"]="1" shift 2 ;; esac done # Validate required for flag in ${_FLAG_INDEX[$ctx]:-}; do key="${ctx}:${flag}" [[ -z "${_FLAG_C_REQUIRED[$key]:-}" ]] && continue if [[ -z "${_FLAG_SET[$flag]+x}" && -z "${_FLAG_VALUES[$flag]:-}" ]]; then log::error "Flag ${flag} is required"; return 1 fi done # Validate exclusive groups local groups="${_FLAG_EXCLUSIVE_GROUPS[${_CURRENT_COMMAND%%::*}]:-}" if [[ -n "$groups" ]]; then local group while IFS= read -r group; do [[ -z "$group" ]] && continue local -a members=() local OLD_IFS="$IFS"; IFS=','; read -ra members <<< "$group"; IFS="$OLD_IFS" local found_count=0 found_flags="" member for member in "${members[@]}"; do flag::set "$member" && (( found_count++ )) && found_flags+=" $member" done if [[ $found_count -gt 1 ]]; then log::error "Flags${found_flags} are mutually exclusive"; return 1 fi done < <(printf '%s' "$groups" | tr '|' '\n') fi return 0 }