Refactor check_solutions

This commit is contained in:
mo8it 2024-08-01 15:53:32 +02:00
parent c7590dd752
commit e0f0944bff

View file

@ -1,13 +1,10 @@
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Error, Result};
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
fs::{self, read_dir, OpenOptions}, fs::{self, read_dir, OpenOptions},
io::{self, Read, Write}, io::{self, Read, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{ sync::atomic::{self, AtomicBool},
atomic::{self, AtomicBool},
Mutex,
},
thread, thread,
}; };
@ -213,56 +210,76 @@ fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> {
check_exercises_unsolved(info_file, cmd_runner) check_exercises_unsolved(info_file, cmd_runner)
} }
enum SolutionCheck {
Success { sol_path: String },
MissingRequired,
MissingOptional,
RunFailure { output: Vec<u8> },
Err(Error),
}
fn check_solutions( fn check_solutions(
require_solutions: bool, require_solutions: bool,
info_file: &InfoFile, info_file: &InfoFile,
cmd_runner: &CmdRunner, cmd_runner: &CmdRunner,
) -> Result<()> { ) -> Result<()> {
let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len()));
let error_occurred = AtomicBool::new(false);
println!("Running all solutions. This may take a while…\n"); println!("Running all solutions. This may take a while…\n");
thread::scope(|s| { let sol_paths = thread::scope(|s| {
for exercise_info in &info_file.exercises { let handles = info_file
s.spawn(|| { .exercises
let error = |e| { .iter()
let mut stderr = io::stderr().lock(); .map(|exercise_info| {
stderr.write_all(e).unwrap(); s.spawn(|| {
stderr let sol_path = exercise_info.sol_path();
.write_all(b"\nFailed to run the solution of the exercise ") if !Path::new(&sol_path).exists() {
.unwrap(); if require_solutions {
stderr.write_all(exercise_info.name.as_bytes()).unwrap(); return SolutionCheck::MissingRequired;
stderr.write_all(SEPARATOR).unwrap(); }
error_occurred.store(true, atomic::Ordering::Relaxed);
};
let path = exercise_info.sol_path(); return SolutionCheck::MissingOptional;
if !Path::new(&path).exists() {
if require_solutions {
error(b"Solution missing");
} }
// No solution to check. let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
return; match exercise_info.run_solution(Some(&mut output), cmd_runner) {
} Ok(true) => SolutionCheck::Success { sol_path },
Ok(false) => SolutionCheck::RunFailure { output },
let mut output = Vec::with_capacity(OUTPUT_CAPACITY); Err(e) => SolutionCheck::Err(e),
match exercise_info.run_solution(Some(&mut output), cmd_runner) {
Ok(true) => {
paths.lock().unwrap().insert(PathBuf::from(path));
} }
Ok(false) => error(&output), })
Err(e) => error(e.to_string().as_bytes()), })
.collect::<Vec<_>>();
let mut sol_paths = hashbrown::HashSet::with_capacity(info_file.exercises.len());
for (exercise_name, handle) in info_file
.exercises
.iter()
.map(|exercise_info| &exercise_info.name)
.zip(handles)
{
match handle.join() {
Ok(SolutionCheck::Success { sol_path }) => {
sol_paths.insert(PathBuf::from(sol_path));
} }
}); Ok(SolutionCheck::MissingRequired) => {
bail!("The solution of the exercise {exercise_name} is missing");
}
Ok(SolutionCheck::MissingOptional) => (),
Ok(SolutionCheck::RunFailure { output }) => {
io::stderr().lock().write_all(&output)?;
bail!("Running the solution of the exercise {exercise_name} failed with the error above");
}
Ok(SolutionCheck::Err(e)) => return Err(e),
Err(_) => {
bail!("Panic while trying to run the solution of the exericse {exercise_name}");
}
}
} }
});
if error_occurred.load(atomic::Ordering::Relaxed) { Ok(sol_paths)
bail!("At least one solution failed. See the output above."); })?;
}
check_unexpected_files("solutions", &paths.into_inner().unwrap())?; check_unexpected_files("solutions", &sol_paths)?;
Ok(()) Ok(())
} }