diff --git a/barg.sh b/barg.sh index 5426eeb..9c6d56f 100755 --- a/barg.sh +++ b/barg.sh @@ -8,22 +8,12 @@ if [ "${BASH_VERSINFO:-0}" -lt 4 ]; then fi # --- globals --- +BARG_VAR_SUFFIX="_arg" declare -A BARG_PARSED_VALUES declare -a BARG_PARSED_COMMANDS declare -A BARG_SCHEMA declare -A BARG_ALIASES BARG_HELP_ONLY=false -BARG_GENERATE_VARS=false # new flag to control var file generation - -# --- Auto-source generated ShellCheck stub vars --- -_barg_self="${BASH_SOURCE[0]}" -_barg_dir="$(cd "$(dirname "$_barg_self")" && pwd)" -_barg_stub="${_barg_dir}/$(basename "${_barg_self%.*}").vars.generated.sh" -if [[ -f "$_barg_stub" ]]; then - # shellcheck source=/dev/null - source "$_barg_stub" -fi -unset _barg_self _barg_dir _barg_stub # -------------------------------------------------------------------- # Parse CLI args according to spec @@ -123,8 +113,10 @@ parse_arguments() { local help_text="${elements[2]}" schema["${id}.entryType"]="command" schema["${id}.name"]="${aliases_str%%,*}" + schema["${id}.aliases"]="$aliases_str" schema["${id}.help"]="$help_text" schema["${id}.args"]="" + schema["${id}.cmds"]="" if [[ ${#cmd_stack[@]} -eq 0 ]]; then aliases["cmd::root"]="$id" @@ -134,6 +126,10 @@ parse_arguments() { local parent="${cmd_stack[-1]}" aliases["${parent}.cmd::${alias}"]="$id" done + + local parent="${cmd_stack[-1]}" + local parent_cmds="${schema["${parent}.cmds"]}" + schema["${parent}.cmds"]="${parent_cmds:+$parent_cmds,}$id" fi ((level++)) @@ -193,6 +189,18 @@ parse_arguments() { ;; esac + ((entry_id++)) + ;; + note) + local id="$entry_id" + local text="${elements[1]}" + schema["${id}.entryType"]="note" + schema["${id}.text"]="$text" + + # attach note to the current command + local parent="${cmd_stack[-1]}" + local notes="${schema["${parent}.notes"]}" + schema["${parent}.notes"]="${notes:+$notes,}$id" ((entry_id++)) ;; *) @@ -298,7 +306,6 @@ parse_arguments() { ((token_idx++)) done - # extract BARG_PARSED_COMMANDS=() BARG_PARSED_VALUES=() for cmd_id in "${cmd_stack[@]}"; do @@ -311,9 +318,10 @@ parse_arguments() { for arg_id in "${arg_ids[@]}"; do local dest="${schema["${arg_id}.dest"]}" local value="${schema["${arg_id}.value"]}" + local dest_val="${BARG_PARSED_VALUES[$dest]}" local required="${schema["${arg_id}.required"]}" local atype="${schema["${arg_id}.type"]}" - if [[ -z "$value" && "$required" == "true" && "$BARG_HELP_ONLY" != "true" ]]; then + if [[ -z "$value" && -z "$dest_val" && "$required" == "true" && "$BARG_HELP_ONLY" != "true" ]]; then echo "Error: Argument \"$dest\" required" >&2 return 1 fi @@ -321,11 +329,7 @@ parse_arguments() { continue fi if [[ -n "$value" ]]; then - if [[ -n "${BARG_PARSED_VALUES[$dest]}" ]]; then - BARG_PARSED_VALUES["${cmd_name}_${dest}"]="$value" - else - BARG_PARSED_VALUES["$dest"]="$value" - fi + BARG_PARSED_VALUES["$dest"]="$value" fi done done @@ -346,7 +350,6 @@ barg_usage() { local -a cmd_path=("$@") local cmd_id="${BARG_ALIASES["cmd::root"]}" - # resolve command path if [[ ${#cmd_path[@]} -gt 0 ]]; then for cmd_name in "${cmd_path[@]}"; do local next_id="${BARG_ALIASES["${cmd_id}.cmd::${cmd_name}"]}" @@ -365,7 +368,14 @@ barg_usage() { echo "$help_text" } - # --- list arguments --- + local cmd_notes="${BARG_SCHEMA["${cmd_id}.notes"]}" + if [[ -n "$cmd_notes" ]]; then + IFS=',' read -ra note_ids <<<"$cmd_notes" + for nid in "${note_ids[@]}"; do + echo " ${BARG_SCHEMA["${nid}.text"]}" + done + fi + local cmd_args="${BARG_SCHEMA["${cmd_id}.args"]}" if [[ -n "$cmd_args" ]]; then echo "" @@ -394,35 +404,38 @@ barg_usage() { done fi - # --- list subcommands --- - local has_subcommands=false - for key in "${!BARG_ALIASES[@]}"; do - if [[ "$key" =~ ^${cmd_id}\.cmd::(.+)$ ]]; then - if [[ "$has_subcommands" == "false" ]]; then - echo "" - echo "Commands:" - has_subcommands=true + local cmd_children="${BARG_SCHEMA["${cmd_id}.cmds"]}" + if [[ -n "$cmd_children" ]]; then + echo "" + echo "Commands:" + IFS=',' read -ra child_ids <<<"$cmd_children" + for child_id in "${child_ids[@]}"; do + local sub_name="${BARG_SCHEMA["${child_id}.name"]}" + local sub_help="${BARG_SCHEMA["${child_id}.help"]}" + local sub_aliases="${BARG_SCHEMA["${child_id}.aliases"]}" + + # split aliases and remove duplicates + IFS=',' read -ra alias_list <<<"$sub_aliases" + local alias_display="" + if ((${#alias_list[@]} > 1)); then + alias_display=" (aliases: ${alias_list[*]:1})" fi - local sub_cmd="${BASH_REMATCH[1]}" - local sub_id="${BARG_ALIASES[$key]}" - local sub_help="${BARG_SCHEMA["${sub_id}.help"]}" - echo " ${sub_cmd}" + + echo " ${sub_name}${alias_display}" [[ -n "$sub_help" ]] && echo " ${sub_help}" - fi - done + done + fi } # -------------------------------------------------------------------- # Export parsed vars # -------------------------------------------------------------------- barg_export_vars() { - local k base + local k dest + : "${BARG_VAR_SUFFIX:=}" for k in "${!BARG_PARSED_VALUES[@]}"; do - declare -g "${k}=${BARG_PARSED_VALUES[$k]}" - if [[ "$k" =~ ^[^_]+_(.+)$ ]]; then - base="${BASH_REMATCH[1]}" - [[ -z "${!base+x}" ]] && declare -g "${base}=${BARG_PARSED_VALUES[$k]}" - fi + dest="${k}${BARG_VAR_SUFFIX}" + declare -g "${dest}=${BARG_PARSED_VALUES[$k]}" done } @@ -443,33 +456,6 @@ barg_dispatch() { fi } -# -------------------------------------------------------------------- -# Helper: collect all .dest and command_dest variants -# -------------------------------------------------------------------- -_barg_collect_all_dests() { - local -n _schema=$1 - local -A seen=() - for key in "${!_schema[@]}"; do - [[ $key =~ \.dest$ ]] || continue - local dest="${_schema[$key]}" - [[ -z "$dest" ]] && continue - seen[$dest]=1 - done - for key in "${!_schema[@]}"; do - [[ $key =~ ^([0-9]+)\.name$ ]] || continue - local cmd_id="${BASH_REMATCH[1]}" - local cmd="${_schema[$key]}" - local args_str="${_schema[${cmd_id}.args]}" - [[ -z "$args_str" ]] && continue - IFS=',' read -r -a arg_ids <<<"$args_str" - for a in "${arg_ids[@]}"; do - local d="${_schema[${a}.dest]}" - [[ -n "$d" ]] && seen["${cmd}_${d}"]=1 - done - done - printf '%s\n' "${!seen[@]}" | sort -} - # -------------------------------------------------------------------- # Entry point # -------------------------------------------------------------------- @@ -486,31 +472,6 @@ barg_run() { return 0 fi - local -a caller_stack=("${BASH_SOURCE[@]}") - local caller="${caller_stack[1]}" - local out_file="${caller%.*}.vars.generated.sh" - - # Only generate when explicitly requested - if [[ "$BARG_GENERATE_VARS" == "true" || "$BARG_REGEN_VARS" == "1" ]]; then - mapfile -t vars < <(_barg_collect_all_dests BARG_SCHEMA) - { - echo "# ---- autogenerated: BARG vars for ShellCheck ----" - echo "# generated from $(basename "$caller")" - echo "# regenerate with: BARG_REGEN_VARS=1 ./$(basename "$caller") ..." - echo "# shellcheck disable=SC2034" - echo "if false; then" - echo " CLI_SPEC=()" - printf ' %s=\n' "${vars[@]}" | paste -sd' ' - - echo "fi" - } >"$out_file" - fi - - # Source file if it already exists (not required to generate) - if [[ -f "$out_file" ]]; then - # shellcheck source=/dev/null - source "$out_file" - fi - barg_dispatch }