move input files into exercise directory

This commit is contained in:
Remo Senekowitsch 2026-08-09 14:06:43 +02:00
parent 1c106e5f1e
commit f63e11aeb6
No known key found for this signature in database
9 changed files with 49 additions and 39 deletions

View File

@ -6,14 +6,18 @@
// Let's simulate this using asynchronous programming. Each person is // Let's simulate this using asynchronous programming. Each person is
// represented as an asynchronous task, which can be executed concurrently. // represented as an asynchronous task, which can be executed concurrently.
const SCORES_CLASS_A: &str = "exercises/24_async/scores_class_a.txt";
const SCORES_CLASS_B: &str = "exercises/24_async/scores_class_b.txt";
const SCORES_CLASS_C: &str = "exercises/24_async/scores_class_c.txt";
// Async tasks need to be executed by a "runtime", which is not provided by // Async tasks need to be executed by a "runtime", which is not provided by
// Rust's standard library. Here, we use the mainstream runtime `tokio`. // Rust's standard library. Here, we use the mainstream runtime `tokio`.
// The macro `tokio::main` wraps the entire main function in a runtime. // The macro `tokio::main` wraps the entire main function in a runtime.
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let mean_score_a = tokio::spawn(calculate_mean_score("input_files/scores_class_a.txt")); let mean_score_a = tokio::spawn(calculate_mean_score(SCORES_CLASS_A));
let mean_score_b = tokio::spawn(calculate_mean_score("input_files/scores_class_b.txt")); let mean_score_b = tokio::spawn(calculate_mean_score(SCORES_CLASS_B));
let mean_score_c = tokio::spawn(calculate_mean_score("input_files/scores_class_c.txt")); let mean_score_c = tokio::spawn(calculate_mean_score(SCORES_CLASS_C));
// TODO: Await the spawned tasks to check their results. // TODO: Await the spawned tasks to check their results.
assert_eq!(mean_score_a, 84); // alice assert_eq!(mean_score_a, 84); // alice

View File

