mirror of
https://github.com/notohh/rustlings.git
synced 2024-10-30 08:49:12 -04:00
Refactor check_solutions
This commit is contained in:
parent
c7590dd752
commit
e0f0944bff
1 changed files with 58 additions and 41 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue