162 lines
4.4 KiB
Bash
Executable File
162 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# ===== SPEC =====
|
|
SPEC=(
|
|
"command;build;Build a dev container"
|
|
"argument;name;type:positional;required;help:Name of the container"
|
|
"argument;image,i;type:option;required;map:image_name;help:Base image"
|
|
"argument;include,I;type:option;repeatable;map:include_paths;help:Include paths"
|
|
"argument;verbose,v;type:flag;repeatable;default:0;help:Increase verbosity (-v, -vv, ...)"
|
|
"argument;quiet,q;type:flag;default:false;help:Suppress all output"
|
|
"argument;cmd;type:rest;map:cmd_rest;help:Command and args to run inside the container"
|
|
|
|
"command;stop;Stop a dev container"
|
|
"argument;name;type:positional;required;help:Name of the container"
|
|
"argument;kill,k;type:flag;default:false;help:Force kill the container"
|
|
)
|
|
|
|
# ===== CONSTANTS =====
|
|
ENTRY_COMMAND="command"
|
|
ENTRY_ARGUMENT="argument"
|
|
|
|
ATTR_REQUIRED="required"
|
|
ATTR_HELP="help"
|
|
ATTR_DEFAULT="default"
|
|
ATTR_MAP="map"
|
|
ATTR_TYPE="type"
|
|
ATTR_REPEATABLE="repeatable"
|
|
|
|
ATTR_TYPE_POSITIONAL="positional"
|
|
ATTR_TYPE_OPTION="option"
|
|
ATTR_TYPE_FLAG="flag"
|
|
|
|
# ===== STORAGE =====
|
|
declare -A SPEC_DATA
|
|
declare -A SPEC_ALIASES
|
|
|
|
# ===== GENERATE IDS =====
|
|
cmd_id=0
|
|
arg_id=0
|
|
get_cmd_id() { echo $((cmd_id++)); }
|
|
get_arg_id() { echo $((arg_id++)); }
|
|
|
|
# ===== PARSE SPEC ENTRY =====
|
|
parse_spec_entry() {
|
|
local raw_entry="$1"
|
|
IFS=';' read -r type alias_str rest <<<"$raw_entry"
|
|
IFS=',' read -r -a aliases <<<"$alias_str"
|
|
|
|
declare -A attr
|
|
|
|
if [[ "$type" == "$ENTRY_COMMAND" ]]; then
|
|
attr["$ATTR_HELP"]="$rest"
|
|
else
|
|
attr["$ATTR_REQUIRED"]=false
|
|
attr["$ATTR_REPEATABLE"]=false
|
|
attr["$ATTR_TYPE"]="$ATTR_TYPE_POSITIONAL"
|
|
attr["$ATTR_MAP"]=""
|
|
attr["$ATTR_DEFAULT"]=""
|
|
attr["$ATTR_HELP"]=""
|
|
|
|
# Re-split rest on ';'
|
|
IFS=';' read -r -a elements <<<"$rest"
|
|
for element in "${elements[@]}"; do
|
|
[[ -z "$element" ]] && continue
|
|
IFS=':' read -r attribute value <<<"$element"
|
|
case "$attribute" in
|
|
"$ATTR_REQUIRED") attr["$ATTR_REQUIRED"]=true ;;
|
|
"$ATTR_REPEATABLE") attr["$ATTR_REPEATABLE"]=true ;;
|
|
"$ATTR_TYPE") attr["$ATTR_TYPE"]="$value" ;;
|
|
"$ATTR_MAP") attr["$ATTR_MAP"]="$value" ;;
|
|
"$ATTR_DEFAULT") attr["$ATTR_DEFAULT"]="$value" ;;
|
|
"$ATTR_HELP") attr["$ATTR_HELP"]="$value" ;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
# Serialize attributes to string
|
|
local attr_str=""
|
|
for k in "${!attr[@]}"; do
|
|
attr_str+="$k=${attr[$k]}|"
|
|
done
|
|
attr_str="${attr_str%|}"
|
|
|
|
echo "$type;$alias_str;$attr_str"
|
|
}
|
|
|
|
# ===== PARSE SPEC =====
|
|
parse_spec() {
|
|
local command_filter="${1:-}"
|
|
local skip_cmd=false
|
|
local exit_on_next_cmd=false
|
|
local current_cmd_id=""
|
|
|
|
for raw_entry in "${SPEC[@]}"; do
|
|
local entry
|
|
entry="$(parse_spec_entry "$raw_entry")"
|
|
IFS=';' read -r type alias_str attr_str <<<"$entry"
|
|
IFS=',' read -r -a aliases <<<"$alias_str"
|
|
|
|
if [[ "$type" == "$ENTRY_COMMAND" ]]; then
|
|
if [[ "$exit_on_next_cmd" == true ]]; then
|
|
break
|
|
fi
|
|
|
|
if [[ -n "$command_filter" ]]; then
|
|
local found=false
|
|
for a in "${aliases[@]}"; do
|
|
[[ "$a" == "$command_filter" ]] && found=true && break
|
|
done
|
|
if [[ "$found" == false ]]; then
|
|
skip_cmd=true
|
|
continue
|
|
fi
|
|
skip_cmd=false
|
|
exit_on_next_cmd=true
|
|
fi
|
|
|
|
current_cmd_id="$(get_cmd_id)"
|
|
SPEC_DATA["$current_cmd_id.type"]="$type"
|
|
SPEC_DATA["$current_cmd_id.aliases"]="$alias_str"
|
|
SPEC_DATA["$current_cmd_id.attr"]="$attr_str"
|
|
SPEC_DATA["$current_cmd_id.id"]="$current_cmd_id"
|
|
|
|
for a in "${aliases[@]}"; do
|
|
SPEC_ALIASES["$a"]="$current_cmd_id"
|
|
done
|
|
elif [[ "$type" == "$ENTRY_ARGUMENT" ]]; then
|
|
if [[ "$skip_cmd" == true ]]; then
|
|
continue
|
|
fi
|
|
local this_arg_id
|
|
this_arg_id="$(get_arg_id)"
|
|
SPEC_DATA["$current_cmd_id.$this_arg_id.type"]="$type"
|
|
SPEC_DATA["$current_cmd_id.$this_arg_id.aliases"]="$alias_str"
|
|
SPEC_DATA["$current_cmd_id.$this_arg_id.attr"]="$attr_str"
|
|
SPEC_DATA["$current_cmd_id.$this_arg_id.id"]="$this_arg_id"
|
|
|
|
for a in "${aliases[@]}"; do
|
|
SPEC_ALIASES["$current_cmd_id.$a"]="$this_arg_id"
|
|
done
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ===== MAIN =====
|
|
parse_spec
|
|
|
|
# Print results as JSON-like structure
|
|
echo "{"
|
|
echo " \"spec_data\": {"
|
|
for k in "${!SPEC_DATA[@]}"; do
|
|
printf ' "%s": "%s",\n' "$k" "${SPEC_DATA[$k]}"
|
|
done
|
|
echo " },"
|
|
echo " \"spec_aliases\": {"
|
|
for k in "${!SPEC_ALIASES[@]}"; do
|
|
printf ' "%s": "%s",\n' "$k" "${SPEC_ALIASES[$k]}"
|
|
done
|
|
echo " }"
|
|
echo "}"
|