Improve check-all command

This commit is contained in:
mo8it 2024-10-13 22:02:41 +02:00
parent 685e069c58
commit 326169a7fa
4 changed files with 38 additions and 31 deletions

View file

@ -13,4 +13,6 @@ disallowed-methods = [
# Use `thread::Builder::spawn` instead and handle the error. # Use `thread::Builder::spawn` instead and handle the error.
"std::thread::spawn", "std::thread::spawn",
"std::thread::Scope::spawn", "std::thread::Scope::spawn",
# Return `ExitCode` instead.
"std::process::exit",
] ]

View file

@ -211,6 +211,11 @@ impl AppState {
self.n_done self.n_done
} }
#[inline]
pub fn n_pending(&self) -> u16 {
self.exercises.len() as u16 - self.n_done
}
#[inline] #[inline]
pub fn current_exercise(&self) -> &Exercise { pub fn current_exercise(&self) -> &Exercise {
&self.exercises[self.current_exercise_ind] &self.exercises[self.current_exercise_ind]

View file

@ -8,7 +8,7 @@ use crossterm::{
use std::{ use std::{
io::{self, IsTerminal, Write}, io::{self, IsTerminal, Write},
path::Path, path::Path,
process::exit, process::ExitCode,
}; };
use term::{clear_terminal, press_enter_prompt}; use term::{clear_terminal, press_enter_prompt};
@ -51,8 +51,8 @@ enum Subcommands {
/// The name of the exercise /// The name of the exercise
name: Option<String>, name: Option<String>,
}, },
/// Run all the exercises, marking them as done or pending accordingly. /// Check all the exercises, marking them as done or pending accordingly.
RunAll, CheckAll,
/// Reset a single exercise /// Reset a single exercise
Reset { Reset {
/// The name of the exercise /// The name of the exercise
@ -68,22 +68,26 @@ enum Subcommands {
Dev(DevCommands), Dev(DevCommands),
} }
fn main() -> Result<()> { fn main() -> Result<ExitCode> {
let args = Args::parse(); let args = Args::parse();
if cfg!(not(debug_assertions)) && Path::new("dev/rustlings-repo.txt").exists() { if cfg!(not(debug_assertions)) && Path::new("dev/rustlings-repo.txt").exists() {
bail!("{OLD_METHOD_ERR}"); bail!("{OLD_METHOD_ERR}");
} }
'priority_cmd: {
match args.command { match args.command {
Some(Subcommands::Init) => return init::init().context("Initialization failed"), Some(Subcommands::Init) => init::init().context("Initialization failed")?,
Some(Subcommands::Dev(dev_command)) => return dev_command.run(), Some(Subcommands::Dev(dev_command)) => dev_command.run()?,
_ => (), _ => break 'priority_cmd,
}
return Ok(ExitCode::SUCCESS);
} }
if !Path::new("exercises").is_dir() { if !Path::new("exercises").is_dir() {
println!("{PRE_INIT_MSG}"); println!("{PRE_INIT_MSG}");
exit(1); return Ok(ExitCode::FAILURE);
} }
let info_file = InfoFile::parse()?; let info_file = InfoFile::parse()?;
@ -142,33 +146,29 @@ fn main() -> Result<()> {
if let Some(name) = name { if let Some(name) = name {
app_state.set_current_exercise_by_name(&name)?; app_state.set_current_exercise_by_name(&name)?;
} }
run::run(&mut app_state)?; return run::run(&mut app_state);
} }
Some(Subcommands::RunAll) => { Some(Subcommands::CheckAll) => {
let mut stdout = io::stdout().lock(); let mut stdout = io::stdout().lock();
if let Some(first_fail) = app_state.check_all_exercises(&mut stdout)? { if let Some(first_pending_exercise_ind) = app_state.check_all_exercises(&mut stdout)? {
let pending = app_state
.exercises()
.iter()
.filter(|exercise| !exercise.done)
.count();
if app_state.current_exercise().done { if app_state.current_exercise().done {
app_state.set_current_exercise_ind(first_fail)?; app_state.set_current_exercise_ind(first_pending_exercise_ind)?;
} }
stdout
.queue(SetForegroundColor(Color::Red))? let pending = app_state.n_pending();
.queue(Print(format!("{pending}")))?
.queue(ResetColor)?;
if pending == 1 { if pending == 1 {
stdout.queue(Print(" exercise has some errors: "))?; stdout.queue(Print("One exercise pending: "))?;
} else { } else {
stdout.queue(Print(" exercises have errors, including "))?; stdout.queue(SetForegroundColor(Color::Red))?;
write!(stdout, "{pending}")?;
stdout.queue(ResetColor)?;
stdout.queue(Print(" exercises are pending. The first: "))?;
} }
app_state app_state
.current_exercise() .current_exercise()
.terminal_file_link(&mut stdout)?; .terminal_file_link(&mut stdout)?;
stdout.write_all(b".\n")?; stdout.write_all(b"\n")?;
exit(1); return Ok(ExitCode::FAILURE);
} else { } else {
app_state.render_final_message(&mut stdout)?; app_state.render_final_message(&mut stdout)?;
} }
@ -188,7 +188,7 @@ fn main() -> Result<()> {
Some(Subcommands::Init | Subcommands::Dev(_)) => (), Some(Subcommands::Init | Subcommands::Dev(_)) => (),
} }
Ok(()) Ok(ExitCode::SUCCESS)
} }
const OLD_METHOD_ERR: &str = const OLD_METHOD_ERR: &str =

View file

@ -5,7 +5,7 @@ use crossterm::{
}; };
use std::{ use std::{
io::{self, Write}, io::{self, Write},
process::exit, process::ExitCode,
}; };
use crate::{ use crate::{
@ -13,7 +13,7 @@ use crate::{
exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY},
}; };
pub fn run(app_state: &mut AppState) -> Result<()> { pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
let exercise = app_state.current_exercise(); let exercise = app_state.current_exercise();
let mut output = Vec::with_capacity(OUTPUT_CAPACITY); let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
let success = exercise.run_exercise(Some(&mut output), app_state.cmd_runner())?; let success = exercise.run_exercise(Some(&mut output), app_state.cmd_runner())?;
@ -29,7 +29,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> {
.current_exercise() .current_exercise()
.terminal_file_link(&mut stdout)?; .terminal_file_link(&mut stdout)?;
stdout.write_all(b" with errors\n")?; stdout.write_all(b" with errors\n")?;
exit(1); return Ok(ExitCode::FAILURE);
} }
stdout.queue(SetForegroundColor(Color::Green))?; stdout.queue(SetForegroundColor(Color::Green))?;
@ -55,5 +55,5 @@ pub fn run(app_state: &mut AppState) -> Result<()> {
ExercisesProgress::AllDone => (), ExercisesProgress::AllDone => (),
} }
Ok(()) Ok(ExitCode::SUCCESS)
} }