Skip to content

Commit c527bba

Browse files
authored
Generator script improvements (#1663)
* Add difficulty as int, further format fn name * Add util fn to escape double quotes * Simplify escape double quotes algo * Add missing end line
1 parent af8e3cf commit c527bba

File tree

12 files changed

+119
-11
lines changed

12 files changed

+119
-11
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ bin/configlet.exe
88
bin/exercise
99
bin/exercise.exe
1010
bin/generator-utils/ngram
11+
bin/generator-utils/escape_double_quotes
1112
exercises/*/*/Cargo.lock
1213
exercises/*/*/clippy.log
1314
canonical_data.json

bin/add_practice_exercise

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ message "info" "Adding instructions and configuration files..."
8282
uuid=$(bin/configlet uuid)
8383

8484
# Add exercise-data to global config.json
85-
jq --arg slug "$slug" --arg uuid "$uuid" --arg name "$exercise_name" --arg difficulty "$exercise_difficulty" \
85+
jq --arg slug "$slug" --arg uuid "$uuid" --arg name "$exercise_name" --argjson difficulty "$exercise_difficulty" \
8686
'.exercises.practice += [{slug: $slug, name: $name, uuid: $uuid, practices: [], prerequisites: [], difficulty: $difficulty}]' \
8787
config.json >config.json.tmp
8888
# jq always rounds whole numbers, but average_run_time needs to be a float

bin/generate_tests

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
#!/usr/bin/env bash
22

3-
43
# Exit if anything fails.
54
set -euo pipefail
65

7-
86
# see comment in generator-utils/utils.sh
97
# shellcheck source=bin/generator-utils/utils.sh
108
# shellcheck source=./generator-utils/utils.sh
119
source ./bin/generator-utils/utils.sh
1210

11+
if [ ! -e bin/generator-utils/escape_double_quotes ]; then
12+
message "info" "Building util function"
13+
cd util/escape_double_quotes && ./build && cd ../..
14+
fi
15+
1316
digest_template() {
1417
local template
15-
template=$(cat bin/test_template)
18+
template=$(bin/generator-utils/escape_double_quotes bin/test_template)
1619
# Turn every token into a jq command
1720

1821
echo "$template" | sed 's/${\([^}]*\)\}\$/$(echo $case | jq -r '\''.\1'\'')/g'
@@ -35,7 +38,6 @@ EOT
3538
# Flattens canonical json, extracts only the objects with a uuid
3639
cases=$(echo "$canonical_json" | jq '[ .. | objects | with_entries(select(.key | IN("uuid", "description", "input", "expected", "property"))) | select(. != {}) | select(has("uuid")) ]')
3740

38-
3941
# Shellcheck doesn't recognize that `case` is not unused
4042

4143
# shellcheck disable=SC2034
@@ -45,9 +47,8 @@ jq -c '.[]' <<<"$cases" | while read -r case; do
4547
eval_template="$(digest_template | sed -e "s/\$(\(.*\))/\$\(\1\)/g")"
4648
eval_template="$(eval "echo \"$eval_template\"")"
4749

48-
4950
# Turn function name into snake_case
50-
formatted_template=$(echo "$eval_template" | sed -e ':loop' -e 's/\(fn[^(]*\)[ -]/\1_/g' -e 't loop' | sed 's/fn_/fn /')
51+
formatted_template=$(echo "$eval_template" | sed -E -e '/^fn/!b' -e 's/[^a-zA-Z0-9_{}()[:space:]-]//g' -e 's/([[:upper:]])/ \L\1/g' -e 's/(fn[[:space:]]+)([a-z0-9_-]+)/\1\L\2/g' -e 's/ /_/g' -e 's/_\{/\{/g' -e 's/-/_/g' | sed 's/fn_/fn /' | sed 's/__\+/_/g')
5152

5253
# Push to test file
5354
echo "$formatted_template" >>"$test_file"
@@ -58,4 +59,3 @@ done
5859
rustfmt "$test_file"
5960

