Skip to content

Commit e84875f

Browse files
authored
Improve practice exercise generator script #1648
Stub generator patch
2 parents 29afef9 + 72080cb commit e84875f

File tree

10 files changed

+159
-153
lines changed

10 files changed

+159
-153
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ jobs:
8888

8989
- name: Run shellcheck
9090
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
91+
env:
92+
SHELLCHECK_OPTS: -x -s bash -e SC2001 --norc
9193

9294
compilation:
9395
name: Check compilation

bin/.shellcheckrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
shell=bash
2+
external-sources=true
3+
disable=SC2001

bin/add_practice_exercise

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
#!/usr/bin/env bash
22

3-
# shellcheck source=/dev/null
3+
# see comment in generator-utils/utils.sh
4+
# shellcheck source=bin/generator-utils/utils.sh
5+
# shellcheck source=bin/generator-utils/templates.sh
6+
# shellcheck source=bin/generator-utils/prompts.sh
7+
# shellcheck source=./generator-utils/utils.sh
8+
# shellcheck source=./generator-utils/prompts.sh
9+
# shellcheck source=./generator-utils/templates.sh
10+
411
source ./bin/generator-utils/utils.sh
512
source ./bin/generator-utils/prompts.sh
613
source ./bin/generator-utils/templates.sh
@@ -30,51 +37,52 @@ command -v curl >/dev/null 2>&1 || {
3037
exit 1
3138
}
3239

40+
# Build configlet
41+
bin/fetch-configlet
42+
message "success" "Fetched configlet successfully!"
43+
3344
# Check if exercise exists in configlet info or in config.json
3445
check_exercise_existence "$1"
3546

3647
# ==================================================
3748

38-
SLUG="$1"
39-
HAS_CANONICAL_DATA=true
49+
slug="$1"
4050
# Fetch canonical data
41-
canonical_json=$(bin/fetch_canonical_data "$SLUG")
51+
canonical_json=$(bin/fetch_canonical_data "$slug")
4252

53+
has_canonical_data=true
4354
if [ "${canonical_json}" == "404: Not Found" ]; then
44-
HAS_CANONICAL_DATA=false
55+
has_canonical_data=false
4556
message "warning" "This exercise doesn't have canonical data"
4657

4758
else
4859
echo "$canonical_json" >canonical_data.json
4960
message "success" "Fetched canonical data successfully!"
5061
fi
5162

52-
UNDERSCORED_SLUG=$(dash_to_underscore "$SLUG")
53-
EXERCISE_DIR="exercises/practice/${SLUG}"
54-
EXERCISE_NAME=$(format_exercise_name "$SLUG")
55-
message "info" "Using ${YELLOW}${EXERCISE_NAME}${BLUE} as a default exercise name. You can edit this later in the config.json file"
63+
underscored_slug=$(dash_to_underscore "$slug")
64+
exercise_dir="exercises/practice/${slug}"
65+
exercise_name=$(format_exercise_name "$slug")
66+
message "info" "Using ${yellow}${exercise_name}${blue} as a default exercise name. You can edit this later in the config.json file"
5667
# using default value for difficulty
57-
EXERCISE_DIFFICULTY=$(validate_difficulty_input "${2:-$(get_exercise_difficulty)}")
58-
message "info" "The exercise difficulty has been set to ${YELLOW}${EXERCISE_DIFFICULTY}${BLUE}. You can edit this later in the config.json file"
68+
exercise_difficulty=$(validate_difficulty_input "${2:-$(get_exercise_difficulty)}")
69+
message "info" "The exercise difficulty has been set to ${yellow}${exercise_difficulty}${blue}. You can edit this later in the config.json file"
5970
# using default value for author
60-
AUTHOR_HANDLE=${3:-$(get_author_handle)}
61-
message "info" "Using ${YELLOW}${AUTHOR_HANDLE}${BLUE} as author's handle. You can edit this later in the 'authors' field in the ${EXERCISE_DIR}/.meta/config.json file"
62-
63-
64-
create_rust_files "$EXERCISE_DIR" "$SLUG" "$HAS_CANONICAL_DATA"
71+
author_handle=${3:-$(get_author_handle)}
72+
message "info" "Using ${yellow}${author_handle}${blue} as author's handle. You can edit this later in the 'authors' field in the ${exercise_dir}/.meta/config.json file"
6573