@ -1218,6 +1218,11 @@ Add `AsRef<str>` or `AsMut<u32>` as a trait bound to the functions."""
name = "async1" name = "async1"
dir = "24_async" dir = "24_async"
test = false test = false
input_files = [
"scores_class_a.txt",
"scores_class_b.txt",
"scores_class_c.txt",
]
hint = """ hint = """
Asynchronous runtimes like tokio can only spawn tasks that are defined as async Asynchronous runtimes like tokio can only spawn tasks that are defined as async
functions, not regular ones. Add the "async" keyword before the "fn" keyword of functions, not regular ones. Add the "async" keyword before the "fn" keyword of
@ -1225,12 +1230,3 @@ the functions "tim", "carl" and "nick".
An async task can wait for another one to complete by "awaiting" it. Add An async task can wait for another one to complete by "awaiting" it. Add
".await" after the three "task_name" variables in the "block_on" call.""" ".await" after the three "task_name" variables in the "block_on" call."""
[[input_files]]
name = "scores_class_a.txt"
[[input_files]]
name = "scores_class_b.txt"
[[input_files]]
name = "scores_class_c.txt"

View File

@ -6,18 +6,14 @@ use serde::Deserialize;
struct ExerciseInfo<'a> { struct ExerciseInfo<'a> {
name: &'a str, name: &'a str,
dir: &'a str, dir: &'a str,
} #[serde(default)]
input_files: Vec<&'a str>,
#[derive(Deserialize)]
struct InputFileInfo<'a> {
name: &'a str,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct InfoFile<'a> { struct InfoFile<'a> {
#[serde(borrow)] #[serde(borrow)]
exercises: Vec<ExerciseInfo<'a>>, exercises: Vec<ExerciseInfo<'a>>,
input_files: Vec<InputFileInfo<'a>>,
} }
#[proc_macro] #[proc_macro]
@ -47,22 +43,34 @@ pub fn include_files(_: TokenStream) -> TokenStream {
*dir_ind = dirs.len() - 1; *dir_ind = dirs.len() - 1;
} }
let input_files = exercises.iter().map(|exercise| {
let names = exercise.input_files.iter();
let paths = exercise
.input_files
.iter()
.map(|f| format!("../exercises/{}/{}", exercise.dir, f));
quote! {
&[#(InputFile {
name: #names,
content: include_str!(#paths),
}),*]
}
});
let readmes = dirs let readmes = dirs
.iter() .iter()
.map(|dir| format!("../exercises/{dir}/README.md")); .map(|dir| format!("../exercises/{dir}/README.md"));
let input_file_names = info.input_files.iter().map(|f| f.name);
let input_file_paths = info
.input_files
.iter()
.map(|f| format!("../input_files/{}", f.name));
quote! { quote! {
EmbeddedFiles { EmbeddedFiles {
info_file: #info_file, info_file: #info_file,
exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*], exercise_files: &[#(ExerciseFiles {
exercise: include_bytes!(#exercise_files),
solution: include_bytes!(#solution_files),
dir_ind: #dir_inds,
input_files: #input_files,
}),*],
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*], exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
input_files: &[#(InputFile { name: #input_file_names, content: include_str!(#input_file_paths) }),*],
} }
} }
.into() .into()

View File

@ -6,14 +6,18 @@
// Let's simulate this using asynchronous programming. Each person is // Let's simulate this using asynchronous programming. Each person is
// represented as an asynchronous task, which can be executed concurrently. // represented as an asynchronous task, which can be executed concurrently.
const SCORES_CLASS_A: &str = "exercises/24_async/scores_class_a.txt";
const SCORES_CLASS_B: &str = "exercises/24_async/scores_class_b.txt";
const SCORES_CLASS_C: &str = "exercises/24_async/scores_class_c.txt";
// Async tasks need to be executed by a "runtime", which is not provided by // Async tasks need to be executed by a "runtime", which is not provided by
// Rust's standard library. Here, we use the mainstream runtime `tokio`. // Rust's standard library. Here, we use the mainstream runtime `tokio`.
// The macro `tokio::main` wraps the entire main function in a runtime. // The macro `tokio::main` wraps the entire main function in a runtime.
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let mean_score_a = tokio::spawn(calculate_mean_score("input_files/scores_class_a.txt")); let mean_score_a = tokio::spawn(calculate_mean_score(SCORES_CLASS_A));
let mean_score_b = tokio::spawn(calculate_mean_score("input_files/scores_class_b.txt")); let mean_score_b = tokio::spawn(calculate_mean_score(SCORES_CLASS_B));
let mean_score_c = tokio::spawn(calculate_mean_score("input_files/scores_class_c.txt")); let mean_score_c = tokio::spawn(calculate_mean_score(SCORES_CLASS_C));
assert_eq!(mean_score_a.await.unwrap(), 84); // alice assert_eq!(mean_score_a.await.unwrap(), 84); // alice
assert_eq!(mean_score_b.await.unwrap(), 89); // bob assert_eq!(mean_score_b.await.unwrap(), 89); // bob

View File

@ -17,6 +17,8 @@ struct ExerciseFiles {
solution: &'static [u8], solution: &'static [u8],
// Index of the related `ExerciseDir` in `EmbeddedFiles::exercise_dirs`. // Index of the related `ExerciseDir` in `EmbeddedFiles::exercise_dirs`.
dir_ind: usize, dir_ind: usize,
// Files that are read by the exercise.
input_files: &'static [InputFile],
} }
// Input files that may be read by exercises. // Input files that may be read by exercises.
@ -64,7 +66,6 @@ pub struct EmbeddedFiles {
pub info_file: &'static str, pub info_file: &'static str,
exercise_files: &'static [ExerciseFiles], exercise_files: &'static [ExerciseFiles],
pub exercise_dirs: &'static [ExerciseDir], pub exercise_dirs: &'static [ExerciseDir],
pub input_files: &'static [InputFile],
} }
impl EmbeddedFiles { impl EmbeddedFiles {
@ -97,6 +98,12 @@ impl EmbeddedFiles {
fs::write(&exercise_path, exercise_files.exercise) fs::write(&exercise_path, exercise_files.exercise)
.with_context(|| format!("Failed to write the exercise file {exercise_path}"))?; .with_context(|| format!("Failed to write the exercise file {exercise_path}"))?;
for InputFile { name, content } in exercise_files.input_files {
let path = format!("{prefix}/{dir_name}/{name}", dir_name = dir.name);
fs::write(&path, content)
.with_context(|| format!("Failed to write the input file {path}"))?;
}
} }
Ok(()) Ok(())

View File

@ -144,15 +144,6 @@ pub fn init() -> Result<()> {
.with_context(|| format!("Failed to create the file {solution_path}"))?; .with_context(|| format!("Failed to create the file {solution_path}"))?;
} }
// init input files
create_dir("input_files").context("Failed to create the directory `input_files`")?;
for input_file in EMBEDDED_FILES.input_files {
fs::write(
format!("input_files/{}", input_file.name),
input_file.content,
)?;
}
let current_cargo_toml = include_str!("../dev-Cargo.toml"); let current_cargo_toml = include_str!("../dev-Cargo.toml");
// Skip the first line (comment). // Skip the first line (comment).
let newline_ind = current_cargo_toml let newline_ind = current_cargo_toml