6061
message "success" "Generated tests successfully! Check out ${test_file}"
61-

bin/test_template

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#[test]
22
#[ignore]
33
fn ${description}$() {
4-
5-
assert_eq!(${property}$(${input}$), ${expected}$);
4+
let expected = "${expected}$";
5+
assert_eq!(${property}$(${input}$), expected);
66
}
7-

util/escape_double_quotes/Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

util/escape_double_quotes/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "escape_double_quotes"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]

util/escape_double_quotes/build

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
cargo build --release --quiet && cp ./target/release/escape_double_quotes ../../bin/generator-utils && rm -rf ./target

util/escape_double_quotes/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod utils;
2+
pub use utils::escape_double_quotes::*;

util/escape_double_quotes/src/main.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use std::env;
2+
use std::fs::File;
3+
use std::io::{self, BufRead, BufReader, BufWriter, Write};
4+
mod utils;
5+
use utils::escape_double_quotes::*;
6+
7+
8+
fn main() -> io::Result<()> {
9+
let args: Vec<String> = env::args().collect();
10+
if args.len() != 2 {
11+
eprintln!("Usage: {} <file>", args[0]);
12+
std::process::exit(1);
13+
}
14+
15+
let file_path = &args[1];
16+
let file = File::open(file_path)?;
17+
let reader = BufReader::new(file);
18+
19+
let stdout = io::stdout();
20+
let mut writer = BufWriter::new(stdout.lock());
21+
22+
for line in reader.lines() {
23+
let input = line?;
24+
let output = escape_double_quotes(&input);
25+
writeln!(writer, "{}", output)?;
26+
}
27+
28+
Ok(())
29+
}
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pub fn escape_double_quotes(input: &str) -> String {
2+
let mut output = String::new();
3+
let mut escape = true;
4+
5+
let mut chars = input.chars().peekable();
6+
while let Some(char) = chars.next() {
7+
match char {
8+
'$' if chars.peek() == Some(&'{') => {
9+
escape = false;
10+
output.push(char)
11+
}
12+
'}' if chars.peek() == Some(&'$') => {
13+
escape = true;
14+
output.push(char)
15+
}
16+
'"' if escape => output.push_str("\\\""),
17+
_ => output.push(char),
18+
}
19+
}
20+
21+
output
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod escape_double_quotes;
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use escape_double_quotes::utils::escape_double_quotes::escape_double_quotes;
2+
3+
#[test]
4+
fn test_no_double_quotes() {
5+
let input = "let x = 5;";
6+
let expected = "let x = 5;";
7+
assert_eq!(escape_double_quotes(input), expected);
8+
}
9+
10+
#[test]
11+
fn test_simple_double_quotes() {
12+
let input = "let something = \"string\";";
13+
let expected = "let something = \\\"string\\\";";
14+
assert_eq!(escape_double_quotes(input), expected);
15+
}
16+
17+
#[test]
18+
fn test_braces_with_double_quotes() {
19+
let input = "let expected = \"${expected | join(\\\"\\n\\\")}$\";";
20+
let expected = "let expected = \\\"${expected | join(\\\"\\n\\\")}$\\\";";
21+
assert_eq!(escape_double_quotes(input), expected);
22+
}
23+
24+
#[test]
25+
fn test_mixed_double_quotes() {
26+
let input = "let a = \"value\"; let b = \"${value | filter(\\\"text\\\")}$\";";
27+
let expected = "let a = \\\"value\\\"; let b = \\\"${value | filter(\\\"text\\\")}$\\\";";
28+
assert_eq!(escape_double_quotes(input), expected);
29+
}
30+
31+
#[test]
32+
fn test_nested_braces() {
33+
let input = "let nested = \"${outer {inner | escape(\\\"\\n\\\")}}$\";";
34+
let expected = "let nested = \\\"${outer {inner | escape(\\\"\\n\\\")}}$\\\";";
35+
assert_eq!(escape_double_quotes(input), expected);
36+
}

0 commit comments

Comments
 (0)