74+
create_rust_files "$exercise_dir" "$slug" "$has_canonical_data"
6675

6776
# ==================================================
6877

69-
# build configlet
70-
./bin/fetch-configlet
71-
message "success" "Fetched configlet successfully!"
7278

7379
# Preparing config.json
7480
message "info" "Adding instructions and configuration files..."
75-
UUID=$(bin/configlet uuid)
7681

77-
jq --arg slug "$SLUG" --arg uuid "$UUID" --arg name "$EXERCISE_NAME" --arg difficulty "$EXERCISE_DIFFICULTY" \
82+
uuid=$(bin/configlet uuid)
83+
84+
# Add exercise-data to global config.json
85+
jq --arg slug "$slug" --arg uuid "$uuid" --arg name "$exercise_name" --arg difficulty "$exercise_difficulty" \
7886
'.exercises.practice += [{slug: $slug, name: $name, uuid: $uuid, practices: [], prerequisites: [], difficulty: $difficulty}]' \
7987
config.json >config.json.tmp
8088
# jq always rounds whole numbers, but average_run_time needs to be a float
@@ -84,18 +92,21 @@ message "success" "Added instructions and configuration files"
8492

8593
# Create instructions and config files
8694
echo "Creating instructions and config files"
87-
./bin/configlet sync --update --yes --docs --metadata --exercise "$SLUG"
88-
./bin/configlet sync --update --yes --filepaths --exercise "$SLUG"
89-
./bin/configlet sync --update --tests include --exercise "$SLUG"
95+
96+
./bin/configlet sync --update --yes --docs --metadata --exercise "$slug"
97+
./bin/configlet sync --update --yes --filepaths --exercise "$slug"
98+
./bin/configlet sync --update --tests include --exercise "$slug"
9099
message "success" "Created instructions and config files"
91100

92-
META_CONFIG="$EXERCISE_DIR"/.meta/config.json
93-
jq --arg author "$AUTHOR_HANDLE" '.authors += [$author]' "$META_CONFIG" >"$META_CONFIG".tmp && mv "$META_CONFIG".tmp "$META_CONFIG"
101+
# Push author to "authors" array in ./meta/config.json
102+
meta_config="$exercise_dir"/.meta/config.json
103+
jq --arg author "$author_handle" '.authors += [$author]' "$meta_config" >"$meta_config".tmp && mv "$meta_config".tmp "$meta_config"
94104
message "success" "You've been added as the author of this exercise."
95105

96-
sed -i "s/name = \".*\"/name = \"$UNDERSCORED_SLUG\"/" "$EXERCISE_DIR"/Cargo.toml
106+
sed -i "s/name = \".*\"/name = \"$underscored_slug\"/" "$exercise_dir"/Cargo.toml
97107

98108
message "done" "All stub files were created."
99109

100110
message "info" "After implementing the solution, tests and configuration, please run:"
101-
echo "./bin/configlet fmt --update --yes --exercise ${SLUG}"
111+
112+
echo "./bin/configlet fmt --update --yes --exercise ${slug}"

bin/build_exercise_crate.sh

Lines changed: 0 additions & 27 deletions
This file was deleted.

bin/fetch_canonical_data

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#!/usr/bin/env bash
22
# This script fetches the canonical data of the exercise.
33

