Handle the case when all exercises are done

This commit is contained in:
mo8it 2024-04-12 18:57:04 +02:00
parent a534de0312
commit d5a6dee1b3
4 changed files with 84 additions and 43 deletions

View file

@ -1,8 +1,16 @@
use anyhow::{bail, Context, Result};
use crossterm::{
style::Stylize,
terminal::{Clear, ClearType},
ExecutableCommand,
};
use serde::{Deserialize, Serialize};
use std::fs;
use std::{
fs,
io::{StdoutLock, Write},
};
use crate::exercise::Exercise;
use crate::{exercise::Exercise, FENISH_LINE};
const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises";
@ -143,7 +151,7 @@ impl AppState {
Ok(())
}
fn next_exercise_ind(&self) -> Option<usize> {
fn next_pending_exercise_ind(&self) -> Option<usize> {
let current_ind = self.state_file.current_exercise_ind;
if current_ind == self.state_file.progress.len() - 1 {
@ -167,14 +175,41 @@ impl AppState {
}
}
pub fn done_current_exercise(&mut self) -> Result<ExercisesProgress> {
pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result<ExercisesProgress> {
let done = &mut self.state_file.progress[self.state_file.current_exercise_ind];
if !*done {
*done = true;
self.n_done += 1;
}
let Some(ind) = self.next_exercise_ind() else {
let Some(ind) = self.next_pending_exercise_ind() else {
writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?;
for (exercise_ind, exercise) in self.exercises().iter().enumerate() {
writer.write_fmt(format_args!("Running {exercise} ... "))?;
writer.flush()?;
if !exercise.run()?.status.success() {
self.state_file.current_exercise_ind = exercise_ind;
self.current_exercise = exercise;
// No check if the exercise is done before setting it to pending
// because no pending exercise was found.
self.state_file.progress[exercise_ind] = false;
self.n_done -= 1;
self.state_file.write()?;
return Ok(ExercisesProgress::Pending);
}
writer.write_fmt(format_args!("{}\n", "ok".green()))?;
}
writer.execute(Clear(ClearType::All))?;
writer.write_all(FENISH_LINE.as_bytes())?;
// TODO: Show final message.
return Ok(ExercisesProgress::AllDone);
};
@ -183,3 +218,10 @@ impl AppState {
Ok(ExercisesProgress::Pending)
}
}
const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b"
All exercises seem to be done.
Recompiling and running all exercises to make sure that all of them are actually done.
This might take some minutes.
";

View file

@ -1,6 +1,6 @@
use anyhow::{bail, Result};
use crossterm::style::Stylize;
use std::io::{stdout, Write};
use std::io::{self, Write};
use crate::app_state::{AppState, ExercisesProgress};
@ -8,28 +8,24 @@ pub fn run(app_state: &mut AppState) -> Result<()> {
let exercise = app_state.current_exercise();
let output = exercise.run()?;
{
let mut stdout = stdout().lock();
let mut stdout = io::stdout().lock();
stdout.write_all(&output.stdout)?;
stdout.write_all(b"\n")?;
stdout.write_all(&output.stderr)?;
stdout.flush()?;
}
if !output.status.success() {
bail!("Ran {exercise} with errors");
}
println!(
stdout.write_fmt(format_args!(
"{}{}",
"✓ Successfully ran ".green(),
exercise.path.to_string_lossy().green(),
);
))?;
match app_state.done_current_exercise()? {
ExercisesProgress::AllDone => println!(
"🎉 Congratulations! You have done all the exercises!
🔚 There are no more exercises to do next!"
),
match app_state.done_current_exercise(&mut stdout)? {
ExercisesProgress::AllDone => (),
ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()),
}

View file

@ -15,7 +15,7 @@ mod debounce_event;
mod state;
mod terminal_event;
use crate::app_state::AppState;
use crate::app_state::{AppState, ExercisesProgress};
use self::{
debounce_event::DebounceEventHandler,
@ -32,6 +32,7 @@ enum WatchEvent {
}
/// Returned by the watch mode to indicate what to do afterwards.
#[must_use]
pub enum WatchExit {
/// Exit the program.
Shutdown,
@ -60,16 +61,20 @@ pub fn watch(app_state: &mut AppState) -> Result<WatchExit> {
while let Ok(event) = rx.recv() {
match event {
WatchEvent::Input(InputEvent::Next) => {
watch_state.next_exercise()?;
}
WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise()? {
ExercisesProgress::AllDone => break,
ExercisesProgress::Pending => watch_state.run_current_exercise()?,
},
WatchEvent::Input(InputEvent::Hint) => {
watch_state.show_hint()?;
}
WatchEvent::Input(InputEvent::List) => {
return Ok(WatchExit::List);
}
WatchEvent::Input(InputEvent::Quit) => break,
WatchEvent::Input(InputEvent::Quit) => {
watch_state.into_writer().write_all(QUIT_MSG)?;
break;
}
WatchEvent::Input(InputEvent::Unrecognized(cmd)) => {
watch_state.handle_invalid_cmd(&cmd)?;
}
@ -88,8 +93,6 @@ pub fn watch(app_state: &mut AppState) -> Result<WatchExit> {
}
}
watch_state.into_writer().write_all(QUIT_MSG)?;
Ok(WatchExit::Shutdown)
}

View file

@ -4,7 +4,10 @@ use crossterm::{
terminal::{size, Clear, ClearType},
ExecutableCommand,
};
use std::io::{self, StdoutLock, Write};
use std::{
io::{self, StdoutLock, Write},
process::Output,
};
use crate::{
app_state::{AppState, ExercisesProgress},
@ -49,6 +52,9 @@ impl<'a> WatchState<'a> {
self.stderr = None;
self.show_done = true;
} else {
self.app_state
.set_pending(self.app_state.current_exercise_ind())?;
self.stderr = Some(output.stderr);
self.show_done = false;
}
@ -61,18 +67,15 @@ impl<'a> WatchState<'a> {
self.run_current_exercise()
}
pub fn next_exercise(&mut self) -> Result<()> {
pub fn next_exercise(&mut self) -> Result<ExercisesProgress> {
if !self.show_done {
self.writer
.write_all(b"The current exercise isn't done yet\n")?;
self.show_prompt()?;
return Ok(());
return Ok(ExercisesProgress::Pending);
}
match self.app_state.done_current_exercise()? {
ExercisesProgress::AllDone => todo!(),
ExercisesProgress::Pending => self.run_current_exercise(),
}
self.app_state.done_current_exercise(&mut self.writer)
}
fn show_prompt(&mut self) -> io::Result<()> {
@ -93,7 +96,7 @@ impl<'a> WatchState<'a> {
}
pub fn render(&mut self) -> Result<()> {
// Prevent having the first line shifted after clearing because of the prompt.
// Prevent having the first line shifted.
self.writer.write_all(b"\n")?;
self.writer.execute(Clear(ClearType::All))?;
@ -111,11 +114,11 @@ impl<'a> WatchState<'a> {
self.writer.write_all(b"\n")?;
if self.show_hint {
self.writer
.write_fmt(format_args!("{}\n", "Hint".bold().cyan().underlined()))?;
self.writer
.write_all(self.app_state.current_exercise().hint.as_bytes())?;
self.writer.write_all(b"\n\n")?;
self.writer.write_fmt(format_args!(
"{}\n{}\n\n",
"Hint".bold().cyan().underlined(),
self.app_state.current_exercise().hint,
))?;
}
if self.show_done {
@ -134,11 +137,8 @@ When you are done experimenting, enter `n` or `next` to go to the next exercise
self.app_state.exercises().len() as u16,
line_width,
)?;
self.writer.write_all(progress_bar.as_bytes())?;
self.writer.write_all(b"Current exercise: ")?;
self.writer.write_fmt(format_args!(
"{}\n",
"{progress_bar}Current exercise: {}\n",
self.app_state
.current_exercise()
.path