Repository navigation
Expand file tree
/
Copy pathflint_tools
More file actions
executable file
·154 lines (139 loc) · 3.72 KB
/
Copy pathflint_tools
File metadata and controls
executable file
·154 lines (139 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env bash
# Interactive launcher for Python scripts under tools/.
# Usage:
# ./flint_tools
# ./flint_tools 1 -- --list
# ./flint_tools gen_board_catalog -- --set-default
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TOOLS_DIR="$ROOT/tools"
cd "$ROOT"
script_summary() {
local path="$1"
python3 - "$path" <<'PY'
import ast, sys
path = sys.argv[1]
try:
src = open(path, encoding="utf-8").read()
tree = ast.parse(src, filename=path)
except (OSError, SyntaxError):
print("(unreadable)")
raise SystemExit(0)
doc = ast.get_docstring(tree)
if doc:
for line in doc.strip().splitlines():
line = line.strip()
if line:
print(line)
raise SystemExit(0)
for node in tree.body:
if isinstance(node, ast.Assign):
for t in node.targets:
if isinstance(t, ast.Name) and t.id == "PROG_DESCRIPTION":
v = node.value
if isinstance(v, ast.Constant) and isinstance(v.value, str):
print(v.value.strip().splitlines()[0].strip())
raise SystemExit(0)
print("(no description)")
PY
}
mapfile -t TOOL_PATHS < <(find "$TOOLS_DIR" -maxdepth 1 -type f -name '*.py' ! -name '_*' | sort)
if ((${#TOOL_PATHS[@]} == 0)); then
echo "no Python tools in tools/" >&2
exit 1
fi
# Split launcher args from tool args at "--"
LAUNCHER_ARGS=()
TOOL_ARGS=()
seen_dd=0
for a in "$@"; do
if ((seen_dd)); then
TOOL_ARGS+=("$a")
elif [[ "$a" == "--" ]]; then
seen_dd=1
else
LAUNCHER_ARGS+=("$a")
fi
done
if ((${#LAUNCHER_ARGS[@]} > 1)); then
echo "usage: ./flint_tools [n|name] [-- tool-args…]" >&2
echo " (interactive menu if choice omitted)" >&2
exit 1
fi
CHOICE="${LAUNCHER_ARGS[0]:-}"
resolve_choice() {
local raw="$1"
local i name stem
if [[ "$raw" =~ ^[0-9]+$ ]]; then
if ((raw >= 1 && raw <= ${#TOOL_PATHS[@]})); then
echo "${TOOL_PATHS[raw - 1]}"
return 0
fi
echo "invalid choice '$raw'; enter 1–${#TOOL_PATHS[@]}" >&2
return 1
fi
for path in "${TOOL_PATHS[@]}"; do
name="$(basename "$path")"
stem="${name%.py}"
if [[ "$raw" == "$name" || "$raw" == "$stem" ]]; then
echo "$path"
return 0
fi
done
echo -n "unknown tool '$raw'; available: " >&2
local names=()
for path in "${TOOL_PATHS[@]}"; do
names+=("$(basename "$path")")
done
(IFS=', '; echo "${names[*]}") >&2
return 1
}
SELECTED=""
if [[ -n "$CHOICE" ]]; then
SELECTED="$(resolve_choice "$CHOICE")" || exit 1
else
if [[ ! -t 0 ]]; then
echo "./flint_tools needs a TTY for the menu, or pass a choice:" >&2
echo " ./flint_tools <n|name> [-- tool-args…]" >&2
exit 1
fi
echo "FlinT OS tools:"
i=1
for path in "${TOOL_PATHS[@]}"; do
name="$(basename "$path")"
summary="$(script_summary "$path")"
printf ' %d. %s\n' "$i" "$name"
printf ' %s\n' "$summary"
((i++)) || true
done
echo " 0. cancel"
while true; do
read -r -p "Choice [number]: " raw || {
echo
echo "cancelled"
exit 0
}
raw="${raw#"${raw%%[![:space:]]*}"}"
raw="${raw%"${raw##*[![:space:]]}"}"
case "$raw" in
"" | 0 | q | Q)
echo "cancelled"
exit 0
;;
esac
if SELECTED="$(resolve_choice "$raw" 2>/dev/null)"; then
break
fi
echo " invalid choice '$raw'; enter 1–${#TOOL_PATHS[@]}, name, or 0 to cancel"
done
if ((${#TOOL_ARGS[@]} == 0)); then
read -r -p "Extra args (Enter for none): " extra || extra=""
if [[ -n "${extra// }" ]]; then
# shellcheck disable=SC2206
TOOL_ARGS=($extra)
fi
fi
fi
name="$(basename "$SELECTED")"
echo "+ python3 tools/$name${TOOL_ARGS[*]:+ ${TOOL_ARGS[*]}}"
exec python3 "$SELECTED" "${TOOL_ARGS[@]}"