4+
5+
# Exit if anything fails.
6+
set -euo pipefail
7+
8+
49
if [ $# -ne 1 ]; then
510
echo "Usage: bin/fetch_canonical_data <exercise-slug>"
611
exit 1

bin/generate_tests

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,61 @@
11
#!/usr/bin/env bash
22

3+
4+
# Exit if anything fails.
35
set -euo pipefail
4-
# shellcheck source=/dev/null
6+
7+
8+
# see comment in generator-utils/utils.sh
9+
# shellcheck source=bin/generator-utils/utils.sh
10+
# shellcheck source=./generator-utils/utils.sh
511
source ./bin/generator-utils/utils.sh
612

7-
function digest_template() {
13+
digest_template() {
14+
local template
815
template=$(cat bin/test_template)
9-
# shellcheck disable=SC2001
10-
# turn every token into a jq command
16+
# Turn every token into a jq command
17+
1118
echo "$template" | sed 's/${\([^}]*\)\}\$/$(echo $case | jq -r '\''.\1'\'')/g'
1219
}
1320

1421
message "info" "Generating tests.."
1522
canonical_json=$(cat canonical_data.json)
16-
SLUG=$(echo "$canonical_json" | jq '.exercise')
17-
# shellcheck disable=SC2001
23+
24+
slug=$(echo "$canonical_json" | jq '.exercise')
1825
# Remove double quotes
19-
SLUG=$(echo "$SLUG" | sed 's/"//g')
20-
EXERCISE_DIR="exercises/practice/$SLUG"
21-
TEST_FILE="$EXERCISE_DIR/tests/$SLUG.rs"
26+
slug=$(echo "$slug" | sed 's/"//g')
27+
exercise_dir="exercises/practice/$slug"
28+
test_file="$exercise_dir/tests/$slug.rs"
2229

23-
cat <<EOT >"$TEST_FILE"
24-
use $(dash_to_underscore "$SLUG")::*;
25-
// Add tests here
30+
cat <<EOT >"$test_file"
31+
use $(dash_to_underscore "$slug")::*;
2632
2733
EOT
2834

2935
# Flattens canonical json, extracts only the objects with a uuid
3036
cases=$(echo "$canonical_json" | jq '[ .. | objects | with_entries(select(.key | IN("uuid", "description", "input", "expected", "property"))) | select(. != {}) | select(has("uuid")) ]')
3137

38+
39+
# Shellcheck doesn't recognize that `case` is not unused
40+
3241
# shellcheck disable=SC2034
3342
jq -c '.[]' <<<"$cases" | while read -r case; do
3443

3544
# Evaluate the bash parts and replace them with their return values
3645
eval_template="$(digest_template | sed -e "s/\$(\(.*\))/\$\(\1\)/g")"
3746
eval_template="$(eval "echo \"$eval_template\"")"
3847

39-
# Turn function name unto snake_case
48+
49+
# Turn function name into snake_case
4050
formatted_template=$(echo "$eval_template" | sed -e ':loop' -e 's/\(fn[^(]*\)[ -]/\1_/g' -e 't loop' | sed 's/fn_/fn /')
41-
# Push to file
4251

43-
echo "$formatted_template" >>"$TEST_FILE"
44-
printf "\\n" >>"$TEST_FILE"
52+
# Push to test file
53+
echo "$formatted_template" >>"$test_file"
54+
printf "\\n" >>"$test_file"
4555

4656
done
4757

48-
rustfmt "$TEST_FILE"
58+
rustfmt "$test_file"
59+
60+
message "success" "Generated tests successfully! Check out ${test_file}"
4961

50-
message "success" "Generated tests successfully! Check out ${TEST_FILE}"

bin/generator-utils/colors.sh

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,13 @@
11
#!/usr/bin/env bash
22

3-
# shellcheck disable=SC2034
4-
# Reset
5-
RESET=$(echo -e '\033[0m')
3+
reset_color=$(echo -e '\033[0m')
64

7-
# Regular Colors
8-
BLACK=$(echo -e '\033[0;30m')
9-
RED=$(echo -e '\033[0;31m')
10-
GREEN=$(echo -e '\033[0;32m')
11-
YELLOW=$(echo -e '\033[0;33m')
12-
BLUE=$(echo -e '\033[0;34m')
13-
PURPLE=$(echo -e '\033[0;35m')
14-
CYAN=$(echo -e '\033[0;36m')
15-
WHITE=$(echo -e '\033[0;37m')
5+
red=$(echo -e '\033[0;31m')
6+
green=$(echo -e '\033[0;32m')
7+
yellow=$(echo -e '\033[0;33m')
8+
blue=$(echo -e '\033[0;34m')
9+
cyan=$(echo -e '\033[0;36m')
1610

