Refactor embedded files to add solutions

pull/1955/head
mo8it 5 months ago
parent e5a19a4c33
commit 2dac8e509b

2
Cargo.lock generated

@ -690,6 +690,8 @@ name = "rustlings-macros"
version = "6.0.0-alpha.0" version = "6.0.0-alpha.0"
dependencies = [ dependencies = [
"quote", "quote",
"serde",
"toml_edit",
] ]
[[package]] [[package]]

@ -17,6 +17,10 @@ authors = [
license = "MIT" license = "MIT"
edition = "2021" edition = "2021"
[workspace.dependencies]
serde = { version = "1.0.198", features = ["derive"] }
toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] }
[package] [package]
name = "rustlings" name = "rustlings"
description = "Small exercises to get you used to reading and writing Rust code!" description = "Small exercises to get you used to reading and writing Rust code!"
@ -41,8 +45,8 @@ hashbrown = "0.14.3"
notify-debouncer-mini = "0.4.1" notify-debouncer-mini = "0.4.1"
ratatui = "0.26.2" ratatui = "0.26.2"
rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" }
serde = { version = "1.0.198", features = ["derive"] } serde.workspace = true
toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } toml_edit.workspace = true
which = "6.0.1" which = "6.0.1"
[dev-dependencies] [dev-dependencies]

@ -236,6 +236,7 @@ Make sure the type is consistent across all arms."""
[[exercises]] [[exercises]]
name = "quiz1" name = "quiz1"
dir = "quizzes"
mode = "test" mode = "test"
hint = "No hints this time ;)" hint = "No hints this time ;)"
@ -637,6 +638,7 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v
[[exercises]] [[exercises]]
name = "quiz2" name = "quiz2"
dir = "quizzes"
mode = "test" mode = "test"
hint = "No hints this time ;)" hint = "No hints this time ;)"
@ -870,6 +872,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe
[[exercises]] [[exercises]]
name = "quiz3" name = "quiz3"
dir = "quizzes"
mode = "test" mode = "test"
hint = """ hint = """
To find the best solution to this challenge you're going to need to think back To find the best solution to this challenge you're going to need to think back

@ -11,3 +11,5 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0.36" quote = "1.0.36"
serde.workspace = true
toml_edit.workspace = true

@ -1,86 +1,39 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use std::{fs::read_dir, panic, path::PathBuf}; use serde::Deserialize;
fn path_to_string(path: PathBuf) -> String { #[derive(Deserialize)]
path.into_os_string() struct ExerciseInfo {
.into_string() name: String,
.unwrap_or_else(|original| { dir: String,
panic!("The path {} is invalid UTF8", original.to_string_lossy()); }
})
#[derive(Deserialize)]
struct InfoFile {
exercises: Vec<ExerciseInfo>,
} }
#[proc_macro] #[proc_macro]
pub fn include_files(_: TokenStream) -> TokenStream { pub fn include_files(_: TokenStream) -> TokenStream {
let mut files = Vec::with_capacity(8); let exercises = toml_edit::de::from_str::<InfoFile>(include_str!("../../info.toml"))
let mut dirs = Vec::with_capacity(128); .expect("Failed to parse `info.toml`")
.exercises;
for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") {
let entry = entry.expect("Failed to read the `exercises` directory"); let exercise_files = exercises
.iter()
if entry.file_type().unwrap().is_file() { .map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name));
let path = entry.path(); let solution_files = exercises
if path.file_name().unwrap() != "README.md" { .iter()
files.push(path_to_string(path)); .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name));
} let dirs = exercises.iter().map(|exercise| &exercise.dir);
let readmes = exercises
continue; .iter()
} .map(|exercise| format!("../exercises/{}/README.md", exercise.dir));
let dir_path = entry.path();
let dir_files = read_dir(&dir_path).unwrap_or_else(|e| {
panic!("Failed to open the directory {}: {e}", dir_path.display());
});
let dir_path = path_to_string(dir_path);
let dir_files = dir_files.filter_map(|entry| {
let entry = entry.unwrap_or_else(|e| {
panic!("Failed to read the directory {dir_path}: {e}");
});
let path = entry.path();
if !entry.file_type().unwrap().is_file() {
panic!("Found {} but expected only files", path.display());
}
if path.file_name().unwrap() == "README.md" {
return None;
}
Some(path_to_string(path))
});
dirs.push(quote! {
EmbeddedFlatDir {
path: #dir_path,
readme: EmbeddedFile {
path: ::std::concat!(#dir_path, "/README.md"),
content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")),
},
content: &[
#(EmbeddedFile {
path: #dir_files,
content: ::std::include_bytes!(::std::concat!("../", #dir_files)),
}),*
],
}
});
}
quote! { quote! {
EmbeddedFiles { EmbeddedFiles {
exercises_dir: ExercisesDir { exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*],
readme: EmbeddedFile { exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*]
path: "exercises/README.md",
content: ::std::include_bytes!("../exercises/README.md"),
},
files: &[#(
EmbeddedFile {
path: #files,
content: ::std::include_bytes!(::std::concat!("../", #files)),
}
),*],
dirs: &[#(#dirs),*],
},
} }
} }
.into() .into()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save