Zuletzt aktiv 1 month ago

gistfile1.txt Originalformat
1function flag::parse() {
2 local ctx="${_CURRENT_COMMAND:-__global__}"
3
4 # Reset runtime
5 _FLAG_VALUES=(); _FLAG_ARRAYS=(); _FLAG_SET=(); _FLAG_ARGS=()
6
7 # Check for --help/-h first (fast path)
8 local arg
9 for arg in "$@"; do
10 if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then
11 local cmd="${_CURRENT_COMMAND%%::*}"
12 declare -f hook::fire &>/dev/null && \
13 hook::fire "command:help:${cmd}" "$cmd" "${_CURRENT_COMMAND##*::}"
14 return 1
15 fi
16 done
17
18 # Initialize from per-command index (fast — no full registry scan)
19 local flag key type default_val
20 for flag in ${_FLAG_INDEX[$ctx]:-}; do
21 key="${ctx}:${flag}"
22 [[ -z "${_FLAG_REGISTRY[$key]+x}" ]] && continue
23 type="${_FLAG_REGISTRY[$key]%%|*}"
24 default_val="${_FLAG_C_DEFAULT[$key]:-}"
25 case "$type" in
26 bool) _FLAG_VALUES["$flag"]="${default_val:-false}" ;;
27 value) [[ -n "$default_val" ]] && _FLAG_VALUES["$flag"]="$default_val" ;;
28 array) _FLAG_ARRAYS["$flag"]="" ;;
29 esac
30 done
31
32 # Parse args
33 while [[ $# -gt 0 ]]; do
34 arg="$1"
35
36 if [[ "$arg" == "--" ]]; then
37 shift; _FLAG_ARGS+=("$@"); break
38 fi
39
40 if [[ "$arg" != --* ]]; then
41 _FLAG_ARGS+=("$arg"); shift; continue
42 fi
43
44 key="${ctx}:${arg}"
45 if [[ -z "${_FLAG_REGISTRY[$key]+x}" ]]; then
46 log::error "Unknown flag: ${arg}"; return 1
47 fi
48
49 type="${_FLAG_REGISTRY[$key]%%|*}"
50
51 case "$type" in
52 bool)
53 _FLAG_VALUES["$arg"]="true"
54 _FLAG_SET["$arg"]="1"
55 shift
56 ;;
57 value)
58 if [[ $# -lt 2 || "$2" == --* ]]; then
59 log::error "Flag ${arg} requires a value"; return 1
60 fi
61 local val="$2"
62 local vtype="${_FLAG_C_TYPE[$key]:-}"
63 if [[ "$vtype" == "int" ]]; then
64 if ! [[ "$val" =~ ^[0-9]+$ ]]; then
65 log::error "Flag ${arg} requires an integer, got: ${val}"; return 1
66 fi
67 local min="${_FLAG_C_MIN[$key]:-}" max="${_FLAG_C_MAX[$key]:-}"
68 [[ -n "$min" && "$val" -lt "$min" ]] && \
69 log::error "Flag ${arg} minimum is ${min}, got: ${val}" && return 1
70 [[ -n "$max" && "$val" -gt "$max" ]] && \
71 log::error "Flag ${arg} maximum is ${max}, got: ${val}" && return 1
72 fi
73 local choices="${_FLAG_C_CHOICES[$key]:-}"
74 if [[ -n "$choices" ]]; then
75 local valid=false choice
76 local OLD_IFS="$IFS"
77 IFS='|'
78 local -a choice_list=($choices)
79 IFS="$OLD_IFS"
80 local choice
81 for choice in "${choice_list[@]}"; do
82 [[ "$val" == "$choice" ]] && valid=true && break
83 done
84 unset IFS
85 if ! $valid; then
86 log::error "Flag ${arg} must be one of: ${choices//|/, }, got: ${val}"
87 return 1
88 fi
89 fi
90 _FLAG_VALUES["$arg"]="$val"
91 _FLAG_SET["$arg"]="1"
92 shift 2
93 ;;
94 array)
95 if [[ $# -lt 2 || "$2" == --* ]]; then
96 log::error "Flag ${arg} requires a value"; return 1
97 fi
98 if [[ -n "${_FLAG_ARRAYS[$arg]:-}" ]]; then
99 _FLAG_ARRAYS["$arg"]+=$'\n'"$2"
100 else
101 _FLAG_ARRAYS["$arg"]="$2"
102 fi
103 _FLAG_SET["$arg"]="1"
104 shift 2
105 ;;
106 esac
107 done
108
109 # Validate required
110 for flag in ${_FLAG_INDEX[$ctx]:-}; do
111 key="${ctx}:${flag}"
112 [[ -z "${_FLAG_C_REQUIRED[$key]:-}" ]] && continue
113 if [[ -z "${_FLAG_SET[$flag]+x}" && -z "${_FLAG_VALUES[$flag]:-}" ]]; then
114 log::error "Flag ${flag} is required"; return 1
115 fi
116 done
117
118 # Validate exclusive groups
119 local groups="${_FLAG_EXCLUSIVE_GROUPS[${_CURRENT_COMMAND%%::*}]:-}"
120 if [[ -n "$groups" ]]; then
121 local group
122 while IFS= read -r group; do
123 [[ -z "$group" ]] && continue
124 local -a members=()
125 local OLD_IFS="$IFS"; IFS=','; read -ra members <<< "$group"; IFS="$OLD_IFS"
126 local found_count=0 found_flags="" member
127 for member in "${members[@]}"; do
128 flag::set "$member" && (( found_count++ )) && found_flags+=" $member"
129 done
130 if [[ $found_count -gt 1 ]]; then
131 log::error "Flags${found_flags} are mutually exclusive"; return 1
132 fi
133 done < <(printf '%s' "$groups" | tr '|' '\n')
134 fi
135
136 return 0
137}