17-
# Bold
18-
BOLD_BLACK=$(echo -e '\033[1;30m')
19-
BOLD_RED=$(echo -e '\033[1;31m')
20-
BOLD_GREEN=$(echo -e '\033[1;32m')
21-
BOLD_YELLOW=$(echo -e '\033[1;33m')
22-
BOLD_BLUE=$(echo -e '\033[1;34m')
23-
BOLD_PURPLE=$(echo -e '\033[1;35m')
24-
BOLD_CYAN=$(echo -e '\033[1;36m')
25-
BOLD_WHITE=$(echo -e '\033[1;37m')
11+
bold_green=$(echo -e '\033[1;32m')
2612

27-
# Underline
28-
UNDERLINE=$(echo -e ='\033[4m')
29-
30-
# Background
31-
BG_BLACK=$(echo -e ='\033[40m')
32-
BG_RED=$(echo -e ='\033[41m')
33-
BG_GREEN=$(echo -e ='\033[42m')
34-
BG_YELLOW=$(echo -e ='\033[43m')
35-
BG_BLUE=$(echo -e ='\033[44m')
36-
BG_PURPLE=$(echo -e ='\033[45m')
37-
BG_CYAN=$(echo -e ='\033[46m')
38-
BG_WHITE=$(echo -e ='\033[47m')
13+
export red green blue yellow bold_green reset_color cyan

bin/generator-utils/prompts.sh

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
11
#!/usr/bin/env bash
22

3-
# shellcheck source=/dev/null
3+
# see comment in utils.sh
4+
# shellcheck source=bin/generator-utils/colors.sh
5+
# shellcheck source=./colors.sh
46
source ./bin/generator-utils/colors.sh
57

6-
function get_exercise_difficulty() {
8+
get_exercise_difficulty() {
79
read -rp "Difficulty of exercise (1-10): " exercise_difficulty
810
echo "$exercise_difficulty"
911
}
1012

11-
function validate_difficulty_input() {
12-
13-
valid_input=false
13+
validate_difficulty_input() {
14+
local valid_input=false
1415
while ! $valid_input; do
1516
if [[ "$1" =~ ^[1-9]$|^10$ ]]; then
16-
exercise_difficulty=$1
17-
valid_input=true
17+
local exercise_difficulty=$1
18+
local valid_input=true
1819
else
19-
read -rp "${RED}Invalid input. ${RESET}Please enter an integer between 1 and 10. " exercise_difficulty
20+
read -rp "${red}Invalid input. ${reset_color}Please enter an integer between 1 and 10. " exercise_difficulty
21+
2022
[[ "$exercise_difficulty" =~ ^[1-9]$|^10$ ]] && valid_input=true
2123

2224
fi
2325
done
2426
echo "$exercise_difficulty"
2527
}
2628

27-
function get_author_handle {
28-
DEFAULT_AUTHOR_HANDLE="$(git config user.name)"
29+
get_author_handle() {
30+
local default_author_handle
31+
default_author_handle="$(git config user.name)"
2932

30-
if [ -z "$DEFAULT_AUTHOR_HANDLE" ]; then
31-
read -rp "Hey! Couldn't find your Github handle. Add it now or skip with enter and add it later in the .meta.config.json file: " AUTHOR_HANDLE
33+
if [ -z "$default_author_handle" ]; then
34+
read -rp "Hey! Couldn't find your Github handle. Add it now or skip with enter and add it later in the .meta.config.json file: " author_handle
3235
else
33-
AUTHOR_HANDLE="$DEFAULT_AUTHOR_HANDLE"
36+
local author_handle="$default_author_handle"
3437

3538
fi
36-
echo "$AUTHOR_HANDLE"
37-
39+
echo "$author_handle"
3840
}

0 commit comments

Comments
 (0)