mirror of
https://github.com/rust-lang/rustlings
synced 2024-11-15 00:47:11 +00:00
Handle the case when all exercises are done
This commit is contained in:
parent
a534de0312
commit
d5a6dee1b3
4 changed files with 84 additions and 43 deletions
|
@ -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.
|
||||
|
||||
";
|
||||
|
|
24
src/run.rs
24
src/run.rs
|
@ -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();
|
||||
stdout.write_all(&output.stdout)?;
|
||||
stdout.write_all(&output.stderr)?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
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()),
|
||||
}
|
||||
|
||||
|
|
17
src/watch.rs
17
src/watch.rs
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue