diff options
Diffstat (limited to 'src/app_state.rs')
| -rw-r--r-- | src/app_state.rs | 101 |
1 files changed, 64 insertions, 37 deletions
diff --git a/src/app_state.rs b/src/app_state.rs index b88c125..cc77711 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,8 +1,8 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{bail, Context, Result}; use std::{ env, fs::{self, File}, - io::{Read, StdoutLock, Write}, + io::{self, Read, StdoutLock, Write}, path::Path, process::{Command, Stdio}, thread, @@ -35,6 +35,12 @@ pub enum StateFileStatus { NotRead, } +enum AllExercisesCheck { + Pending(usize), + AllDone, + CheckedUntil(usize), +} + pub struct AppState { current_exercise_ind: usize, exercises: Vec<Exercise>, @@ -340,59 +346,80 @@ impl AppState { } } - /// Mark the current exercise as done and move on to the next pending exercise if one exists. - /// If all exercises are marked as done, run all of them to make sure that they are actually - /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. - pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> { - let exercise = &mut self.exercises[self.current_exercise_ind]; - if !exercise.done { - exercise.done = true; - self.n_done += 1; - } - - if let Some(ind) = self.next_pending_exercise_ind() { - self.set_current_exercise_ind(ind)?; - return Ok(ExercisesProgress::NewPending); - } - + // Return the exercise index of the first pending exercise found. + fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result<Option<usize>> { stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?; - let n_exercises = self.exercises.len(); - let pending_exercise_ind = thread::scope(|s| { + let status = thread::scope(|s| { let handles = self .exercises - .iter_mut() - .map(|exercise| { - s.spawn(|| { - let success = exercise.run_exercise(None, &self.cmd_runner)?; - exercise.done = success; - Ok::<_, Error>(success) - }) - }) + .iter() + .map(|exercise| s.spawn(|| exercise.run_exercise(None, &self.cmd_runner))) .collect::<Vec<_>>(); for (exercise_ind, handle) in handles.into_iter().enumerate() { write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; stdout.flush()?; - let success = handle.join().unwrap()?; + let Ok(success) = handle.join().unwrap() else { + return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); + }; + if !success { - stdout.write_all(b"\n\n")?; - return Ok(Some(exercise_ind)); + return Ok(AllExercisesCheck::Pending(exercise_ind)); } } - Ok::<_, Error>(None) + Ok::<_, io::Error>(AllExercisesCheck::AllDone) })?; - if let Some(pending_exercise_ind) = pending_exercise_ind { + let mut exercise_ind = match status { + AllExercisesCheck::Pending(exercise_ind) => return Ok(Some(exercise_ind)), + AllExercisesCheck::AllDone => return Ok(None), + AllExercisesCheck::CheckedUntil(ind) => ind, + }; + + // We got an error while checking all exercises in parallel. + // This could be because we exceeded the limit of open file descriptors. + // Therefore, try to continue the check sequentially. + for exercise in &self.exercises[exercise_ind..] { + write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; + stdout.flush()?; + + let success = exercise.run_exercise(None, &self.cmd_runner)?; + if !success { + return Ok(Some(exercise_ind)); + } + + exercise_ind += 1; + } + + Ok(None) + } + + /// Mark the current exercise as done and move on to the next pending exercise if one exists. + /// If all exercises are marked as done, run all of them to make sure that they are actually + /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. + pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> { + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; + self.n_done += 1; + } + + if let Some(ind) = self.next_pending_exercise_ind() { + self.set_current_exercise_ind(ind)?; + return Ok(ExercisesProgress::NewPending); + } + + if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { + stdout.write_all(b"\n\n")?; + self.current_exercise_ind = pending_exercise_ind; - self.n_done = self - .exercises - .iter() - .filter(|exercise| exercise.done) - .count() as u16; + self.exercises[pending_exercise_ind].done = false; + // All exercises were marked as done. + self.n_done -= 1; self.write()?; return Ok(ExercisesProgress::NewPending); } |
